From 1042db731753bfa93002d1cccfda6bfaf3ed0c74 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Mon, 24 May 2021 13:41:27 +0530 Subject: [PATCH 001/233] Initial commit for addition of dedicated BWPs --- .vscode/launch.json | 140 +++++++++++++++--- cmake_targets/CMakeLists.txt | 2 +- cmake_targets/epc_test/CMakeLists.template | 2 +- openair1/SIMULATION/NR_PHY/dlsim.c | 6 +- openair2/GNB_APP/RRC_nr_paramsvalues.h | 16 +- openair2/GNB_APP/gnb_config.c | 28 +++- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 4 +- openair2/LAYER2/NR_MAC_UE/config_ue.c | 51 ++++--- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 2 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 12 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 9 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 43 +++++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 10 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 12 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 3 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 5 + openair2/RRC/NR/rrc_gNB_reconfig.c | 58 ++++++-- .../CONF/gnb.band78.tm1.106PRB.usrpn300.conf | 82 ++++++++++ 20 files changed, 404 insertions(+), 87 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 4b563febde..79964cec9d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,25 +4,125 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "enter program name, for example ${workspaceFolder}/a.exe", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/path/to/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } + + { + "name": "(gdb) Launch UE PHY Simu", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "args": ["--rfsimulator.serveraddr","127.0.0.1", + "--rfsim", + "--phy-test", + + "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch UE RA", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "args": ["--rfsimulator.serveraddr","127.0.0.1", + "--rfsim", + "--do-ra", + + "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch gNB PHY Simu", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "args": ["--rfsimulator.serveraddr","server","-O", + "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "--rfsim", + "--phy-test" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch gNB RA", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "args": ["--rfsimulator.serveraddr","server","-O", + "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "--rfsim", + "--do-ra" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) dlsim", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/phy_simulators/build/nr_dlsim", + "args": ["-n", "4", "-m", "2" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } ] } diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 8d8e88455a..acc27404c0 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -396,7 +396,7 @@ add_definitions("-DPACKAGE_BUGREPORT=\"openair4g-devel@lists.eurecom.fr\"") # Debug related options ######################################### -add_boolean_option(ASN_DEBUG False "ASN1 coder/decoder Debug") +add_boolean_option(ASN_DEBUG True "ASN1 coder/decoder Debug") add_boolean_option(EMIT_ASN_DEBUG False "ASN1 coder/decoder Debug") add_boolean_option(MSG_PRINT False "print debug messages") add_boolean_option(DISABLE_XER_PRINT False "print XER Format") diff --git a/cmake_targets/epc_test/CMakeLists.template b/cmake_targets/epc_test/CMakeLists.template index 7a097fffb0..1efbd1340c 100755 --- a/cmake_targets/epc_test/CMakeLists.template +++ b/cmake_targets/epc_test/CMakeLists.template @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) set ( CMAKE_BUILD_TYPE "RelWithDebInfo" ) -set ( ASN_DEBUG False) +set ( ASN_DEBUG True) set ( DEBUG_OMG False ) set ( DISABLE_XER_PRINT False ) set ( DRIVER2013 True ) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 730e78c162..abf8faf225 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -353,7 +353,7 @@ int main(int argc, char **argv) NB_UE_INST = 1; //double pbch_sinr; //int pbch_tx_ant; - int N_RB_DL=106,mu=1; + int N_RB_DL=106,mu=2; //unsigned char frame_type = 0; @@ -794,6 +794,10 @@ int main(int argc, char **argv) fs = 61.44e6; bw = 40e6; } + else if (mu == 2 && N_RB_DL == 106) { + fs = 122.88e6; + bw = 80e6; + } else if (mu == 3 && N_RB_DL == 66) { fs = 122.88e6; bw = 100e6; diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index d05908e8ed..c4092c132d 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -228,6 +228,16 @@ #define GNB_CONFIG_STRING_ULPTRSREOFFSET_0 "ul_ptrsReOffset_0" #define GNB_CONFIG_STRING_ULPTRSMAXPORTS_0 "ul_ptrsMaxPorts_0" #define GNB_CONFIG_STRING_ULPTRSPOWER_0 "ul_ptrsPower_0" +#define GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID "firstActiveDownlinkBWP-Id" +#define GNB_CONFIG_STRING_BWP_ID_0 "bwp-Id_0" +#define GNB_CONFIG_STRING_BWP_ID_1 "bwp-Id_1" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_0 "locationAndBandwidth_0" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_1 "locationAndBandwidth_1" +#define GNB_CONFIG_STRING_SCS_0 "subcarrierSpacing_0" +#define GNB_CONFIG_STRING_SCS_1 "subcarrierSpacing_1" +#define GNB_CONFIG_STRING_DEFAULTDLBWP_ID "defaultDownlinkBWP-Id" +#define GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID "firstActiveUplinkBWP-Id" + /*--------------------------------------------------------------------------------------------------------------------*/ /* pdcch_ConfigSIB1 parameters */ @@ -432,7 +442,11 @@ {GNB_CONFIG_STRING_ULPTRSTIMEDENSITY2_0,NULL,0,i64ptr:scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[2],defint64val:-1,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSREOFFSET_0,NULL,0,i64ptr:scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset,defint64val:-1,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSMAXPORTS_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->maxNrofPorts,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_ULPTRSPOWER_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->ptrs_Power,defint64val:0,TYPE_INT64,0}} +{GNB_CONFIG_STRING_ULPTRSPOWER_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->ptrs_Power,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID,NULL,0,i64ptr:scd->firstActiveDownlinkBWP_Id,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID,NULL,0,i64ptr:scd->uplinkConfig->firstActiveUplinkBWP_Id,defint64val:0,TYPE_INT64,0}} //, \ +{GNB_CONFIG_STRING_BWP_ID_0,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[0].bwp_Id,defint64val:0,TYPE_INT64,0}}//, \ +{GNB_CONFIG_STRING_BWP_ID_1,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:0,TYPE_INT64,0}} diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index e578e98c5e..cbc710c0cc 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -326,16 +326,26 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } /* Function to allocate dedicated serving cell config strutures */ +//Abhi : error at this function : when adding for loop to add new elements of downlinkBWP-ToAddModList. void prepare_scd(NR_ServingCellConfig_t *scd) { // Allocate downlink structures - scd->downlinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->downlinkBWP_ToAddModList)); - + scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); + scd->bwp_InactivityTimer = CALLOC(1, sizeof(*scd->bwp_InactivityTimer)); + scd->uplinkConfig->firstActiveUplinkBWP_Id = CALLOC(1, sizeof(*scd->uplinkConfig->firstActiveUplinkBWP_Id)); + scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); + scd->firstActiveDownlinkBWP_Id = CALLOC(1, sizeof(*scd->firstActiveDownlinkBWP_Id)); +for (int j = 0 ;j < 4 ; j++) +{ + // Downlink bandwidth part NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); bwp->bwp_Id = 1; // Allocate downlink dedicated bandwidth part and PDSCH structures + bwp->bwp_Common = calloc(1, sizeof(*bwp->bwp_Common)); + bwp->bwp_Common->pdcch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); + bwp->bwp_Common->pdsch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Dedicated = calloc(1, sizeof(*bwp->bwp_Dedicated)); bwp->bwp_Dedicated->pdsch_Config = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config)); bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; @@ -353,6 +363,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { NR_PTRS_DownlinkCfg->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->frequencyDensity)); long *dl_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { + dl_rbs[i]=1; ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->frequencyDensity->list, &dl_rbs[i]); } NR_PTRS_DownlinkCfg->timeDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->timeDensity)); @@ -367,9 +378,6 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { // Allocate uplink structures - scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); - scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); - NR_PUSCH_Config_t *pusch_Config = CALLOC(1, sizeof(*pusch_Config)); // Allocate UL DMRS and PTRS structures @@ -385,6 +393,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity)); long *n_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { + n_rbs[i] = 1; ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity->list, &n_rbs[i]); } NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity)); @@ -398,6 +407,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { // UL bandwidth part NR_BWP_Uplink_t *ubwp = CALLOC(1, sizeof(*ubwp)); ubwp->bwp_Id = 1; + ubwp->bwp_Common = CALLOC(1, sizeof(*ubwp->bwp_Common)); ubwp->bwp_Dedicated = CALLOC(1, sizeof(*ubwp->bwp_Dedicated)); ubwp->bwp_Dedicated->pusch_Config = CALLOC(1, sizeof(*ubwp->bwp_Dedicated->pusch_Config)); @@ -406,6 +416,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { ASN_SEQUENCE_ADD(&scd->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } +} /* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { @@ -825,6 +836,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { memset((void*)scd,0,sizeof(NR_ServingCellConfig_t)); prepare_scd(scd); paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); + printf("RRC BWP 0: %d", scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Id); + printf("RRC BWP 1: %d", scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id); paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; ////////// Physical parameters @@ -906,13 +919,14 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { if (SCDsParamList.numelt > 0) { sprintf(aprefix, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, 0); config_get( SCDsParams,sizeof(SCDsParams)/sizeof(paramdef_t),aprefix); - LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d \n", + LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d, BWP_ID %d \n", (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[2], - (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); + (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset, + (int)*scd->firstActiveDownlinkBWP_Id); } fix_scd(scd); diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index ef358f40fe..4a03f81d98 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2880,7 +2880,9 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, dci_pdu->antenna_ports.nbits = getAntPortBitWidth(typeA,typeB); size += dci_pdu->antenna_ports.nbits; // Tx Config Indication - long *isTciEnable = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]->tci_PresentInDCI; + //Abhi : Check this -- bwp_id to replaced with corset_id ? each BWP can have multiple CORSETs .. this has to be corset_id + int corset_id = 1 ; + long *isTciEnable = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI; if (isTciEnable != NULL) { dci_pdu->transmission_configuration_indication.nbits = 3; size += dci_pdu->transmission_configuration_indication.nbits; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 79e6eca300..45d8cf533c 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -333,20 +333,21 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; - if (bwp_ind && dci_format){ +//Abhi : TODO : uncomment this when DCI based swithching is available + // if (bwp_ind && dci_format){ - switch(*dci_format){ - case NR_UL_DCI_FORMAT_0_1: - mac->UL_BWP_Id = *bwp_ind; - break; - case NR_DL_DCI_FORMAT_1_1: - mac->DL_BWP_Id = *bwp_ind; - break; - default: - LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); - } + // switch(*dci_format){ + // case NR_UL_DCI_FORMAT_0_1: + // mac->UL_BWP_Id = *bwp_ind; + // break; + // case NR_DL_DCI_FORMAT_1_1: + // mac->DL_BWP_Id = *bwp_ind; + // break; + // default: + // LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); + // } - } else { + // } else { if (scd->firstActiveDownlinkBWP_Id) mac->DL_BWP_Id = *scd->firstActiveDownlinkBWP_Id; @@ -360,9 +361,9 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format else mac->UL_BWP_Id = 1; - } + // } - LOG_D(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); + LOG_I(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); } @@ -373,15 +374,18 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format */ void config_control_ue(NR_UE_MAC_INST_t *mac){ + //Abhi : Hardcoded CORSET ID here uint8_t coreset_id = 1, ss_id; NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; AssertFatal(scd->downlinkBWP_ToAddModList != NULL, "downlinkBWP_ToAddModList is null\n"); - AssertFatal(scd->downlinkBWP_ToAddModList->list.count == 1, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); + //AssertFatal(scd->downlinkBWP_ToAddModList->list.count == 1, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); config_bwp_ue(mac, NULL, NULL); NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; AssertFatal(dl_bwp_id != 0, "DL_BWP_Id is 0!"); + AssertFatal(ul_bwp_id != 0, "UL_BWP_Id is 0!"); NR_BWP_DownlinkCommon_t *bwp_Common = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Common; AssertFatal(bwp_Common != NULL, "bwp_Common is null\n"); @@ -389,6 +393,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ NR_BWP_DownlinkDedicated_t *dl_bwp_Dedicated = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Dedicated; AssertFatal(dl_bwp_Dedicated != NULL, "dl_bwp_Dedicated is null\n"); + //Abhi : TODO : add assert statements for UL BWP! NR_SetupRelease_PDCCH_Config_t *pdcch_Config = dl_bwp_Dedicated->pdcch_Config; AssertFatal(pdcch_Config != NULL, "pdcch_Config is null\n"); @@ -412,14 +417,20 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ struct NR_UplinkConfig__uplinkBWP_ToAddModList *uplinkBWP_ToAddModList = scd->uplinkConfig->uplinkBWP_ToAddModList; AssertFatal(uplinkBWP_ToAddModList != NULL, "uplinkBWP_ToAddModList is null\n"); - AssertFatal(uplinkBWP_ToAddModList->list.count == 1, "uplinkBWP_ToAddModList->list->count is %d\n", uplinkBWP_ToAddModList->list.count); + // AssertFatal(uplinkBWP_ToAddModList->list.count == 1, "uplinkBWP_ToAddModList->list->count is %d\n", uplinkBWP_ToAddModList->list.count); // check pdcch_Config, pdcch_ConfigCommon and DL BWP - mac->DLbwp[0] = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]; + // Check dedicated UL BWP and pass to MAC + for (int num_BWP = 0; num_BWP DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP]; + mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP]; + } + mac->coreset[dl_bwp_id - 1][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; - // Check dedicated UL BWP and pass to MAC - mac->ULbwp[0] = uplinkBWP_ToAddModList->list.array[0]; + + AssertFatal(mac->ULbwp[0]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); // check available Search Spaces in the searchSpacesToAddModList and pass to MAC @@ -443,7 +454,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ AssertFatal(css->searchSpaceType != NULL, "css->searchSpaceType is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot != NULL, "css->monitoringSymbolsWithinSlot is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot->buf != NULL, "css->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[0][0][ss_id] = css; + mac->SSpace[dl_bwp_id -1][coreset_id -1][ss_id] = css; ss_id++; } diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 332cd600d3..8d7b26bed9 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -74,7 +74,7 @@ // ========== #define NB_NR_UE_MAC_INST 1 -#define MAX_NUM_BWP 2 +#define MAX_NUM_BWP 4 #define NUM_SLOT_FRAME 10 /*!\brief value for indicating BSR Timer is not running */ diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index f879599f7d..8875695eaf 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -224,7 +224,9 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - uint8_t bwp_id = 1, coreset_id = 1; + //Abhi : harcoded CORSET ID here + uint8_t bwp_id = mac->DL_BWP_Id; + uint8_t coreset_id = 1; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; NR_BWP_Downlink_t *bwp = mac->DLbwp[bwp_id - 1]; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index adbf7a5774..14bdc962c3 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -434,6 +434,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr AssertFatal(mac->DLbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); AssertFatal(mac->ULbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); + //Abhi : TODO const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); LOG_D(MAC, "In %s: Processing received DCI format %s (DL BWP %d)\n", __FUNCTION__, dci_formats[dci_format], n_RB_DLBWP); @@ -786,10 +787,11 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr * 47 DMRS_SEQ_INI: */ - if (dci->bwp_indicator.val != 1) { - LOG_W(MAC, "[%d.%d] bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n", frame, slot); - return -1; - } + //Abhi : temporarily commenting this out + // if (dci->bwp_indicator.val != 1) { + // LOG_W(MAC, "[%d.%d] bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n", frame, slot); + // return -1; + // } config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; @@ -1787,7 +1789,7 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, return; } - LOG_D(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); + LOG_I(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); while (!done && pdu_len > 0){ mac_ce_len = 0x0000; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 4932119a83..2950c6b9d4 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -621,10 +621,11 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } else if (*dci_format == NR_UL_DCI_FORMAT_0_1) { /* BANDWIDTH_PART_IND */ - if (dci->bwp_indicator.val != 1) { - LOG_W(MAC, "bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n"); - return -1; - } + //Abhi : why must BWP always be 1 ? + // if (dci->bwp_indicator.val != 1) { + // LOG_W(MAC, "bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n"); + // return -1; + // } config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format); target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 36f687b5cd..ee54386c5f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -325,11 +325,52 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - const int bwp_id = 1; + int nb_periods_per_frame; gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + + // NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; + // const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; + + // switch(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity) { + // case 0: + // nb_periods_per_frame = 20; // 10ms/0p5ms + // break; + + // case 1: + // nb_periods_per_frame = 16; // 10ms/0p625ms + // break; + + // case 2: + // nb_periods_per_frame = 10; // 10ms/1ms + // break; + + // case 3: + // nb_periods_per_frame = 8; // 10ms/1p25ms + // break; + + // case 4: + // nb_periods_per_frame = 5; // 10ms/2ms + // break; + + // case 5: + // nb_periods_per_frame = 4; // 10ms/2p5ms + // break; + + // case 6: + // nb_periods_per_frame = 2; // 10ms/5ms + // break; + + // case 7: + // nb_periods_per_frame = 1; // 10ms/10ms + // break; + + // default: + // AssertFatal(1==0,"Undefined tdd period %ld\n", scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); + + } if (slot==0 && (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]>=257)) { const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 7c1cbea9b6..e16b43f84f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -790,8 +790,8 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->crnti); - AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); + // AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, + // "downlinkBWP_ToAddModList has %d BWP!\n", ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); NR_BWP_Uplink_t *ubwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; LOG_D(NR_MAC, "Frame %d, Subframe %d Adding Msg3 UL Config Request for (%d,%d) : (%d,%d,%d) for rnti: %d\n", frameP, @@ -986,9 +986,9 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->RA_rnti); - AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", - ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); + // AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, + // "downlinkBWP_ToAddModList has %d BWP!\n", + // ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); // SCF222: PDU index incremented for each PDSCH PDU sent in TX control message. This is used to associate control // information to data and is reset every slot. diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index cb1a73edae..ba32c9d361 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -819,8 +819,8 @@ void nr_schedule_ue_spec(module_id_t module_id, harq->is_waiting = true; UE_info->mac_stats[UE_id].dlsch_rounds[harq->round]++; - LOG_D(MAC, - "%4d.%2d RNTI %04x start %3d RBs %3d startSymbol %2d nb_symbol %2d MCS %2d TBS %4d HARQ PID %2d round %d NDI %d\n", + LOG_I(MAC, + "%4d.%2d RNTI %04x start %d RBs %d startSymbol %d nb_symbsol %d MCS %d TBS %d HARQ PID %d round %d NDI %d\n", frame, slot, rnti, @@ -952,11 +952,13 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_pdu_rel15_t dci_payload; memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t)); // bwp indicator + const int n_dl_bwp = UE_info->secondaryCellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; - AssertFatal(n_dl_bwp == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", - n_dl_bwp); + // AssertFatal(n_dl_bwp == 1, + // "downlinkBWP_ToAddModList has %d BWP!\n", + // n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 + // Abhi :look into this dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, "Only frequency resource allocation type 1 is currently supported\n"); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index b3d2ecb0b2..160232cfd8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -973,7 +973,8 @@ void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, if (bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList != NULL) AssertFatal(1==0,"Aperiodic ZP CSI-RS currently not supported\n"); // transmission configuration indication - if (bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]->tci_PresentInDCI != NULL) + int corset_id = 1; //TODO : corset_id to come from higher layers + if (bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI != NULL) AssertFatal(1==0,"TCI in DCI currently not supported\n"); //srs resource set if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching!=NULL) { diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index c1ec27580f..36c8ac7932 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -72,7 +72,7 @@ /* Defs */ -#define MAX_NUM_BWP 2 +#define MAX_NUM_BWP 4 #define MAX_NUM_CORESET 2 #define MAX_NUM_CCE 90 #define MAX_HARQ_ROUNDS 4 diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index a789f0c34e..14ad8b7a9d 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -105,6 +105,11 @@ void handle_nr_uci(NR_UL_IND_t *UL_info) } UL_info->uci_ind.num_ucis = 0; + + // mark corresponding PUCCH resources as free + // NOTE: we just assume it is BWP ID 1, to be revised for multiple BWPs + //Abhi : TODO + RC.nrmac[mod_id]->pucch_index_used[1][slot] = 0; } diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 8ed19e9d82..0d153ff5cb 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -280,6 +280,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->choice.setup->ext1 = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->choice.setup->ext1)); secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->choice.setup->ext1->t311 = NR_RLF_TimersAndConstants__ext1__t311_ms30000; secondaryCellGroup->spCellConfig->rlmInSyncOutOfSyncThreshold = NULL; + if (servingcellconfigdedicated) { secondaryCellGroup->spCellConfig->spCellConfigDedicated = servingcellconfigdedicated; } else { @@ -460,23 +461,31 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; + //Abhi : Fix me + // Code has to loop thorough multiple BWP wherever necessary NR_BWP_Downlink_t *bwp = NULL; + for (int bwp_loop = 0; bwp_loop < 4 ; bwp_loop++ ) + { + if (servingcellconfigdedicated) { - bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[0]; + bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; } else { secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); bwp=calloc(1,sizeof(*bwp)); } - bwp->bwp_Id=1; + bwp->bwp_Id = bwp_loop +1; //Abhi: remove this ? BWP is added to asn only when scd is not present + +#pragma region bwp-downlink-Common bwp->bwp_Common=calloc(1,sizeof(*bwp->bwp_Common)); // copy common BWP size from initial BWP except for bandwdith memcpy((void*)&bwp->bwp_Common->genericParameters, &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, sizeof(bwp->bwp_Common->genericParameters)); bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; //Abhi : hardcoded + #pragma region bwp_common->pdcch_ConfigCommon bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); @@ -485,6 +494,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); +#pragma region corset NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); coreset->controlResourceSetId=1; // frequency domain resources depends on BWP size @@ -522,11 +532,13 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco coreset->pdcch_DMRS_ScramblingID = NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; +#pragma endregion coreset bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); +#pragma region CommonSearchSpace NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); ss->searchSpaceId = 1; ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); @@ -562,12 +574,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; +#pragma endregion pdcch_ConfigCommon bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); +#pragma endregion bwp_Common->pdcch_ConfigCommon // copy PDSCH TimeDomainResourceAllocation from InitialBWP NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; @@ -581,10 +595,15 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pdschi->startSymbolAndLength = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); } +#pragma endregion bwp_common + if (!servingcellconfigdedicated) { bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); } + + #pragma region bwp-downlink-Dedicated + bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); @@ -754,9 +773,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + #pragma endregion bwp-downlink-Dedicated + + //Abhi : FIX ME! + //secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; //Abhi: this has to be removed secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); @@ -766,6 +788,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); } +#pragma region bwp-uplink NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; initialUplinkBWP->pucch_Config = NULL; @@ -773,10 +796,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; NR_PUSCH_Config_t *pusch_Config = NULL; if (servingcellconfigdedicated) { - pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup; + pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Dedicated->pusch_Config->choice.setup; } else { pusch_Config = calloc(1,sizeof(*pusch_Config)); } + + #pragma region pusch-config-ubwp-dedicated initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; @@ -870,12 +895,15 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco } /*----------------------------------------------------------------------------------------------------------------------------*/ + #pragma endregion PUSCH-Config-ubwp-dedicated initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); initialUplinkBWP->srs_Config->choice.setup=srs_Config; srs_Config->srs_ResourceSetToReleaseList=NULL; + + #pragma region srs_Config-ubwp-dedicated srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); srs_resset0->srs_ResourceSetId = 0; @@ -927,15 +955,17 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); +#pragma endregion srs_config + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; NR_BWP_Uplink_t *ubwp = NULL; if (servingcellconfigdedicated) { - ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]; + ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; } else { secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); ubwp = calloc(1,sizeof(*ubwp)); } - ubwp->bwp_Id=1; + ubwp->bwp_Id=bwp_loop+1; ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); // copy bwp_Common from Initial UL BWP except for bandwidth memcpy((void*)&ubwp->bwp_Common->genericParameters, @@ -950,6 +980,9 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); } + + #pragma region bwp-uplink-Dedicated + ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); @@ -1077,18 +1110,21 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; +#pragma endregion bwp-uplink-Dedicated if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } - - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; +#pragma endregion bwp-uplink-Config + } + //secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig)); NR_PUSCH_ServingCellConfig_t *pusch_scc = calloc(1,sizeof(*pusch_scc)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->present = NR_SetupRelease_PUSCH_ServingCellConfig_PR_setup; secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup = pusch_scc; + pusch_scc->codeBlockGroupTransmission = NULL; pusch_scc->rateMatching = NULL; pusch_scc->xOverhead = NULL; diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf index b57cb00779..51a78bed40 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf @@ -175,6 +175,88 @@ gNBs = ); +# Dedicated Serving Cell Configuration + servingCellConfigDedicated = ( + { + # BWP-Downlink + # DL PTRS related parameters. + # 3GPP TS 38.331 PTRS-DownlinkConfig + + # BWP 1 Configuration + + bwp-Id_0 = 1; + locationAndBandwidth_0 = 1234; //TODO + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing_0 = 1 ; + + # BWP 2 Configuration + + bwp-Id_1 = 1; + locationAndBandwidth_1 = 1234; //TODO + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing_1 = 1 ; + + + # frequencyDensity value range: 1-276 + dl_ptrsFreqDensity0_0 = 25; + dl_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + dl_ptrsTimeDensity0_0 = 2; + dl_ptrsTimeDensity1_0 = 4; + dl_ptrsTimeDensity2_0 = 10; + + # epre_Ratio + # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 + dl_ptrsEpreRatio_0 = 0; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 + dl_ptrsReOffset_0 = 0; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + defaultDownlinkBWP-Id = 1; #BWP-Id + + # UplinkConfig + firstActiveUplinkBWP-Id = 1 ; #BWP-Id + + #BWP-Uplink + # UL PTRS related parameters. + # 3GPP TS 38.331 PTRS-UplinkConfig + + # frequencyDensity value range: 1-276 + ul_ptrsFreqDensity0_0 = 25; + ul_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + ul_ptrsTimeDensity0_0 = 2; + ul_ptrsTimeDensity1_0 = 4; + ul_ptrsTimeDensity2_0 = 10; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 + ul_ptrsReOffset_0 = 0; + + # maxNrofPorts + # 0 = n1, 1 = n2 + ul_ptrsMaxPorts_0 = 0; + + # ptrs-Power + # 0 = p00, 1 = p01, 2 = p10, 3 = p11 + #ul_ptrsPower_0 = 0; + + } + ); # ------- SCTP definitions SCTP : From 8e79241cbee1aa3044be95be5e73852f13eb1675 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Mon, 24 May 2021 14:18:26 +0530 Subject: [PATCH 002/233] Fixed compilation issues --- cmake_targets/CMakeLists.txt | 2 +- cmake_targets/epc_test/CMakeLists.template | 2 +- openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 5 +++-- openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 4 ---- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index acc27404c0..8d8e88455a 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -396,7 +396,7 @@ add_definitions("-DPACKAGE_BUGREPORT=\"openair4g-devel@lists.eurecom.fr\"") # Debug related options ######################################### -add_boolean_option(ASN_DEBUG True "ASN1 coder/decoder Debug") +add_boolean_option(ASN_DEBUG False "ASN1 coder/decoder Debug") add_boolean_option(EMIT_ASN_DEBUG False "ASN1 coder/decoder Debug") add_boolean_option(MSG_PRINT False "print debug messages") add_boolean_option(DISABLE_XER_PRINT False "print XER Format") diff --git a/cmake_targets/epc_test/CMakeLists.template b/cmake_targets/epc_test/CMakeLists.template index 1efbd1340c..7a097fffb0 100755 --- a/cmake_targets/epc_test/CMakeLists.template +++ b/cmake_targets/epc_test/CMakeLists.template @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) set ( CMAKE_BUILD_TYPE "RelWithDebInfo" ) -set ( ASN_DEBUG True) +set ( ASN_DEBUG False) set ( DEBUG_OMG False ) set ( DISABLE_XER_PRINT False ) set ( DRIVER2013 True ) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index abf8faf225..aed4cb68ed 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -353,7 +353,7 @@ int main(int argc, char **argv) NB_UE_INST = 1; //double pbch_sinr; //int pbch_tx_ant; - int N_RB_DL=106,mu=2; + int N_RB_DL=106,mu=1; //unsigned char frame_type = 0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index ee54386c5f..a0c172a700 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -330,9 +330,10 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; + // NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; - // const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; // switch(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity) { // case 0: @@ -370,7 +371,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, // default: // AssertFatal(1==0,"Undefined tdd period %ld\n", scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); - } + // } if (slot==0 && (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]>=257)) { const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index 14ad8b7a9d..7bf738e043 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -106,10 +106,6 @@ void handle_nr_uci(NR_UL_IND_t *UL_info) UL_info->uci_ind.num_ucis = 0; - // mark corresponding PUCCH resources as free - // NOTE: we just assume it is BWP ID 1, to be revised for multiple BWPs - //Abhi : TODO - RC.nrmac[mod_id]->pucch_index_used[1][slot] = 0; } From 0ec806f5fe9c0af437ca4bc9f27564432232ae34 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Tue, 25 May 2021 20:16:31 +0530 Subject: [PATCH 003/233] bugfix : pdcch and BWP switch capability --- .vscode/launch.json | 38 +++------- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 28 ++++---- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 74 +++++++++++--------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 30 ++++---- 5 files changed, 80 insertions(+), 92 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 79964cec9d..01a7af51a0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,12 +9,12 @@ "name": "(gdb) Launch UE PHY Simu", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", "args": ["--rfsimulator.serveraddr","127.0.0.1", "--rfsim", "--phy-test", - "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -34,12 +34,12 @@ "name": "(gdb) Launch UE RA", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", "args": ["--rfsimulator.serveraddr","127.0.0.1", "--rfsim", "--do-ra", - "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -59,9 +59,9 @@ "name": "(gdb) Launch gNB PHY Simu", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", "--rfsim", "--phy-test" ], @@ -83,9 +83,9 @@ "name": "(gdb) Launch gNB RA", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", "--rfsim", "--do-ra" ], @@ -102,27 +102,7 @@ "ignoreFailures": true } ] - }, - { - "name": "(gdb) dlsim", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/phy_simulators/build/nr_dlsim", - "args": ["-n", "4", "-m", "2" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] } + ] } diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index 0455c481d5..9e3707cae8 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -417,7 +417,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ int dmrs_scrambling_id=0,data_scrambling_id=0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_PUCCH_Resource_t *pucch_resource; + NR_PUCCH_Resource_t *pucch_resource = NULL; uint16_t crnti = mac->crnti; NR_BWP_Id_t bwp_id = mac->UL_BWP_Id; @@ -497,7 +497,8 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ pucch_resource_set = find_pucch_resource_set( mac, gNB_id, N_UCI); if (pucch_resource_set != MAX_NB_OF_PUCCH_RESOURCE_SETS) { pucch_resource_indicator = 0; - pucch_resource_id = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set]->resourceList.list.array[pucch_resource_indicator][0]; /* get the first resource of the set */ + /* get the first resource of the set */ + pucch_resource_id = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set]->resourceList.list.array[pucch_resource_indicator][0]; } else { LOG_W(PHY,"PUCCH no resource set found for CSI at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); @@ -639,7 +640,6 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ int O_CRC = 0; nb_symbols = nb_symbols_total; /* by default, it can be reduced due to symbols reserved for dmrs */ - pucch_resource = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.array[pucch_resource_id]; switch(format) { case pucch_format0_nr: @@ -937,10 +937,10 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int N_m_c_rx = 0; int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + if (mac->DLbwp[dl_bwp_id-1] == NULL) return 0; - if (mac->DLbwp[0] == NULL) return 0; - - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { two_transport_blocks = TRUE; number_of_code_word = 2; } @@ -1018,7 +1018,7 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t N_m_c_rx = number_harq_feedback; int N_SPS_c = 0; /* FFS TODO_NR multicells and SPS are not supported at the moment */ if (mac->scg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH == NULL) { - int N_TB_max_DL = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; + int N_TB_max_DL = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; *n_HARQ_ACK = (((V_DAI_m_DL - U_DAI_c)%4) * N_TB_max_DL) + N_m_c_rx + N_SPS_c; NR_TST_PHY_PRINTF("PUCCH power n(%d) = ( V(%d) - U(%d) )mod4 * N_TB(%d) + N(%d) \n", *n_HARQ_ACK, V_DAI_m_DL, U_DAI_c, N_TB_max_DL, N_m_c_rx); } @@ -1122,7 +1122,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 int current_resource_id = MAX_NB_OF_PUCCH_RESOURCES; pucch_format_nr_t format_pucch; int ready_pucch_resource_id = FALSE; /* in the case that it is already given */ - NR_PUCCH_Resource_t *pucch_resource; + NR_PUCCH_Resource_t *pucch_resource = NULL; NR_BWP_Id_t bwp_id = mac->UL_BWP_Id; /* ini values to unset */ @@ -1236,15 +1236,13 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 } } - /*uint8_t pucch_resource_count = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList.list.count; + uint8_t pucch_resource_count = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.count; for (uint8_t i=0; iULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList.list.array[i]->pucch_ResourceId == current_resource_id) - pucch_resource = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList.list.array[i]; - }*/ - - pucch_resource = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.array[current_resource_id]; + if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.array[i]->pucch_ResourceId == current_resource_id) + pucch_resource = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.array[i]; + } if (pucch_resource != NULL) { - format_pucch = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.array[current_resource_id]->format.present; + format_pucch = pucch_resource->format.present; nb_symbols_for_tx = get_nb_symbols_pucch(pucch_resource, format_pucch); if (check_pucch_format(mac, gNB_id, format_pucch, nb_symbols_for_tx, uci_size) == TRUE) { diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 45d8cf533c..85e7dc7afc 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -442,7 +442,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ AssertFatal(*ss->controlResourceSetId == mac->coreset[dl_bwp_id - 1][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); AssertFatal(ss->monitoringSymbolsWithinSlot != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot->buf != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[0][0][ss_id] = ss; + mac->SSpace[dl_bwp_id-1][0][ss_id] = ss; } // Check available CSSs in the commonSearchSpaceList (list of additional common search spaces) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 14bdc962c3..83fb5924aa 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -272,6 +272,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t sliv_S=0; uint8_t sliv_L=0; uint8_t mu_pusch = 1; + NR_BWP_Id_t dl_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // definition table j Table 6.1.2.1.1-4 uint8_t j = (mu_pusch==3)?3:(mu_pusch==2)?2:1; @@ -317,10 +319,10 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(dlsch_config_pdu != NULL){ NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; if (pdsch_TimeDomainAllocationList && use_default==false) { if (time_domain_ind >= pdsch_TimeDomainAllocationList->list.count) { @@ -370,11 +372,11 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(pusch_config_pdu != NULL){ NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; + if (mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } if (pusch_TimeDomainAllocationList && use_default==false) { @@ -429,13 +431,15 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; //const uint16_t n_RB_DLBWP = dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP; //make sure this has been set AssertFatal(mac->DLbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); AssertFatal(mac->ULbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); //Abhi : TODO - const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); LOG_D(MAC, "In %s: Processing received DCI format %s (DL BWP %d)\n", __FUNCTION__, dci_formats[dci_format], n_RB_DLBWP); @@ -618,14 +622,14 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr if (get_softmodem_params()->sa) { dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } else { // NSA mode is not using the Initial BWP - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdsch_config = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup; + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; } } else { - dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[0]->bwp_Common->genericParameters.subcarrierSpacing; - pdsch_config = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup; + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; + pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; } BWPSize = n_RB_DLBWP; } @@ -643,10 +647,10 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; int mappingtype = pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; @@ -709,9 +713,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - pucch_res_set_cnt = mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -793,7 +797,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr // return -1; // } config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH; @@ -801,9 +805,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_1 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[0]->bwp_Common->genericParameters.subcarrierSpacing; + dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; /* IDENTIFIER_DCI_FORMATS */ /* CARRIER_IND */ @@ -821,10 +825,10 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; int mappingtype = pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; @@ -883,9 +887,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_1->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - pucch_res_set_cnt = mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -897,7 +901,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr /* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */ // according to TS 38.213 Table 9.2.3-1 - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + dlsch_config_pdu_1_1->pdsch_to_harq_feedback_time_ind = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; @@ -1103,6 +1107,7 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ int N_RB = 0, start_RB; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; switch(rnti_type) { case NR_RNTI_RA: case NR_RNTI_TC: @@ -1121,7 +1126,7 @@ int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ N_RB = mac->type0_PDCCH_CSS_config.num_rbs; break; case NR_RNTI_C: - N_RB = NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + N_RB = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); break; } return N_RB; @@ -1140,8 +1145,9 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, AssertFatal(mac->DLbwp[0] != NULL, "DLbwp[0] shouldn't be null here!\n"); AssertFatal(mac->ULbwp[0] != NULL, "ULbwp[0] shouldn't be null here!\n"); int N_RB = get_n_rb(mac, rnti_type); + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; int N_RB_UL = (mac->scg != NULL) ? - NRRIV2BW(mac->ULbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : + NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); int pos=0; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 2950c6b9d4..d23c489bc6 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -94,14 +94,15 @@ void fill_scheduled_response(nr_scheduled_response_t *scheduled_response, */ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { long k2 = -1; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Get K2 from RRC configuration - NR_PUSCH_Config_t *pusch_config=mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; if (pusch_config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } if (pusch_TimeDomainAllocationList) { if (time_domain_ind >= pusch_TimeDomainAllocationList->list.count) { @@ -126,6 +127,7 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { */ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) { + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; //Check if request to access ul_config is for a UL slot if (is_nr_UL_slot(mac->scc, slot, mac->frame_type) == 0) { LOG_W(MAC, "Slot %d is not a UL slot. %s called for wrong slot!!!\n", slot, __FUNCTION__); @@ -135,7 +137,7 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl // Calculate the index of the UL slot in mac->ul_config_request list. This is // based on the TDD pattern (slot configuration period) and number of UL+mixed // slots in the period. TS 38.213 Sec 11.1 - int mu = mac->ULbwp[0]->bwp_Common->genericParameters.subcarrierSpacing; + int mu = mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; NR_TDD_UL_DL_Pattern_t *tdd_pattern = &mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1; const int num_slots_per_tdd = nr_slots_per_frame[mu] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity); const int num_slots_ul = tdd_pattern->nrofUplinkSlots + (tdd_pattern->nrofUplinkSymbols!=0); @@ -152,7 +154,8 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) { NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -290,9 +293,9 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf /* ANTENNA_PORTS */ uint8_t rank = 0; // We need to initialize rank FIXME!!! - + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -498,6 +501,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t *scc = mac->scc; int rnti_type = get_rnti_type(mac, rnti); + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Common configuration pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; @@ -512,7 +516,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, // Note: for Msg3 or MsgA PUSCH transmission the N_PRB_oh is always set to 0 - NR_BWP_Uplink_t *ubwp = mac->ULbwp[0]; + NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id-1]; NR_BWP_UplinkDedicated_t *ibwp = mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP; NR_PUSCH_Config_t *pusch_Config = ibwp->pusch_Config->choice.setup; int startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->startSymbolAndLength; @@ -599,8 +603,8 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int target_ss; bool valid_ptrs_setup = 0; - uint16_t n_RB_ULBWP = NRRIV2BW(mac->ULbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + uint16_t n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; // Basic sanity check for MCS value to check for a false or erroneous DCI if (dci->mcs > 28) { @@ -642,8 +646,8 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (pusch_Config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_Config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } int mappingtype = pusch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; @@ -1021,7 +1025,7 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, uint8_t tda_id){ int delta = 0; - NR_BWP_Uplink_t *ubwp = mac->ULbwp[0]; + NR_BWP_Uplink_t *ubwp = mac->ULbwp[mac->UL_BWP_Id-1]; // Get the numerology to calculate the Tx frame and slot int mu = ubwp->bwp_Common->genericParameters.subcarrierSpacing; struct NR_PUSCH_TimeDomainResourceAllocationList *pusch_TimeDomainAllocationList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; From 1ce9f1fccbf4c7f891c648955c0a8a852a3e4fa1 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Wed, 26 May 2021 21:20:02 +0530 Subject: [PATCH 004/233] pucch errors fixed --- .vscode/launch.json | 108 ----------------- openair2/GNB_APP/gnb_config.c | 113 +++++++++--------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 4 +- .../CONF/gnb.band78.tm1.106PRB.usrpn300.conf | 39 +----- 4 files changed, 64 insertions(+), 200 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 01a7af51a0..0000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - - { - "name": "(gdb) Launch UE PHY Simu", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", - "args": ["--rfsimulator.serveraddr","127.0.0.1", - "--rfsim", - "--phy-test", - - "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, - { - "name": "(gdb) Launch UE RA", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", - "args": ["--rfsimulator.serveraddr","127.0.0.1", - "--rfsim", - "--do-ra", - - "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, - { - "name": "(gdb) Launch gNB PHY Simu", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", - "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", - "--rfsim", - "--phy-test" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, - { - "name": "(gdb) Launch gNB RA", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", - "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", - "--rfsim", - "--do-ra" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } - - ] -} diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index cbc710c0cc..c0cac495cd 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -421,76 +421,79 @@ for (int j = 0 ;j < 4 ; j++) /* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { // Check for DL PTRS parameters validity - if (scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { - // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.count-1; i >= 0; i--) { - if ((*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] < 1) - || (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] > 276)) { - LOG_I(RRC, "DL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; - break; + for (int bwp_i = 0 ; bwp_i< MAX_NUM_BWP; bwp_i++) + { + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.count-1; i >= 0; i--) { + if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] < 1) + || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] > 276)) { + LOG_I(RRC, "DL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { - // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.count-1; i >= 0; i--) { - if ((*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] < 0) - || (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] > 29)) { - LOG_I(RRC, "DL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; - break; + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.count-1; i >= 0; i--) { + if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] < 0) + || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] > 29)) { + LOG_I(RRC, "DL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { - if (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset > 2) { - LOG_I(RRC, "Freeing DL PTRS resourceElementOffset \n"); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset = NULL; + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + if (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset > 2) { + LOG_I(RRC, "Freeing DL PTRS resourceElementOffset \n"); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset = NULL; + } + if (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio > 1) { + LOG_I(RRC, "Freeing DL PTRS epre_Ratio \n"); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio = NULL; + } } - if (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio > 1) { - LOG_I(RRC, "Freeing DL PTRS epre_Ratio \n"); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio = NULL; - } - } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { - // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.count-1; i >= 0; i--) { - if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) - || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] > 276)) { - LOG_I(RRC, "UL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; - break; + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.count-1; i >= 0; i--) { + if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) + || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] > 276)) { + LOG_I(RRC, "UL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { - // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.count-1; i >= 0; i--) { - if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] < 0) - || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] > 29)) { - LOG_I(RRC, "UL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; - break; + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.count-1; i >= 0; i--) { + if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] < 0) + || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] > 29)) { + LOG_I(RRC, "UL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { // Check for UL PTRS parameters validity - if (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { + if (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { LOG_I(RRC, "Freeing UL PTRS resourceElementOffset \n"); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; + free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; } } + } } void RCconfig_nr_flexran() diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index d23c489bc6..689f71cf88 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -748,11 +748,11 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } /* PTRS */ - if (mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS != NULL) { + if (mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS != NULL) { if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; - valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, + valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[mac->UL_BWP_Id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, pusch_config_pdu->rb_size, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table, &pusch_config_pdu->pusch_ptrs.ptrs_freq_density,&pusch_config_pdu->pusch_ptrs.ptrs_time_density, &pusch_config_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset,&pusch_config_pdu->pusch_ptrs.num_ptrs_ports, diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf index 51a78bed40..d3f886332e 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf @@ -199,25 +199,9 @@ gNBs = subcarrierSpacing_1 = 1 ; - # frequencyDensity value range: 1-276 - dl_ptrsFreqDensity0_0 = 25; - dl_ptrsFreqDensity1_0 = 75; + - # timeDensity value range: 0-29 - dl_ptrsTimeDensity0_0 = 2; - dl_ptrsTimeDensity1_0 = 4; - dl_ptrsTimeDensity2_0 = 10; - - # epre_Ratio - # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 - dl_ptrsEpreRatio_0 = 0; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 - dl_ptrsReOffset_0 = 0; - - firstActiveDownlinkBWP-Id = 1; #BWP-Id + firstActiveDownlinkBWP-Id = 2; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -227,29 +211,14 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 1 ; #BWP-Id + firstActiveUplinkBWP-Id = 2 ; #BWP-Id #BWP-Uplink # UL PTRS related parameters. # 3GPP TS 38.331 PTRS-UplinkConfig # frequencyDensity value range: 1-276 - ul_ptrsFreqDensity0_0 = 25; - ul_ptrsFreqDensity1_0 = 75; - - # timeDensity value range: 0-29 - ul_ptrsTimeDensity0_0 = 2; - ul_ptrsTimeDensity1_0 = 4; - ul_ptrsTimeDensity2_0 = 10; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 - ul_ptrsReOffset_0 = 0; - - # maxNrofPorts - # 0 = n1, 1 = n2 - ul_ptrsMaxPorts_0 = 0; + # ptrs-Power # 0 = p00, 1 = p01, 2 = p10, 3 = p11 From 0afec1e3dd1b1d57b5322fb041017d29d1b7792e Mon Sep 17 00:00:00 2001 From: Abhijith Date: Thu, 27 May 2021 18:10:46 +0530 Subject: [PATCH 005/233] Access SCD parameters from config file --- openair2/GNB_APP/RRC_nr_paramsvalues.h | 37 +- openair2/GNB_APP/gnb_config.c | 32 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 1 - openair2/LAYER2/NR_MAC_UE/config_ue.c | 31 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 1 - openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 7 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 6 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 9 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- openair2/RRC/NR/rrc_gNB_reconfig.c | 51 +-- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 320 ++++++++++++++++++ .../CONF/gnb.band78.tm1.106PRB.usrpn300.conf | 51 --- 13 files changed, 407 insertions(+), 145 deletions(-) create mode 100644 targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index c4092c132d..a410bcac6a 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -228,13 +228,19 @@ #define GNB_CONFIG_STRING_ULPTRSREOFFSET_0 "ul_ptrsReOffset_0" #define GNB_CONFIG_STRING_ULPTRSMAXPORTS_0 "ul_ptrsMaxPorts_0" #define GNB_CONFIG_STRING_ULPTRSPOWER_0 "ul_ptrsPower_0" +#define GNB_CONFIG_STRING_DLBWP1_ID "bwp-Id_1" +#define GNB_CONFIG_STRING_DLBWP2_ID "bwp-Id_2" +#define GNB_CONFIG_STRING_DLBWP3_ID "bwp-Id_3" +#define GNB_CONFIG_STRING_DLBWP4_ID "bwp-Id_4" #define GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID "firstActiveDownlinkBWP-Id" -#define GNB_CONFIG_STRING_BWP_ID_0 "bwp-Id_0" -#define GNB_CONFIG_STRING_BWP_ID_1 "bwp-Id_1" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_0 "locationAndBandwidth_0" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_1 "locationAndBandwidth_1" -#define GNB_CONFIG_STRING_SCS_0 "subcarrierSpacing_0" -#define GNB_CONFIG_STRING_SCS_1 "subcarrierSpacing_1" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP1 "bwp1_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP2 "bwp2_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP3 "bwp3_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP4 "bwp4_locationAndBandwidth" +#define GNB_CONFIG_STRING_SCS_BWP1 "bwp1_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_BWP2 "bwp2_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_BWP3 "bwp3_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_BWP4 "bwp4_subcarrierSpacing" #define GNB_CONFIG_STRING_DEFAULTDLBWP_ID "defaultDownlinkBWP-Id" #define GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID "firstActiveUplinkBWP-Id" @@ -443,10 +449,21 @@ {GNB_CONFIG_STRING_ULPTRSREOFFSET_0,NULL,0,i64ptr:scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset,defint64val:-1,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSMAXPORTS_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->maxNrofPorts,defint64val:0,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSPOWER_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->ptrs_Power,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID,NULL,0,i64ptr:scd->firstActiveDownlinkBWP_Id,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID,NULL,0,i64ptr:scd->uplinkConfig->firstActiveUplinkBWP_Id,defint64val:0,TYPE_INT64,0}} //, \ -{GNB_CONFIG_STRING_BWP_ID_0,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[0].bwp_Id,defint64val:0,TYPE_INT64,0}}//, \ -{GNB_CONFIG_STRING_BWP_ID_1,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:0,TYPE_INT64,0}} +{GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID,NULL,0,i64ptr:scd->firstActiveDownlinkBWP_Id,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID,NULL,0,i64ptr:scd->uplinkConfig->firstActiveUplinkBWP_Id,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP1_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Id,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP2_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:2,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP3_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Id,defint64val:3,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP4_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Id,defint64val:4,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}} + diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index c0cac495cd..b6d3f5657e 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -326,24 +326,32 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } /* Function to allocate dedicated serving cell config strutures */ -//Abhi : error at this function : when adding for loop to add new elements of downlinkBWP-ToAddModList. -void prepare_scd(NR_ServingCellConfig_t *scd) { +void prepare_scd(NR_ServingCellConfig_t *scd, int scd_present) { // Allocate downlink structures scd->downlinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->downlinkBWP_ToAddModList)); scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); + scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); + + if(scd_present) + { scd->bwp_InactivityTimer = CALLOC(1, sizeof(*scd->bwp_InactivityTimer)); scd->uplinkConfig->firstActiveUplinkBWP_Id = CALLOC(1, sizeof(*scd->uplinkConfig->firstActiveUplinkBWP_Id)); - scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); scd->firstActiveDownlinkBWP_Id = CALLOC(1, sizeof(*scd->firstActiveDownlinkBWP_Id)); + *scd->firstActiveDownlinkBWP_Id = 1; + *scd->uplinkConfig->firstActiveUplinkBWP_Id = 1; + } + for (int j = 0 ;j < 4 ; j++) { // Downlink bandwidth part NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); - bwp->bwp_Id = 1; + bwp->bwp_Id = j+1; // Allocate downlink dedicated bandwidth part and PDSCH structures bwp->bwp_Common = calloc(1, sizeof(*bwp->bwp_Common)); + // bwp->bwp_Common->genericParameters.locationAndBandwidth = calloc(1, sizeof(*bwp->bwp_Common->genericParameters.locationAndBandwidth) ); + // bwp->bwp_Common->genericParameters.subcarrierSpacing = calloc(1, sizeof(*bwp->bwp_Common->genericParameters.subcarrierSpacing) ); bwp->bwp_Common->pdcch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Dedicated = calloc(1, sizeof(*bwp->bwp_Dedicated)); @@ -406,7 +414,7 @@ for (int j = 0 ;j < 4 ; j++) // UL bandwidth part NR_BWP_Uplink_t *ubwp = CALLOC(1, sizeof(*ubwp)); - ubwp->bwp_Id = 1; + ubwp->bwp_Id = j+1; ubwp->bwp_Common = CALLOC(1, sizeof(*ubwp->bwp_Common)); ubwp->bwp_Dedicated = CALLOC(1, sizeof(*ubwp->bwp_Dedicated)); @@ -837,11 +845,10 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { // Serving Cell Config Dedicated NR_ServingCellConfig_t *scd = calloc(1,sizeof(NR_ServingCellConfig_t)); memset((void*)scd,0,sizeof(NR_ServingCellConfig_t)); - prepare_scd(scd); - paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); - printf("RRC BWP 0: %d", scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Id); - printf("RRC BWP 1: %d", scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id); paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; + prepare_scd(scd, SCDsParamList.numelt); + paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); + ////////// Physical parameters @@ -922,14 +929,17 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { if (SCDsParamList.numelt > 0) { sprintf(aprefix, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, 0); config_get( SCDsParams,sizeof(SCDsParams)/sizeof(paramdef_t),aprefix); - LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d, BWP_ID %d \n", + LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d, First_active_BWP_ID %d SCS %d, LocationandBW %d \n", (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[2], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset, - (int)*scd->firstActiveDownlinkBWP_Id); + (int)*scd->firstActiveDownlinkBWP_Id, + (int)scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.subcarrierSpacing, + (int)scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth + ); } fix_scd(scd); diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 4a03f81d98..91f009da30 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2880,7 +2880,6 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, dci_pdu->antenna_ports.nbits = getAntPortBitWidth(typeA,typeB); size += dci_pdu->antenna_ports.nbits; // Tx Config Indication - //Abhi : Check this -- bwp_id to replaced with corset_id ? each BWP can have multiple CORSETs .. this has to be corset_id int corset_id = 1 ; long *isTciEnable = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI; if (isTciEnable != NULL) { diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 85e7dc7afc..2b10ff1c0b 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -333,21 +333,20 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; -//Abhi : TODO : uncomment this when DCI based swithching is available - // if (bwp_ind && dci_format){ + if (bwp_ind && dci_format){ - // switch(*dci_format){ - // case NR_UL_DCI_FORMAT_0_1: - // mac->UL_BWP_Id = *bwp_ind; - // break; - // case NR_DL_DCI_FORMAT_1_1: - // mac->DL_BWP_Id = *bwp_ind; - // break; - // default: - // LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); - // } + switch(*dci_format){ + case NR_UL_DCI_FORMAT_0_1: + mac->UL_BWP_Id = *bwp_ind; + break; + case NR_DL_DCI_FORMAT_1_1: + mac->DL_BWP_Id = *bwp_ind; + break; + default: + LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); + } - // } else { + } else { if (scd->firstActiveDownlinkBWP_Id) mac->DL_BWP_Id = *scd->firstActiveDownlinkBWP_Id; @@ -361,7 +360,7 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format else mac->UL_BWP_Id = 1; - // } + } LOG_I(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); @@ -374,12 +373,11 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format */ void config_control_ue(NR_UE_MAC_INST_t *mac){ - //Abhi : Hardcoded CORSET ID here uint8_t coreset_id = 1, ss_id; NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; AssertFatal(scd->downlinkBWP_ToAddModList != NULL, "downlinkBWP_ToAddModList is null\n"); - //AssertFatal(scd->downlinkBWP_ToAddModList->list.count == 1, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); + AssertFatal(scd->downlinkBWP_ToAddModList->list.count <= 4, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); config_bwp_ue(mac, NULL, NULL); NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; @@ -393,7 +391,6 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ NR_BWP_DownlinkDedicated_t *dl_bwp_Dedicated = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Dedicated; AssertFatal(dl_bwp_Dedicated != NULL, "dl_bwp_Dedicated is null\n"); - //Abhi : TODO : add assert statements for UL BWP! NR_SetupRelease_PDCCH_Config_t *pdcch_Config = dl_bwp_Dedicated->pdcch_Config; AssertFatal(pdcch_Config != NULL, "pdcch_Config is null\n"); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 8875695eaf..cea476354f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -224,7 +224,6 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - //Abhi : harcoded CORSET ID here uint8_t bwp_id = mac->DL_BWP_Id; uint8_t coreset_id = 1; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 83fb5924aa..f8fe612e5f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -438,7 +438,6 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr AssertFatal(mac->DLbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); AssertFatal(mac->ULbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); - //Abhi : TODO const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); LOG_D(MAC, "In %s: Processing received DCI format %s (DL BWP %d)\n", __FUNCTION__, dci_formats[dci_format], n_RB_DLBWP); @@ -791,11 +790,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr * 47 DMRS_SEQ_INI: */ - //Abhi : temporarily commenting this out - // if (dci->bwp_indicator.val != 1) { - // LOG_W(MAC, "[%d.%d] bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n", frame, slot); - // return -1; - // } + config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 689f71cf88..72e15afe68 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -625,11 +625,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } else if (*dci_format == NR_UL_DCI_FORMAT_0_1) { /* BANDWIDTH_PART_IND */ - //Abhi : why must BWP always be 1 ? - // if (dci->bwp_indicator.val != 1) { - // LOG_W(MAC, "bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n"); - // return -1; - // } + config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format); target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index e16b43f84f..bab78d2f57 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1067,7 +1067,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.mcs = pdsch_pdu_rel15->mcsIndex[0]; dci_payload.tb_scaling = tb_scaling; - LOG_D(NR_MAC, + LOG_I(NR_MAC, "[RAPROC] DCI type 1 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d \n", dci_payload.frequency_domain_assignment.val, pdsch_pdu_rel15->rbStart, @@ -1078,7 +1078,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.mcs, dci_payload.tb_scaling); - LOG_D(NR_MAC, + LOG_I(NR_MAC, "[RAPROC] DCI params: rnti 0x%x, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", pdcch_pdu_rel15->dci_pdu[0].RNTI, NR_RNTI_RA, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index ba32c9d361..8b31043c54 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -954,12 +954,11 @@ void nr_schedule_ue_spec(module_id_t module_id, // bwp indicator const int n_dl_bwp = UE_info->secondaryCellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; - // AssertFatal(n_dl_bwp == 1, - // "downlinkBWP_ToAddModList has %d BWP!\n", - // n_dl_bwp); + AssertFatal(n_dl_bwp <= 4, + "downlinkBWP_ToAddModList has %d BWP!\n", + n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 - // Abhi :look into this - dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; + dci_payload.bwp_indicator.val = n_dl_bwp <= 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, "Only frequency resource allocation type 1 is currently supported\n"); dci_payload.frequency_domain_assignment.val = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 160232cfd8..f24ddd0eb4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -615,7 +615,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, case NR_UL_DCI_FORMAT_0_1: dci_pdu_rel15->dai[0].val = 0; //TODO // bwp indicator as per table 7.3.1.1.2-1 in 38.212 - dci_pdu_rel15->bwp_indicator.val = n_ubwp < 4 ? bwp_id : bwp_id - 1; + dci_pdu_rel15->bwp_indicator.val = n_ubwp <= 4 ? bwp_id : bwp_id - 1; // SRS resource indicator if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) { AssertFatal(*ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig == NR_PUSCH_Config__txConfig_codebook, diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 0d153ff5cb..52fc295dad 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -461,8 +461,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; - //Abhi : Fix me - // Code has to loop thorough multiple BWP wherever necessary NR_BWP_Downlink_t *bwp = NULL; for (int bwp_loop = 0; bwp_loop < 4 ; bwp_loop++ ) { @@ -473,19 +471,16 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); bwp=calloc(1,sizeof(*bwp)); + // copy common BWP size from initial BWP except for bandwdith + bwp->bwp_Id = bwp_loop +1; + + memcpy((void*)&bwp->bwp_Common->genericParameters, + &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, + sizeof(bwp->bwp_Common->genericParameters)); + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); } - bwp->bwp_Id = bwp_loop +1; //Abhi: remove this ? BWP is added to asn only when scd is not present -#pragma region bwp-downlink-Common - bwp->bwp_Common=calloc(1,sizeof(*bwp->bwp_Common)); - // copy common BWP size from initial BWP except for bandwdith - memcpy((void*)&bwp->bwp_Common->genericParameters, - &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, - sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; //Abhi : hardcoded - - #pragma region bwp_common->pdcch_ConfigCommon bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); @@ -494,7 +489,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); -#pragma region corset NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); coreset->controlResourceSetId=1; // frequency domain resources depends on BWP size @@ -532,13 +526,11 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco coreset->pdcch_DMRS_ScramblingID = NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; -#pragma endregion coreset bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); -#pragma region CommonSearchSpace NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); ss->searchSpaceId = 1; ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); @@ -574,14 +566,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; -#pragma endregion pdcch_ConfigCommon bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); -#pragma endregion bwp_Common->pdcch_ConfigCommon // copy PDSCH TimeDomainResourceAllocation from InitialBWP NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; @@ -595,14 +585,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pdschi->startSymbolAndLength = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); } -#pragma endregion bwp_common if (!servingcellconfigdedicated) { bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); } - #pragma region bwp-downlink-Dedicated bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; @@ -773,12 +761,11 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - #pragma endregion bwp-downlink-Dedicated - //Abhi : FIX ME! - //secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - - //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; //Abhi: this has to be removed + if (!servingcellconfigdedicated) { //TODO : check if code goes here + secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + } secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); @@ -788,7 +775,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); } -#pragma region bwp-uplink NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; initialUplinkBWP->pucch_Config = NULL; @@ -801,7 +787,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pusch_Config = calloc(1,sizeof(*pusch_Config)); } - #pragma region pusch-config-ubwp-dedicated initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; @@ -895,7 +880,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco } /*----------------------------------------------------------------------------------------------------------------------------*/ - #pragma endregion PUSCH-Config-ubwp-dedicated initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; @@ -903,7 +887,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco initialUplinkBWP->srs_Config->choice.setup=srs_Config; srs_Config->srs_ResourceSetToReleaseList=NULL; - #pragma region srs_Config-ubwp-dedicated srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); srs_resset0->srs_ResourceSetId = 0; @@ -955,7 +938,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); -#pragma endregion srs_config secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; NR_BWP_Uplink_t *ubwp = NULL; @@ -981,7 +963,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); } - #pragma region bwp-uplink-Dedicated ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; @@ -1110,16 +1091,16 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; -#pragma endregion bwp-uplink-Dedicated if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } -#pragma endregion bwp-uplink-Config } - //secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; + if (!servingcellconfigdedicated) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; + } secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig)); NR_PUSCH_ServingCellConfig_t *pusch_scc = calloc(1,sizeof(*pusch_scc)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->present = NR_SetupRelease_PUSCH_ServingCellConfig_PR_setup; diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf new file mode 100644 index 0000000000..73217360ff --- /dev/null +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -0,0 +1,320 @@ +Active_gNBs = ( "gNB-Eurecom-5GNRBox"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + + cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-Eurecom-5GNRBox"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + + plmn_list = ({mcc = 208; mnc = 93; mnc_length = 2;}); + + tr_s_preference = "local_mac" + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + pusch_AntennaPorts = 1; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP), points to Subcarrier 0 of RB#10 of SSB block + absoluteFrequencySSB = 641032; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640000; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=33 (Start RB of SSB block),L=24 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 6358; #6368; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 0; #12; + initialDLBWPsearchSpaceZero = 0; + #pdsch-ConfigCommon + #pdschTimeDomainAllocationList (up to 16 entries) + initialDLBWPk0_0 = 0; # used for (full) DL slot + initialDLBWPmappingType_0 = 0; # 0=typeA,1=typeB + initialDLBWPstartSymbolAndLength_0 = 40; # this is SS=1,L=13 + + initialDLBWPk0_1 = 0; # used for DL part mixed slot + initialDLBWPmappingType_1 = 0; + initialDLBWPstartSymbolAndLength_1 = 57; # this is SS=1,L=5 + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 6358;#6368; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -118; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + # pusch-ConfigCommon (up to 16 elements) + initialULBWPk2_0 = 6; # used for (full) UL slot + initialULBWPmappingType_0 = 1 + initialULBWPstartSymbolAndLength_0 = 41; # this is SS=0 L=13 + + initialULBWPk2_1 = 6; # used for UL part mixed slot + initialULBWPmappingType_1 = 1; + initialULBWPstartSymbolAndLength_1 = 69; # this is SS=10 L=2 + + initialULBWPk2_2 = 7; # used for Msg.3 during RA + initialULBWPmappingType_2 = 1; + initialULBWPstartSymbolAndLength_2 = 52; # this is SS=10 L=4 + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 0x1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + +# Dedicated Serving Cell Configuration + servingCellConfigDedicated = ( + { + # BWP-Downlink + # BWP 1 Configuration + bwp-Id_1 = 1; + bwp1_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp1_subcarrierSpacing = 1 ; + # BWP 2 Configuration + bwp-Id_2 = 2; + bwp2_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp2_subcarrierSpacing = 1 ; + # BWP 3 Configuration + bwp-Id_3 = 3; + bwp3_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp3_subcarrierSpacing = 1 ; + # BWP 4 Configuration + bwp-Id_4 = 4; + bwp4_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp4_subcarrierSpacing = 1 ; + + firstActiveDownlinkBWP-Id = 2; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + defaultDownlinkBWP-Id = 1; #BWP-Id + + # UplinkConfig + firstActiveUplinkBWP-Id = 2 ; #BWP-Id + + #BWP-Uplink + + + } + ); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// MME parameters: + mme_ip_address = ( { ipv4 = "192.168.12.26"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + NETWORK_INTERFACES : + { + + GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1_MME = "192.168.12.111/24"; + GNB_INTERFACE_NAME_FOR_S1U = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1U = "192.168.12.111/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + } +); + +L1s = ( + { + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + } +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [7]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 50; + eNB_instances = [0]; + ## beamforming 1x2 matrix: 1 layer x 2 antennas + #bf_weights = [0x00007fff, 0x00007fff]; + ## beamforming 1x4 matrix: 1 layer x 4 antennas + #bf_weights = [0x00007fff, 0x0000,0x0000, 0x0000]; + ## beamforming 2x2 matrix: + #bf_weights = [0x00007fff, 0x00000000, 0x00000000, 0x00007fff]; + ## beamforming 4x4 matrix: + #bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff]; + sdr_addrs = "addr=192.168.10.2,mgmt_addr=192.168.10.2,second_addr=192.168.20.2"; + clock_src = "external"; + } +); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + + log_config : + { + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + }; + diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf index d3f886332e..b57cb00779 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf @@ -175,57 +175,6 @@ gNBs = ); -# Dedicated Serving Cell Configuration - servingCellConfigDedicated = ( - { - # BWP-Downlink - # DL PTRS related parameters. - # 3GPP TS 38.331 PTRS-DownlinkConfig - - # BWP 1 Configuration - - bwp-Id_0 = 1; - locationAndBandwidth_0 = 1234; //TODO - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - subcarrierSpacing_0 = 1 ; - - # BWP 2 Configuration - - bwp-Id_1 = 1; - locationAndBandwidth_1 = 1234; //TODO - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - subcarrierSpacing_1 = 1 ; - - - - - firstActiveDownlinkBWP-Id = 2; #BWP-Id - - # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, - # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, - # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, - # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } - - defaultDownlinkBWP-Id = 1; #BWP-Id - - # UplinkConfig - firstActiveUplinkBWP-Id = 2 ; #BWP-Id - - #BWP-Uplink - # UL PTRS related parameters. - # 3GPP TS 38.331 PTRS-UplinkConfig - - # frequencyDensity value range: 1-276 - - - # ptrs-Power - # 0 = p00, 1 = p01, 2 = p10, 3 = p11 - #ul_ptrsPower_0 = 0; - - } - ); # ------- SCTP definitions SCTP : From 0628d544801de76dfb621c49460c9f17607e5b5b Mon Sep 17 00:00:00 2001 From: Abhijith Date: Thu, 27 May 2021 20:49:55 +0530 Subject: [PATCH 006/233] scs and bwp location error fixed --- openair2/GNB_APP/gnb_config.c | 12 +++++------- openair2/RRC/NR/rrc_gNB_reconfig.c | 14 ++++++-------- .../CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 4 ++-- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index b6d3f5657e..bfbe454a32 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -326,20 +326,17 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } /* Function to allocate dedicated serving cell config strutures */ -void prepare_scd(NR_ServingCellConfig_t *scd, int scd_present) { +void prepare_scd(NR_ServingCellConfig_t *scd) { // Allocate downlink structures scd->downlinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->downlinkBWP_ToAddModList)); scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); - - if(scd_present) - { scd->bwp_InactivityTimer = CALLOC(1, sizeof(*scd->bwp_InactivityTimer)); scd->uplinkConfig->firstActiveUplinkBWP_Id = CALLOC(1, sizeof(*scd->uplinkConfig->firstActiveUplinkBWP_Id)); scd->firstActiveDownlinkBWP_Id = CALLOC(1, sizeof(*scd->firstActiveDownlinkBWP_Id)); *scd->firstActiveDownlinkBWP_Id = 1; *scd->uplinkConfig->firstActiveUplinkBWP_Id = 1; - } + for (int j = 0 ;j < 4 ; j++) { @@ -845,9 +842,10 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { // Serving Cell Config Dedicated NR_ServingCellConfig_t *scd = calloc(1,sizeof(NR_ServingCellConfig_t)); memset((void*)scd,0,sizeof(NR_ServingCellConfig_t)); - paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; - prepare_scd(scd, SCDsParamList.numelt); + prepare_scd(scd); paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); + paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; + ////////// Physical parameters diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 52fc295dad..72b0fd1d34 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -473,12 +473,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp=calloc(1,sizeof(*bwp)); // copy common BWP size from initial BWP except for bandwdith bwp->bwp_Id = bwp_loop +1; - - memcpy((void*)&bwp->bwp_Common->genericParameters, + } + if(bwp->bwp_Common->genericParameters.subcarrierSpacing == 0 || bwp->bwp_Common->genericParameters.locationAndBandwidth == 0) + { + memcpy((void*)&bwp->bwp_Common->genericParameters, &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); } bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); @@ -762,10 +764,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - if (!servingcellconfigdedicated) { //TODO : check if code goes here - secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; - } secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 73217360ff..9212bff4b8 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -204,7 +204,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp4_subcarrierSpacing = 1 ; - firstActiveDownlinkBWP-Id = 2; #BWP-Id + firstActiveDownlinkBWP-Id = 1; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -214,7 +214,7 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 2 ; #BWP-Id + firstActiveUplinkBWP-Id = 1 ; #BWP-Id #BWP-Uplink From b2fedd793ce6a8de85d5f70573e21ad0d1cc065d Mon Sep 17 00:00:00 2001 From: Abhijith Date: Tue, 1 Jun 2021 15:50:16 +0530 Subject: [PATCH 007/233] Fixed error with BWP_Id 4 --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 4 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 8 +-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 28 +++++------ .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 6 +-- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 11 +++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 24 ++++----- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 49 ++++++++++++++++++- 9 files changed, 91 insertions(+), 45 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 2b10ff1c0b..0ef76d6c8b 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -337,10 +337,10 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format switch(*dci_format){ case NR_UL_DCI_FORMAT_0_1: - mac->UL_BWP_Id = *bwp_ind; + mac->UL_BWP_Id = *bwp_ind +1 ; break; case NR_DL_DCI_FORMAT_1_1: - mac->DL_BWP_Id = *bwp_ind; + mac->DL_BWP_Id = *bwp_ind +1; break; default: LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 72e15afe68..ecf1ce1a83 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -748,7 +748,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; - valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[mac->UL_BWP_Id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, + valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, pusch_config_pdu->rb_size, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table, &pusch_config_pdu->pusch_ptrs.ptrs_freq_density,&pusch_config_pdu->pusch_ptrs.ptrs_time_density, &pusch_config_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset,&pusch_config_pdu->pusch_ptrs.num_ptrs_ports, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index a0c172a700..17bec32905 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -397,8 +397,8 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing); } - memset(RC.nrmac[module_idP]->cce_list[bwp_id][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - memset(RC.nrmac[module_idP]->cce_list[bwp_id][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 + memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 + memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) for (int i=0; icontrolResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -971,7 +971,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1284,7 +1284,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -1293,7 +1293,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 8b31043c54..98d5362e75 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -60,7 +60,7 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = bwp->bwp_Id; - if (nrmac->preferred_dl_tda[bwp_id]) + if (nrmac->preferred_dl_tda[bwp_id-1]) return; /* there is a mixed slot only when in TDD */ @@ -118,17 +118,17 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_dl_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id])); + nrmac->preferred_dl_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id-1])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int i = 0; i < n; ++i) { - nrmac->preferred_dl_tda[bwp_id][i] = -1; + nrmac->preferred_dl_tda[bwp_id-1][i] = -1; if (!tdd || i % nr_slots_period < tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id][i] = 0; + nrmac->preferred_dl_tda[bwp_id-1][i] = 0; else if (tdd && nr_mix_slots && i % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id][i] = tdaMi; - LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id][i]); + nrmac->preferred_dl_tda[bwp_id-1][i] = tdaMi; + LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id-1][i]); } } @@ -429,7 +429,7 @@ bool allocate_dl_retransmission(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = 1; int rbSize = 0; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; if (tda == retInfo->time_domain_allocation) { /* Check that there are enough resources for retransmission */ while (rbSize < retInfo->rbSize) { @@ -503,7 +503,7 @@ bool allocate_dl_retransmission(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return false; @@ -628,7 +628,7 @@ void pf_dl(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -642,7 +642,7 @@ void pf_dl(module_id_t module_id, /* MCS has been set above */ const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) @@ -688,7 +688,7 @@ void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; if (tda < 0) return; @@ -844,7 +844,7 @@ void nr_schedule_ue_spec(module_id_t module_id, * exist, create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; if (!pdcch_pdu) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -853,7 +853,7 @@ void nr_schedule_ue_spec(module_id_t module_id, dl_req->nPDUs += 1; pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, bwp); - gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu; + gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -958,7 +958,7 @@ void nr_schedule_ue_spec(module_id_t module_id, "downlinkBWP_ToAddModList has %d BWP!\n", n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 - dci_payload.bwp_indicator.val = n_dl_bwp <= 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; + dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, "Only frequency resource allocation type 1 is currently supported\n"); dci_payload.frequency_domain_assignment.val = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index adfcd79b8e..0eef5a59b9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -349,7 +349,7 @@ void nr_preprocessor_phytest(module_id_t module_id, frame, slot); UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -364,7 +364,7 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_pdsch->pucch_allocation = alloc; sched_pdsch->rbStart = rbStart; sched_pdsch->rbSize = rbSize; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; const uint8_t num_dmrs_cdm_grps_no_data = 1; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( @@ -414,7 +414,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (tda < 0) return false; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index f24ddd0eb4..47e5d4e6e4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -177,9 +177,9 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, int *cce_list; if( bwp==NULL || bwp->bwp_Id == 0 ) { - cce_list = nr_mac->cce_list[1][0]; + cce_list = nr_mac->cce_list[0][0]; } else { - cce_list = nr_mac->cce_list[bwp->bwp_Id][coreset_id]; + cce_list = nr_mac->cce_list[bwp->bwp_Id-1][coreset_id]; } int n_rb=0; @@ -615,7 +615,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, case NR_UL_DCI_FORMAT_0_1: dci_pdu_rel15->dai[0].val = 0; //TODO // bwp indicator as per table 7.3.1.1.2-1 in 38.212 - dci_pdu_rel15->bwp_indicator.val = n_ubwp <= 4 ? bwp_id : bwp_id - 1; + dci_pdu_rel15->bwp_indicator.val = n_ubwp < 4 ? bwp_id : bwp_id - 1; // SRS resource indicator if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) { AssertFatal(*ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig == NR_PUSCH_Config__txConfig_codebook, @@ -632,7 +632,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, } LOG_D(MAC, - "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n", + "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d UL_BWP_ID %d\n", __func__, dci_pdu_rel15->frequency_domain_assignment.val, dci_pdu_rel15->time_domain_assignment.val, @@ -640,7 +640,8 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, dci_pdu_rel15->mcs, dci_pdu_rel15->tpc, dci_pdu_rel15->ndi, - dci_pdu_rel15->rv); + dci_pdu_rel15->rv, + dci_pdu_rel15->bwp_indicator.val); } void nr_configure_pdcch(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 323baa48a7..c881f19de6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -65,7 +65,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = ubwp->bwp_Id; - if (nrmac->preferred_ul_tda[bwp_id]) + if (nrmac->preferred_ul_tda[bwp_id-1]) return; /* there is a mixed slot only when in TDD */ @@ -151,18 +151,18 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_ul_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id])); + nrmac->preferred_ul_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id-1])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int slot = 0; slot < n; ++slot) { const int sched_slot = (slot + k2) % n; - nrmac->preferred_ul_tda[bwp_id][slot] = -1; + nrmac->preferred_ul_tda[bwp_id-1][slot] = -1; if (!tdd || sched_slot % nr_slots_period >= tdd->nrofDownlinkSlots + nr_mix_slots) - nrmac->preferred_ul_tda[bwp_id][slot] = 0; + nrmac->preferred_ul_tda[bwp_id-1][slot] = 0; else if (tdd && nr_mix_slots && sched_slot % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_ul_tda[bwp_id][slot] = tdaMi; - LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id][slot]); + nrmac->preferred_ul_tda[bwp_id-1][slot] = tdaMi; + LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id-1][slot]); } if (k2 < tdd->nrofUplinkSlots) @@ -799,7 +799,7 @@ bool allocate_ul_retransmission(module_id_t module_id, NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (tda == retInfo->time_domain_allocation) { /* Check the resource is enough for retransmission */ while (rbStart < bwpSize && !rballoc_mask[rbStart]) @@ -986,7 +986,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1076,7 +1076,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1126,7 +1126,7 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * schedule now (slot + k2 is not UL slot) */ int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (tda < 0) return false; int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu); @@ -1461,7 +1461,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) * create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid-1][coresetid]; if (!pdcch_pdu) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); @@ -1470,7 +1470,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, sched_ctrl->active_bwp); - pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu; + pdcch_pdu_bwp_coreset[bwpid-1][coresetid] = pdcch_pdu; } LOG_D(MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 9212bff4b8..74777ecced 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -204,7 +204,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp4_subcarrierSpacing = 1 ; - firstActiveDownlinkBWP-Id = 1; #BWP-Id + firstActiveDownlinkBWP-Id = 4; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -214,9 +214,54 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 1 ; #BWP-Id + firstActiveUplinkBWP-Id = 4 ; #BWP-Id #BWP-Uplink + # DL PTRS related parameters. + # 3GPP TS 38.331 PTRS-DownlinkConfig + + # frequencyDensity value range: 1-276 + dl_ptrsFreqDensity0_0 = 25; + dl_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + dl_ptrsTimeDensity0_0 = 2; + dl_ptrsTimeDensity1_0 = 4; + dl_ptrsTimeDensity2_0 = 10; + + # epre_Ratio + # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 + dl_ptrsEpreRatio_0 = 0; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 + dl_ptrsReOffset_0 = 0; + + # UL PTRS related parameters. + # 3GPP TS 38.331 PTRS-UplinkConfig + + # frequencyDensity value range: 1-276 + ul_ptrsFreqDensity0_0 = 25; + ul_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + ul_ptrsTimeDensity0_0 = 2; + ul_ptrsTimeDensity1_0 = 4; + ul_ptrsTimeDensity2_0 = 10; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 + ul_ptrsReOffset_0 = 0; + + # maxNrofPorts + # 0 = n1, 1 = n2 + ul_ptrsMaxPorts_0 = 0; + + # ptrs-Power + # 0 = p00, 1 = p01, 2 = p10, 3 = p11 + #ul_ptrsPower_0 = 0; } From 82ba9b96f46b770a580a6310896f50fc6621d191 Mon Sep 17 00:00:00 2001 From: Abhijith Atreya Date: Tue, 1 Jun 2021 11:35:03 +0000 Subject: [PATCH 008/233] Fixed PTRS errors --- openair2/GNB_APP/gnb_config.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index bfbe454a32..ff295a1757 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -368,7 +368,6 @@ for (int j = 0 ;j < 4 ; j++) NR_PTRS_DownlinkCfg->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->frequencyDensity)); long *dl_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { - dl_rbs[i]=1; ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->frequencyDensity->list, &dl_rbs[i]); } NR_PTRS_DownlinkCfg->timeDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->timeDensity)); @@ -398,7 +397,6 @@ for (int j = 0 ;j < 4 ; j++) NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity)); long *n_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { - n_rbs[i] = 1; ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity->list, &n_rbs[i]); } NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity)); From e9a6efc9c976cd8de5e9ed6de107a2428018a667 Mon Sep 17 00:00:00 2001 From: Shruthi Date: Wed, 2 Jun 2021 12:17:08 +0530 Subject: [PATCH 009/233] PTRS paramaters removed --- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 50 +------------------ 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 74777ecced..6bbf4541ff 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -204,7 +204,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp4_subcarrierSpacing = 1 ; - firstActiveDownlinkBWP-Id = 4; #BWP-Id + firstActiveDownlinkBWP-Id = 1; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -214,55 +214,9 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 4 ; #BWP-Id + firstActiveUplinkBWP-Id = 1; #BWP-Id #BWP-Uplink - # DL PTRS related parameters. - # 3GPP TS 38.331 PTRS-DownlinkConfig - - # frequencyDensity value range: 1-276 - dl_ptrsFreqDensity0_0 = 25; - dl_ptrsFreqDensity1_0 = 75; - - # timeDensity value range: 0-29 - dl_ptrsTimeDensity0_0 = 2; - dl_ptrsTimeDensity1_0 = 4; - dl_ptrsTimeDensity2_0 = 10; - - # epre_Ratio - # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 - dl_ptrsEpreRatio_0 = 0; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 - dl_ptrsReOffset_0 = 0; - - # UL PTRS related parameters. - # 3GPP TS 38.331 PTRS-UplinkConfig - - # frequencyDensity value range: 1-276 - ul_ptrsFreqDensity0_0 = 25; - ul_ptrsFreqDensity1_0 = 75; - - # timeDensity value range: 0-29 - ul_ptrsTimeDensity0_0 = 2; - ul_ptrsTimeDensity1_0 = 4; - ul_ptrsTimeDensity2_0 = 10; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 - ul_ptrsReOffset_0 = 0; - - # maxNrofPorts - # 0 = n1, 1 = n2 - ul_ptrsMaxPorts_0 = 0; - - # ptrs-Power - # 0 = p00, 1 = p01, 2 = p10, 3 = p11 - #ul_ptrsPower_0 = 0; - } ); From 698917bf1cb62978594c73b6b568be8f0657a3d4 Mon Sep 17 00:00:00 2001 From: Abhijith Atreya Date: Wed, 2 Jun 2021 14:32:32 +0000 Subject: [PATCH 010/233] Fixed warnings and extra Log messages removed --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 0ef76d6c8b..2c1fe2e998 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -362,7 +362,7 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format } - LOG_I(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); + LOG_D(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index f8fe612e5f..bdf7daa9ee 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -1790,7 +1790,7 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, return; } - LOG_I(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); + LOG_D(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); while (!done && pdu_len > 0){ mac_ce_len = 0x0000; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 17bec32905..42af216a6b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -325,7 +325,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - int nb_periods_per_frame; + //int nb_periods_per_frame; gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 98d5362e75..427f6dedcc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -819,8 +819,8 @@ void nr_schedule_ue_spec(module_id_t module_id, harq->is_waiting = true; UE_info->mac_stats[UE_id].dlsch_rounds[harq->round]++; - LOG_I(MAC, - "%4d.%2d RNTI %04x start %d RBs %d startSymbol %d nb_symbsol %d MCS %d TBS %d HARQ PID %d round %d NDI %d\n", + LOG_D(MAC, + "%4d.%2d RNTI %04x start %3d RBs %3d startSymbol %2d nb_symbol %2d MCS %2d TBS %4d HARQ PID %2d round %d NDI %d\n", frame, slot, rnti, From 4a6f8a7dc770867f8cb770d7eeac0db965d91b5a Mon Sep 17 00:00:00 2001 From: Abhijith Date: Fri, 25 Jun 2021 13:18:52 +0530 Subject: [PATCH 011/233] Review comments incorporated --- .vscode/launch.json | 29 ++++++++++++ openair2/GNB_APP/gnb_config.c | 2 +- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 46 +------------------ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 8 ++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 26 +++++------ .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 21 +++++++-- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 24 +++++----- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 8 ++-- 12 files changed, 86 insertions(+), 88 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..243518f81e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "enter program name, for example ${workspaceFolder}/a.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/path/to/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index ff295a1757..b9f394dad9 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -424,7 +424,7 @@ for (int j = 0 ;j < 4 ; j++) /* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { // Check for DL PTRS parameters validity - for (int bwp_i = 0 ; bwp_i< MAX_NUM_BWP; bwp_i++) + for (int bwp_i = 0 ; bwp_i< 4; bwp_i++) { if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 2c1fe2e998..111fee4883 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -418,7 +418,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ // check pdcch_Config, pdcch_ConfigCommon and DL BWP // Check dedicated UL BWP and pass to MAC - for (int num_BWP = 0; num_BWP DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP]; mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP]; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 8d7b26bed9..c33173cf38 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -74,7 +74,7 @@ // ========== #define NB_NR_UE_MAC_INST 1 -#define MAX_NUM_BWP 4 +#define MAX_NUM_BWP 5 #define NUM_SLOT_FRAME 10 /*!\brief value for indicating BSR Timer is not running */ diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 42af216a6b..f655d3d65e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -325,54 +325,12 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - //int nb_periods_per_frame; gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; - - // NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; - - // switch(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity) { - // case 0: - // nb_periods_per_frame = 20; // 10ms/0p5ms - // break; - - // case 1: - // nb_periods_per_frame = 16; // 10ms/0p625ms - // break; - - // case 2: - // nb_periods_per_frame = 10; // 10ms/1ms - // break; - - // case 3: - // nb_periods_per_frame = 8; // 10ms/1p25ms - // break; - - // case 4: - // nb_periods_per_frame = 5; // 10ms/2ms - // break; - - // case 5: - // nb_periods_per_frame = 4; // 10ms/2p5ms - // break; - - // case 6: - // nb_periods_per_frame = 2; // 10ms/5ms - // break; - - // case 7: - // nb_periods_per_frame = 1; // 10ms/10ms - // break; - - // default: - // AssertFatal(1==0,"Undefined tdd period %ld\n", scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); - - // } - if (slot==0 && (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]>=257)) { const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; const int n = nr_slots_per_frame[*scc->ssbSubcarrierSpacing]; @@ -397,8 +355,8 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing); } - memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 + memset(RC.nrmac[module_idP]->cce_list[bwp_id][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 + memset(RC.nrmac[module_idP]->cce_list[bwp_id][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) for (int i=0; icontrolResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -971,7 +971,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1284,7 +1284,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -1293,7 +1293,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 427f6dedcc..b43c9e2e2c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -60,7 +60,7 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = bwp->bwp_Id; - if (nrmac->preferred_dl_tda[bwp_id-1]) + if (nrmac->preferred_dl_tda[bwp_id]) return; /* there is a mixed slot only when in TDD */ @@ -118,17 +118,17 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_dl_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id-1])); + nrmac->preferred_dl_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int i = 0; i < n; ++i) { - nrmac->preferred_dl_tda[bwp_id-1][i] = -1; + nrmac->preferred_dl_tda[bwp_id][i] = -1; if (!tdd || i % nr_slots_period < tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id-1][i] = 0; + nrmac->preferred_dl_tda[bwp_id][i] = 0; else if (tdd && nr_mix_slots && i % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id-1][i] = tdaMi; - LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id-1][i]); + nrmac->preferred_dl_tda[bwp_id][i] = tdaMi; + LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id][i]); } } @@ -429,7 +429,7 @@ bool allocate_dl_retransmission(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = 1; int rbSize = 0; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; if (tda == retInfo->time_domain_allocation) { /* Check that there are enough resources for retransmission */ while (rbSize < retInfo->rbSize) { @@ -503,7 +503,7 @@ bool allocate_dl_retransmission(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return false; @@ -628,7 +628,7 @@ void pf_dl(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -642,7 +642,7 @@ void pf_dl(module_id_t module_id, /* MCS has been set above */ const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) @@ -688,7 +688,7 @@ void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; if (tda < 0) return; @@ -844,7 +844,7 @@ void nr_schedule_ue_spec(module_id_t module_id, * exist, create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -853,7 +853,7 @@ void nr_schedule_ue_spec(module_id_t module_id, dl_req->nPDUs += 1; pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, bwp); - gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu; + gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 0eef5a59b9..4986ed747d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -282,6 +282,8 @@ void nr_preprocessor_phytest(module_id_t module_id, NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; /* find largest unallocated chunk */ const int bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + + target_dl_bw = (bwpSize < target_dl_bw) ?bwpSize : target_dl_bw; int rbStart = 0; int rbSize = 0; uint16_t *vrb_map = RC.nrmac[module_id]->common_channels[CC_id].vrb_map; @@ -295,7 +297,7 @@ void nr_preprocessor_phytest(module_id_t module_id, while (rbStart + rbSize < bwpSize && !vrb_map[rbStart + rbSize] && rbSize < target_dl_bw) rbSize++; /* found target_dl_bw? */ - if (rbSize == target_dl_bw) + if (rbStart+ rbSize == target_dl_bw) break; /* at end and below target_dl_bw? */ if (rbStart + rbSize >= bwpSize) @@ -349,7 +351,7 @@ void nr_preprocessor_phytest(module_id_t module_id, frame, slot); UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -364,7 +366,7 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_pdsch->pucch_allocation = alloc; sched_pdsch->rbStart = rbStart; sched_pdsch->rbSize = rbSize; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; const uint8_t num_dmrs_cdm_grps_no_data = 1; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( @@ -414,7 +416,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (tda < 0) return false; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = @@ -445,7 +447,16 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); uint16_t rbStart = 0; - uint16_t rbSize = target_ul_bw; + uint16_t rbSize; + + const int bw = NRRIV2BW(sched_ctrl->active_ubwp ? + sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth : + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + + if (target_ul_bw>bw) + rbSize = bw; + else + rbSize = target_ul_bw; uint16_t *vrb_map_UL = &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[sched_slot * MAX_BWP_SIZE]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 47e5d4e6e4..0ff4136057 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -177,9 +177,9 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, int *cce_list; if( bwp==NULL || bwp->bwp_Id == 0 ) { - cce_list = nr_mac->cce_list[0][0]; + cce_list = nr_mac->cce_list[1][0]; } else { - cce_list = nr_mac->cce_list[bwp->bwp_Id-1][coreset_id]; + cce_list = nr_mac->cce_list[bwp->bwp_Id][coreset_id]; } int n_rb=0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index c881f19de6..323baa48a7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -65,7 +65,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = ubwp->bwp_Id; - if (nrmac->preferred_ul_tda[bwp_id-1]) + if (nrmac->preferred_ul_tda[bwp_id]) return; /* there is a mixed slot only when in TDD */ @@ -151,18 +151,18 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_ul_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id-1])); + nrmac->preferred_ul_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int slot = 0; slot < n; ++slot) { const int sched_slot = (slot + k2) % n; - nrmac->preferred_ul_tda[bwp_id-1][slot] = -1; + nrmac->preferred_ul_tda[bwp_id][slot] = -1; if (!tdd || sched_slot % nr_slots_period >= tdd->nrofDownlinkSlots + nr_mix_slots) - nrmac->preferred_ul_tda[bwp_id-1][slot] = 0; + nrmac->preferred_ul_tda[bwp_id][slot] = 0; else if (tdd && nr_mix_slots && sched_slot % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_ul_tda[bwp_id-1][slot] = tdaMi; - LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id-1][slot]); + nrmac->preferred_ul_tda[bwp_id][slot] = tdaMi; + LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id][slot]); } if (k2 < tdd->nrofUplinkSlots) @@ -799,7 +799,7 @@ bool allocate_ul_retransmission(module_id_t module_id, NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (tda == retInfo->time_domain_allocation) { /* Check the resource is enough for retransmission */ while (rbStart < bwpSize && !rballoc_mask[rbStart]) @@ -986,7 +986,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1076,7 +1076,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1126,7 +1126,7 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * schedule now (slot + k2 is not UL slot) */ int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (tda < 0) return false; int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu); @@ -1461,7 +1461,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) * create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); @@ -1470,7 +1470,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, sched_ctrl->active_bwp); - pdcch_pdu_bwp_coreset[bwpid-1][coresetid] = pdcch_pdu; + pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu; } LOG_D(MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 36c8ac7932..bd7304ad8f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -72,7 +72,7 @@ /* Defs */ -#define MAX_NUM_BWP 4 +#define MAX_NUM_BWP 5 #define MAX_NUM_CORESET 2 #define MAX_NUM_CCE 90 #define MAX_HARQ_ROUNDS 4 diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 6bbf4541ff..c21f586d0a 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -43,7 +43,7 @@ gNBs = # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_subcarrierSpacing = 1; - dl_carrierBandwidth = 106; + dl_carrierBandwidth = 106; //when changing BWPs, change this also to the same RBs #initialDownlinkBWP #genericParameters # this is RBstart=33 (Start RB of SSB block),L=24 (275*(L-1))+RBstart @@ -72,7 +72,7 @@ gNBs = # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_subcarrierSpacing = 1; - ul_carrierBandwidth = 106; + ul_carrierBandwidth = 106; //when changing BWPs, change this also to the same RBs pMax = 20; #initialUplinkBWP #genericParameters @@ -187,13 +187,13 @@ gNBs = bwp1_subcarrierSpacing = 1 ; # BWP 2 Configuration bwp-Id_2 = 2; - bwp2_locationAndBandwidth = 28908; // RBstart=33,L=106 + bwp2_locationAndBandwidth = 6325; // RBstart=0,L=24 ( 10 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp2_subcarrierSpacing = 1 ; # BWP 3 Configuration bwp-Id_3 = 3; - bwp3_locationAndBandwidth = 28908; // RBstart=33,L=106 + bwp3_locationAndBandwidth = 13750; // RBstart=0,L=51 ( 20 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp3_subcarrierSpacing = 1 ; From 91765a2c4d15220784affba1479be2b087ea454d Mon Sep 17 00:00:00 2001 From: Abhijith Date: Mon, 24 May 2021 13:41:27 +0530 Subject: [PATCH 012/233] Initial commit for addition of dedicated BWPs --- .vscode/launch.json | 140 +++++++++++++++--- cmake_targets/CMakeLists.txt | 2 +- cmake_targets/epc_test/CMakeLists.template | 2 +- openair1/SIMULATION/NR_PHY/dlsim.c | 6 +- openair2/GNB_APP/RRC_nr_paramsvalues.h | 16 +- openair2/GNB_APP/gnb_config.c | 28 +++- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 4 +- openair2/LAYER2/NR_MAC_UE/config_ue.c | 51 ++++--- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 2 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 12 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 9 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 43 +++++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 10 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 12 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 3 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 5 + openair2/RRC/NR/rrc_gNB_reconfig.c | 58 ++++++-- .../CONF/gnb.band78.tm1.106PRB.usrpn300.conf | 82 ++++++++++ 20 files changed, 404 insertions(+), 87 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 4b563febde..79964cec9d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,25 +4,125 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "enter program name, for example ${workspaceFolder}/a.exe", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/path/to/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } + + { + "name": "(gdb) Launch UE PHY Simu", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "args": ["--rfsimulator.serveraddr","127.0.0.1", + "--rfsim", + "--phy-test", + + "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch UE RA", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "args": ["--rfsimulator.serveraddr","127.0.0.1", + "--rfsim", + "--do-ra", + + "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch gNB PHY Simu", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "args": ["--rfsimulator.serveraddr","server","-O", + "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "--rfsim", + "--phy-test" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch gNB RA", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "args": ["--rfsimulator.serveraddr","server","-O", + "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "--rfsim", + "--do-ra" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) dlsim", + "type": "cppdbg", + "request": "launch", + "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/phy_simulators/build/nr_dlsim", + "args": ["-n", "4", "-m", "2" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } ] } diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 8d8e88455a..acc27404c0 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -396,7 +396,7 @@ add_definitions("-DPACKAGE_BUGREPORT=\"openair4g-devel@lists.eurecom.fr\"") # Debug related options ######################################### -add_boolean_option(ASN_DEBUG False "ASN1 coder/decoder Debug") +add_boolean_option(ASN_DEBUG True "ASN1 coder/decoder Debug") add_boolean_option(EMIT_ASN_DEBUG False "ASN1 coder/decoder Debug") add_boolean_option(MSG_PRINT False "print debug messages") add_boolean_option(DISABLE_XER_PRINT False "print XER Format") diff --git a/cmake_targets/epc_test/CMakeLists.template b/cmake_targets/epc_test/CMakeLists.template index 7a097fffb0..1efbd1340c 100755 --- a/cmake_targets/epc_test/CMakeLists.template +++ b/cmake_targets/epc_test/CMakeLists.template @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) set ( CMAKE_BUILD_TYPE "RelWithDebInfo" ) -set ( ASN_DEBUG False) +set ( ASN_DEBUG True) set ( DEBUG_OMG False ) set ( DISABLE_XER_PRINT False ) set ( DRIVER2013 True ) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 730e78c162..abf8faf225 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -353,7 +353,7 @@ int main(int argc, char **argv) NB_UE_INST = 1; //double pbch_sinr; //int pbch_tx_ant; - int N_RB_DL=106,mu=1; + int N_RB_DL=106,mu=2; //unsigned char frame_type = 0; @@ -794,6 +794,10 @@ int main(int argc, char **argv) fs = 61.44e6; bw = 40e6; } + else if (mu == 2 && N_RB_DL == 106) { + fs = 122.88e6; + bw = 80e6; + } else if (mu == 3 && N_RB_DL == 66) { fs = 122.88e6; bw = 100e6; diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index d05908e8ed..c4092c132d 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -228,6 +228,16 @@ #define GNB_CONFIG_STRING_ULPTRSREOFFSET_0 "ul_ptrsReOffset_0" #define GNB_CONFIG_STRING_ULPTRSMAXPORTS_0 "ul_ptrsMaxPorts_0" #define GNB_CONFIG_STRING_ULPTRSPOWER_0 "ul_ptrsPower_0" +#define GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID "firstActiveDownlinkBWP-Id" +#define GNB_CONFIG_STRING_BWP_ID_0 "bwp-Id_0" +#define GNB_CONFIG_STRING_BWP_ID_1 "bwp-Id_1" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_0 "locationAndBandwidth_0" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_1 "locationAndBandwidth_1" +#define GNB_CONFIG_STRING_SCS_0 "subcarrierSpacing_0" +#define GNB_CONFIG_STRING_SCS_1 "subcarrierSpacing_1" +#define GNB_CONFIG_STRING_DEFAULTDLBWP_ID "defaultDownlinkBWP-Id" +#define GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID "firstActiveUplinkBWP-Id" + /*--------------------------------------------------------------------------------------------------------------------*/ /* pdcch_ConfigSIB1 parameters */ @@ -432,7 +442,11 @@ {GNB_CONFIG_STRING_ULPTRSTIMEDENSITY2_0,NULL,0,i64ptr:scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[2],defint64val:-1,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSREOFFSET_0,NULL,0,i64ptr:scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset,defint64val:-1,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSMAXPORTS_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->maxNrofPorts,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_ULPTRSPOWER_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->ptrs_Power,defint64val:0,TYPE_INT64,0}} +{GNB_CONFIG_STRING_ULPTRSPOWER_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->ptrs_Power,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID,NULL,0,i64ptr:scd->firstActiveDownlinkBWP_Id,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID,NULL,0,i64ptr:scd->uplinkConfig->firstActiveUplinkBWP_Id,defint64val:0,TYPE_INT64,0}} //, \ +{GNB_CONFIG_STRING_BWP_ID_0,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[0].bwp_Id,defint64val:0,TYPE_INT64,0}}//, \ +{GNB_CONFIG_STRING_BWP_ID_1,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:0,TYPE_INT64,0}} diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index e578e98c5e..cbc710c0cc 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -326,16 +326,26 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } /* Function to allocate dedicated serving cell config strutures */ +//Abhi : error at this function : when adding for loop to add new elements of downlinkBWP-ToAddModList. void prepare_scd(NR_ServingCellConfig_t *scd) { // Allocate downlink structures - scd->downlinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->downlinkBWP_ToAddModList)); - + scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); + scd->bwp_InactivityTimer = CALLOC(1, sizeof(*scd->bwp_InactivityTimer)); + scd->uplinkConfig->firstActiveUplinkBWP_Id = CALLOC(1, sizeof(*scd->uplinkConfig->firstActiveUplinkBWP_Id)); + scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); + scd->firstActiveDownlinkBWP_Id = CALLOC(1, sizeof(*scd->firstActiveDownlinkBWP_Id)); +for (int j = 0 ;j < 4 ; j++) +{ + // Downlink bandwidth part NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); bwp->bwp_Id = 1; // Allocate downlink dedicated bandwidth part and PDSCH structures + bwp->bwp_Common = calloc(1, sizeof(*bwp->bwp_Common)); + bwp->bwp_Common->pdcch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); + bwp->bwp_Common->pdsch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Dedicated = calloc(1, sizeof(*bwp->bwp_Dedicated)); bwp->bwp_Dedicated->pdsch_Config = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config)); bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; @@ -353,6 +363,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { NR_PTRS_DownlinkCfg->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->frequencyDensity)); long *dl_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { + dl_rbs[i]=1; ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->frequencyDensity->list, &dl_rbs[i]); } NR_PTRS_DownlinkCfg->timeDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->timeDensity)); @@ -367,9 +378,6 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { // Allocate uplink structures - scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); - scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); - NR_PUSCH_Config_t *pusch_Config = CALLOC(1, sizeof(*pusch_Config)); // Allocate UL DMRS and PTRS structures @@ -385,6 +393,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity)); long *n_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { + n_rbs[i] = 1; ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity->list, &n_rbs[i]); } NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity)); @@ -398,6 +407,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { // UL bandwidth part NR_BWP_Uplink_t *ubwp = CALLOC(1, sizeof(*ubwp)); ubwp->bwp_Id = 1; + ubwp->bwp_Common = CALLOC(1, sizeof(*ubwp->bwp_Common)); ubwp->bwp_Dedicated = CALLOC(1, sizeof(*ubwp->bwp_Dedicated)); ubwp->bwp_Dedicated->pusch_Config = CALLOC(1, sizeof(*ubwp->bwp_Dedicated->pusch_Config)); @@ -406,6 +416,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { ASN_SEQUENCE_ADD(&scd->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } +} /* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { @@ -825,6 +836,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { memset((void*)scd,0,sizeof(NR_ServingCellConfig_t)); prepare_scd(scd); paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); + printf("RRC BWP 0: %d", scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Id); + printf("RRC BWP 1: %d", scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id); paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; ////////// Physical parameters @@ -906,13 +919,14 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { if (SCDsParamList.numelt > 0) { sprintf(aprefix, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, 0); config_get( SCDsParams,sizeof(SCDsParams)/sizeof(paramdef_t),aprefix); - LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d \n", + LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d, BWP_ID %d \n", (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[2], - (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); + (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset, + (int)*scd->firstActiveDownlinkBWP_Id); } fix_scd(scd); diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index ef358f40fe..4a03f81d98 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2880,7 +2880,9 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, dci_pdu->antenna_ports.nbits = getAntPortBitWidth(typeA,typeB); size += dci_pdu->antenna_ports.nbits; // Tx Config Indication - long *isTciEnable = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]->tci_PresentInDCI; + //Abhi : Check this -- bwp_id to replaced with corset_id ? each BWP can have multiple CORSETs .. this has to be corset_id + int corset_id = 1 ; + long *isTciEnable = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI; if (isTciEnable != NULL) { dci_pdu->transmission_configuration_indication.nbits = 3; size += dci_pdu->transmission_configuration_indication.nbits; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 79e6eca300..45d8cf533c 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -333,20 +333,21 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; - if (bwp_ind && dci_format){ +//Abhi : TODO : uncomment this when DCI based swithching is available + // if (bwp_ind && dci_format){ - switch(*dci_format){ - case NR_UL_DCI_FORMAT_0_1: - mac->UL_BWP_Id = *bwp_ind; - break; - case NR_DL_DCI_FORMAT_1_1: - mac->DL_BWP_Id = *bwp_ind; - break; - default: - LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); - } + // switch(*dci_format){ + // case NR_UL_DCI_FORMAT_0_1: + // mac->UL_BWP_Id = *bwp_ind; + // break; + // case NR_DL_DCI_FORMAT_1_1: + // mac->DL_BWP_Id = *bwp_ind; + // break; + // default: + // LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); + // } - } else { + // } else { if (scd->firstActiveDownlinkBWP_Id) mac->DL_BWP_Id = *scd->firstActiveDownlinkBWP_Id; @@ -360,9 +361,9 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format else mac->UL_BWP_Id = 1; - } + // } - LOG_D(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); + LOG_I(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); } @@ -373,15 +374,18 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format */ void config_control_ue(NR_UE_MAC_INST_t *mac){ + //Abhi : Hardcoded CORSET ID here uint8_t coreset_id = 1, ss_id; NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; AssertFatal(scd->downlinkBWP_ToAddModList != NULL, "downlinkBWP_ToAddModList is null\n"); - AssertFatal(scd->downlinkBWP_ToAddModList->list.count == 1, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); + //AssertFatal(scd->downlinkBWP_ToAddModList->list.count == 1, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); config_bwp_ue(mac, NULL, NULL); NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; AssertFatal(dl_bwp_id != 0, "DL_BWP_Id is 0!"); + AssertFatal(ul_bwp_id != 0, "UL_BWP_Id is 0!"); NR_BWP_DownlinkCommon_t *bwp_Common = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Common; AssertFatal(bwp_Common != NULL, "bwp_Common is null\n"); @@ -389,6 +393,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ NR_BWP_DownlinkDedicated_t *dl_bwp_Dedicated = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Dedicated; AssertFatal(dl_bwp_Dedicated != NULL, "dl_bwp_Dedicated is null\n"); + //Abhi : TODO : add assert statements for UL BWP! NR_SetupRelease_PDCCH_Config_t *pdcch_Config = dl_bwp_Dedicated->pdcch_Config; AssertFatal(pdcch_Config != NULL, "pdcch_Config is null\n"); @@ -412,14 +417,20 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ struct NR_UplinkConfig__uplinkBWP_ToAddModList *uplinkBWP_ToAddModList = scd->uplinkConfig->uplinkBWP_ToAddModList; AssertFatal(uplinkBWP_ToAddModList != NULL, "uplinkBWP_ToAddModList is null\n"); - AssertFatal(uplinkBWP_ToAddModList->list.count == 1, "uplinkBWP_ToAddModList->list->count is %d\n", uplinkBWP_ToAddModList->list.count); + // AssertFatal(uplinkBWP_ToAddModList->list.count == 1, "uplinkBWP_ToAddModList->list->count is %d\n", uplinkBWP_ToAddModList->list.count); // check pdcch_Config, pdcch_ConfigCommon and DL BWP - mac->DLbwp[0] = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]; + // Check dedicated UL BWP and pass to MAC + for (int num_BWP = 0; num_BWP DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP]; + mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP]; + } + mac->coreset[dl_bwp_id - 1][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; - // Check dedicated UL BWP and pass to MAC - mac->ULbwp[0] = uplinkBWP_ToAddModList->list.array[0]; + + AssertFatal(mac->ULbwp[0]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); // check available Search Spaces in the searchSpacesToAddModList and pass to MAC @@ -443,7 +454,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ AssertFatal(css->searchSpaceType != NULL, "css->searchSpaceType is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot != NULL, "css->monitoringSymbolsWithinSlot is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot->buf != NULL, "css->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[0][0][ss_id] = css; + mac->SSpace[dl_bwp_id -1][coreset_id -1][ss_id] = css; ss_id++; } diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 332cd600d3..8d7b26bed9 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -74,7 +74,7 @@ // ========== #define NB_NR_UE_MAC_INST 1 -#define MAX_NUM_BWP 2 +#define MAX_NUM_BWP 4 #define NUM_SLOT_FRAME 10 /*!\brief value for indicating BSR Timer is not running */ diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index f879599f7d..8875695eaf 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -224,7 +224,9 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - uint8_t bwp_id = 1, coreset_id = 1; + //Abhi : harcoded CORSET ID here + uint8_t bwp_id = mac->DL_BWP_Id; + uint8_t coreset_id = 1; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; NR_BWP_Downlink_t *bwp = mac->DLbwp[bwp_id - 1]; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index adbf7a5774..14bdc962c3 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -434,6 +434,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr AssertFatal(mac->DLbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); AssertFatal(mac->ULbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); + //Abhi : TODO const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); LOG_D(MAC, "In %s: Processing received DCI format %s (DL BWP %d)\n", __FUNCTION__, dci_formats[dci_format], n_RB_DLBWP); @@ -786,10 +787,11 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr * 47 DMRS_SEQ_INI: */ - if (dci->bwp_indicator.val != 1) { - LOG_W(MAC, "[%d.%d] bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n", frame, slot); - return -1; - } + //Abhi : temporarily commenting this out + // if (dci->bwp_indicator.val != 1) { + // LOG_W(MAC, "[%d.%d] bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n", frame, slot); + // return -1; + // } config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; @@ -1787,7 +1789,7 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, return; } - LOG_D(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); + LOG_I(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); while (!done && pdu_len > 0){ mac_ce_len = 0x0000; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 4932119a83..2950c6b9d4 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -621,10 +621,11 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } else if (*dci_format == NR_UL_DCI_FORMAT_0_1) { /* BANDWIDTH_PART_IND */ - if (dci->bwp_indicator.val != 1) { - LOG_W(MAC, "bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n"); - return -1; - } + //Abhi : why must BWP always be 1 ? + // if (dci->bwp_indicator.val != 1) { + // LOG_W(MAC, "bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n"); + // return -1; + // } config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format); target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 36f687b5cd..ee54386c5f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -325,11 +325,52 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - const int bwp_id = 1; + int nb_periods_per_frame; gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + + // NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; + // const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; + + // switch(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity) { + // case 0: + // nb_periods_per_frame = 20; // 10ms/0p5ms + // break; + + // case 1: + // nb_periods_per_frame = 16; // 10ms/0p625ms + // break; + + // case 2: + // nb_periods_per_frame = 10; // 10ms/1ms + // break; + + // case 3: + // nb_periods_per_frame = 8; // 10ms/1p25ms + // break; + + // case 4: + // nb_periods_per_frame = 5; // 10ms/2ms + // break; + + // case 5: + // nb_periods_per_frame = 4; // 10ms/2p5ms + // break; + + // case 6: + // nb_periods_per_frame = 2; // 10ms/5ms + // break; + + // case 7: + // nb_periods_per_frame = 1; // 10ms/10ms + // break; + + // default: + // AssertFatal(1==0,"Undefined tdd period %ld\n", scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); + + } if (slot==0 && (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]>=257)) { const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 7c1cbea9b6..e16b43f84f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -790,8 +790,8 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->crnti); - AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); + // AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, + // "downlinkBWP_ToAddModList has %d BWP!\n", ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); NR_BWP_Uplink_t *ubwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; LOG_D(NR_MAC, "Frame %d, Subframe %d Adding Msg3 UL Config Request for (%d,%d) : (%d,%d,%d) for rnti: %d\n", frameP, @@ -986,9 +986,9 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->RA_rnti); - AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", - ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); + // AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, + // "downlinkBWP_ToAddModList has %d BWP!\n", + // ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); // SCF222: PDU index incremented for each PDSCH PDU sent in TX control message. This is used to associate control // information to data and is reset every slot. diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index cb1a73edae..ba32c9d361 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -819,8 +819,8 @@ void nr_schedule_ue_spec(module_id_t module_id, harq->is_waiting = true; UE_info->mac_stats[UE_id].dlsch_rounds[harq->round]++; - LOG_D(MAC, - "%4d.%2d RNTI %04x start %3d RBs %3d startSymbol %2d nb_symbol %2d MCS %2d TBS %4d HARQ PID %2d round %d NDI %d\n", + LOG_I(MAC, + "%4d.%2d RNTI %04x start %d RBs %d startSymbol %d nb_symbsol %d MCS %d TBS %d HARQ PID %d round %d NDI %d\n", frame, slot, rnti, @@ -952,11 +952,13 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_pdu_rel15_t dci_payload; memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t)); // bwp indicator + const int n_dl_bwp = UE_info->secondaryCellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; - AssertFatal(n_dl_bwp == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", - n_dl_bwp); + // AssertFatal(n_dl_bwp == 1, + // "downlinkBWP_ToAddModList has %d BWP!\n", + // n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 + // Abhi :look into this dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, "Only frequency resource allocation type 1 is currently supported\n"); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index b3d2ecb0b2..160232cfd8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -973,7 +973,8 @@ void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, if (bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList != NULL) AssertFatal(1==0,"Aperiodic ZP CSI-RS currently not supported\n"); // transmission configuration indication - if (bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]->tci_PresentInDCI != NULL) + int corset_id = 1; //TODO : corset_id to come from higher layers + if (bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI != NULL) AssertFatal(1==0,"TCI in DCI currently not supported\n"); //srs resource set if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching!=NULL) { diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index c1ec27580f..36c8ac7932 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -72,7 +72,7 @@ /* Defs */ -#define MAX_NUM_BWP 2 +#define MAX_NUM_BWP 4 #define MAX_NUM_CORESET 2 #define MAX_NUM_CCE 90 #define MAX_HARQ_ROUNDS 4 diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index a789f0c34e..14ad8b7a9d 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -105,6 +105,11 @@ void handle_nr_uci(NR_UL_IND_t *UL_info) } UL_info->uci_ind.num_ucis = 0; + + // mark corresponding PUCCH resources as free + // NOTE: we just assume it is BWP ID 1, to be revised for multiple BWPs + //Abhi : TODO + RC.nrmac[mod_id]->pucch_index_used[1][slot] = 0; } diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 8ed19e9d82..0d153ff5cb 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -280,6 +280,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->choice.setup->ext1 = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->choice.setup->ext1)); secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->choice.setup->ext1->t311 = NR_RLF_TimersAndConstants__ext1__t311_ms30000; secondaryCellGroup->spCellConfig->rlmInSyncOutOfSyncThreshold = NULL; + if (servingcellconfigdedicated) { secondaryCellGroup->spCellConfig->spCellConfigDedicated = servingcellconfigdedicated; } else { @@ -460,23 +461,31 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; + //Abhi : Fix me + // Code has to loop thorough multiple BWP wherever necessary NR_BWP_Downlink_t *bwp = NULL; + for (int bwp_loop = 0; bwp_loop < 4 ; bwp_loop++ ) + { + if (servingcellconfigdedicated) { - bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[0]; + bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; } else { secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); bwp=calloc(1,sizeof(*bwp)); } - bwp->bwp_Id=1; + bwp->bwp_Id = bwp_loop +1; //Abhi: remove this ? BWP is added to asn only when scd is not present + +#pragma region bwp-downlink-Common bwp->bwp_Common=calloc(1,sizeof(*bwp->bwp_Common)); // copy common BWP size from initial BWP except for bandwdith memcpy((void*)&bwp->bwp_Common->genericParameters, &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, sizeof(bwp->bwp_Common->genericParameters)); bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; //Abhi : hardcoded + #pragma region bwp_common->pdcch_ConfigCommon bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); @@ -485,6 +494,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); +#pragma region corset NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); coreset->controlResourceSetId=1; // frequency domain resources depends on BWP size @@ -522,11 +532,13 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco coreset->pdcch_DMRS_ScramblingID = NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; +#pragma endregion coreset bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); +#pragma region CommonSearchSpace NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); ss->searchSpaceId = 1; ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); @@ -562,12 +574,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; +#pragma endregion pdcch_ConfigCommon bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); +#pragma endregion bwp_Common->pdcch_ConfigCommon // copy PDSCH TimeDomainResourceAllocation from InitialBWP NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; @@ -581,10 +595,15 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pdschi->startSymbolAndLength = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); } +#pragma endregion bwp_common + if (!servingcellconfigdedicated) { bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); } + + #pragma region bwp-downlink-Dedicated + bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); @@ -754,9 +773,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + #pragma endregion bwp-downlink-Dedicated + + //Abhi : FIX ME! + //secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; //Abhi: this has to be removed secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); @@ -766,6 +788,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); } +#pragma region bwp-uplink NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; initialUplinkBWP->pucch_Config = NULL; @@ -773,10 +796,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; NR_PUSCH_Config_t *pusch_Config = NULL; if (servingcellconfigdedicated) { - pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup; + pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Dedicated->pusch_Config->choice.setup; } else { pusch_Config = calloc(1,sizeof(*pusch_Config)); } + + #pragma region pusch-config-ubwp-dedicated initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; @@ -870,12 +895,15 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco } /*----------------------------------------------------------------------------------------------------------------------------*/ + #pragma endregion PUSCH-Config-ubwp-dedicated initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); initialUplinkBWP->srs_Config->choice.setup=srs_Config; srs_Config->srs_ResourceSetToReleaseList=NULL; + + #pragma region srs_Config-ubwp-dedicated srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); srs_resset0->srs_ResourceSetId = 0; @@ -927,15 +955,17 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); +#pragma endregion srs_config + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; NR_BWP_Uplink_t *ubwp = NULL; if (servingcellconfigdedicated) { - ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]; + ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; } else { secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); ubwp = calloc(1,sizeof(*ubwp)); } - ubwp->bwp_Id=1; + ubwp->bwp_Id=bwp_loop+1; ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); // copy bwp_Common from Initial UL BWP except for bandwidth memcpy((void*)&ubwp->bwp_Common->genericParameters, @@ -950,6 +980,9 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); } + + #pragma region bwp-uplink-Dedicated + ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); @@ -1077,18 +1110,21 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; +#pragma endregion bwp-uplink-Dedicated if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } - - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; +#pragma endregion bwp-uplink-Config + } + //secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig)); NR_PUSCH_ServingCellConfig_t *pusch_scc = calloc(1,sizeof(*pusch_scc)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->present = NR_SetupRelease_PUSCH_ServingCellConfig_PR_setup; secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup = pusch_scc; + pusch_scc->codeBlockGroupTransmission = NULL; pusch_scc->rateMatching = NULL; pusch_scc->xOverhead = NULL; diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf index b57cb00779..51a78bed40 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf @@ -175,6 +175,88 @@ gNBs = ); +# Dedicated Serving Cell Configuration + servingCellConfigDedicated = ( + { + # BWP-Downlink + # DL PTRS related parameters. + # 3GPP TS 38.331 PTRS-DownlinkConfig + + # BWP 1 Configuration + + bwp-Id_0 = 1; + locationAndBandwidth_0 = 1234; //TODO + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing_0 = 1 ; + + # BWP 2 Configuration + + bwp-Id_1 = 1; + locationAndBandwidth_1 = 1234; //TODO + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing_1 = 1 ; + + + # frequencyDensity value range: 1-276 + dl_ptrsFreqDensity0_0 = 25; + dl_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + dl_ptrsTimeDensity0_0 = 2; + dl_ptrsTimeDensity1_0 = 4; + dl_ptrsTimeDensity2_0 = 10; + + # epre_Ratio + # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 + dl_ptrsEpreRatio_0 = 0; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 + dl_ptrsReOffset_0 = 0; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + defaultDownlinkBWP-Id = 1; #BWP-Id + + # UplinkConfig + firstActiveUplinkBWP-Id = 1 ; #BWP-Id + + #BWP-Uplink + # UL PTRS related parameters. + # 3GPP TS 38.331 PTRS-UplinkConfig + + # frequencyDensity value range: 1-276 + ul_ptrsFreqDensity0_0 = 25; + ul_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + ul_ptrsTimeDensity0_0 = 2; + ul_ptrsTimeDensity1_0 = 4; + ul_ptrsTimeDensity2_0 = 10; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 + ul_ptrsReOffset_0 = 0; + + # maxNrofPorts + # 0 = n1, 1 = n2 + ul_ptrsMaxPorts_0 = 0; + + # ptrs-Power + # 0 = p00, 1 = p01, 2 = p10, 3 = p11 + #ul_ptrsPower_0 = 0; + + } + ); # ------- SCTP definitions SCTP : From bca775a59498d1f10f3061fa097b5fcb6463a5d7 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Mon, 24 May 2021 14:18:26 +0530 Subject: [PATCH 013/233] Fixed compilation issues --- cmake_targets/CMakeLists.txt | 2 +- cmake_targets/epc_test/CMakeLists.template | 2 +- openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 5 +++-- openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 4 ---- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index acc27404c0..8d8e88455a 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -396,7 +396,7 @@ add_definitions("-DPACKAGE_BUGREPORT=\"openair4g-devel@lists.eurecom.fr\"") # Debug related options ######################################### -add_boolean_option(ASN_DEBUG True "ASN1 coder/decoder Debug") +add_boolean_option(ASN_DEBUG False "ASN1 coder/decoder Debug") add_boolean_option(EMIT_ASN_DEBUG False "ASN1 coder/decoder Debug") add_boolean_option(MSG_PRINT False "print debug messages") add_boolean_option(DISABLE_XER_PRINT False "print XER Format") diff --git a/cmake_targets/epc_test/CMakeLists.template b/cmake_targets/epc_test/CMakeLists.template index 1efbd1340c..7a097fffb0 100755 --- a/cmake_targets/epc_test/CMakeLists.template +++ b/cmake_targets/epc_test/CMakeLists.template @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) set ( CMAKE_BUILD_TYPE "RelWithDebInfo" ) -set ( ASN_DEBUG True) +set ( ASN_DEBUG False) set ( DEBUG_OMG False ) set ( DISABLE_XER_PRINT False ) set ( DRIVER2013 True ) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index abf8faf225..aed4cb68ed 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -353,7 +353,7 @@ int main(int argc, char **argv) NB_UE_INST = 1; //double pbch_sinr; //int pbch_tx_ant; - int N_RB_DL=106,mu=2; + int N_RB_DL=106,mu=1; //unsigned char frame_type = 0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index ee54386c5f..a0c172a700 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -330,9 +330,10 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; + // NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; - // const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; // switch(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity) { // case 0: @@ -370,7 +371,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, // default: // AssertFatal(1==0,"Undefined tdd period %ld\n", scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); - } + // } if (slot==0 && (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]>=257)) { const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index 14ad8b7a9d..7bf738e043 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -106,10 +106,6 @@ void handle_nr_uci(NR_UL_IND_t *UL_info) UL_info->uci_ind.num_ucis = 0; - // mark corresponding PUCCH resources as free - // NOTE: we just assume it is BWP ID 1, to be revised for multiple BWPs - //Abhi : TODO - RC.nrmac[mod_id]->pucch_index_used[1][slot] = 0; } From 1fe40f312eb2084cd39da5d69d0d2f2a7a7413fa Mon Sep 17 00:00:00 2001 From: Abhijith Date: Tue, 25 May 2021 20:16:31 +0530 Subject: [PATCH 014/233] bugfix : pdcch and BWP switch capability --- .vscode/launch.json | 38 +++------- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 8 +-- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 74 +++++++++++--------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 30 ++++---- 5 files changed, 71 insertions(+), 81 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 79964cec9d..01a7af51a0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,12 +9,12 @@ "name": "(gdb) Launch UE PHY Simu", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", "args": ["--rfsimulator.serveraddr","127.0.0.1", "--rfsim", "--phy-test", - "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -34,12 +34,12 @@ "name": "(gdb) Launch UE RA", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", "args": ["--rfsimulator.serveraddr","127.0.0.1", "--rfsim", "--do-ra", - "--rrc_config_path" ,"/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build" + "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -59,9 +59,9 @@ "name": "(gdb) Launch gNB PHY Simu", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", "--rfsim", "--phy-test" ], @@ -83,9 +83,9 @@ "name": "(gdb) Launch gNB RA", "type": "cppdbg", "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", + "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", + "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", "--rfsim", "--do-ra" ], @@ -102,27 +102,7 @@ "ignoreFailures": true } ] - }, - { - "name": "(gdb) dlsim", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/openairinterface5g/cmake_targets/phy_simulators/build/nr_dlsim", - "args": ["-n", "4", "-m", "2" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] } + ] } diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index f6f331ec21..07c03f4238 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -937,10 +937,10 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int N_m_c_rx = 0; int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + if (mac->DLbwp[dl_bwp_id-1] == NULL) return 0; - if (mac->DLbwp[0] == NULL) return 0; - - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { two_transport_blocks = TRUE; number_of_code_word = 2; } @@ -1018,7 +1018,7 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t N_m_c_rx = number_harq_feedback; int N_SPS_c = 0; /* FFS TODO_NR multicells and SPS are not supported at the moment */ if (mac->scg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH == NULL) { - int N_TB_max_DL = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; + int N_TB_max_DL = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; *n_HARQ_ACK = (((V_DAI_m_DL - U_DAI_c)%4) * N_TB_max_DL) + N_m_c_rx + N_SPS_c; NR_TST_PHY_PRINTF("PUCCH power n(%d) = ( V(%d) - U(%d) )mod4 * N_TB(%d) + N(%d) \n", *n_HARQ_ACK, V_DAI_m_DL, U_DAI_c, N_TB_max_DL, N_m_c_rx); } diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 45d8cf533c..85e7dc7afc 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -442,7 +442,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ AssertFatal(*ss->controlResourceSetId == mac->coreset[dl_bwp_id - 1][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); AssertFatal(ss->monitoringSymbolsWithinSlot != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot->buf != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[0][0][ss_id] = ss; + mac->SSpace[dl_bwp_id-1][0][ss_id] = ss; } // Check available CSSs in the commonSearchSpaceList (list of additional common search spaces) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 14bdc962c3..83fb5924aa 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -272,6 +272,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t sliv_S=0; uint8_t sliv_L=0; uint8_t mu_pusch = 1; + NR_BWP_Id_t dl_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // definition table j Table 6.1.2.1.1-4 uint8_t j = (mu_pusch==3)?3:(mu_pusch==2)?2:1; @@ -317,10 +319,10 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(dlsch_config_pdu != NULL){ NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; if (pdsch_TimeDomainAllocationList && use_default==false) { if (time_domain_ind >= pdsch_TimeDomainAllocationList->list.count) { @@ -370,11 +372,11 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(pusch_config_pdu != NULL){ NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; + if (mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } if (pusch_TimeDomainAllocationList && use_default==false) { @@ -429,13 +431,15 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; //const uint16_t n_RB_DLBWP = dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP; //make sure this has been set AssertFatal(mac->DLbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); AssertFatal(mac->ULbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); //Abhi : TODO - const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); LOG_D(MAC, "In %s: Processing received DCI format %s (DL BWP %d)\n", __FUNCTION__, dci_formats[dci_format], n_RB_DLBWP); @@ -618,14 +622,14 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr if (get_softmodem_params()->sa) { dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } else { // NSA mode is not using the Initial BWP - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdsch_config = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup; + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; } } else { - dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[0]->bwp_Common->genericParameters.subcarrierSpacing; - pdsch_config = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup; + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; + pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; } BWPSize = n_RB_DLBWP; } @@ -643,10 +647,10 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; int mappingtype = pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; @@ -709,9 +713,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - pucch_res_set_cnt = mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -793,7 +797,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr // return -1; // } config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH; @@ -801,9 +805,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_1 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[0]->bwp_Common->genericParameters.subcarrierSpacing; + dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; /* IDENTIFIER_DCI_FORMATS */ /* CARRIER_IND */ @@ -821,10 +825,10 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; int mappingtype = pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; @@ -883,9 +887,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_1->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - pucch_res_set_cnt = mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[0]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -897,7 +901,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr /* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */ // according to TS 38.213 Table 9.2.3-1 - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + dlsch_config_pdu_1_1->pdsch_to_harq_feedback_time_ind = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; @@ -1103,6 +1107,7 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ int N_RB = 0, start_RB; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; switch(rnti_type) { case NR_RNTI_RA: case NR_RNTI_TC: @@ -1121,7 +1126,7 @@ int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ N_RB = mac->type0_PDCCH_CSS_config.num_rbs; break; case NR_RNTI_C: - N_RB = NRRIV2BW(mac->DLbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + N_RB = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); break; } return N_RB; @@ -1140,8 +1145,9 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, AssertFatal(mac->DLbwp[0] != NULL, "DLbwp[0] shouldn't be null here!\n"); AssertFatal(mac->ULbwp[0] != NULL, "ULbwp[0] shouldn't be null here!\n"); int N_RB = get_n_rb(mac, rnti_type); + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; int N_RB_UL = (mac->scg != NULL) ? - NRRIV2BW(mac->ULbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : + NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); int pos=0; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 2950c6b9d4..d23c489bc6 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -94,14 +94,15 @@ void fill_scheduled_response(nr_scheduled_response_t *scheduled_response, */ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { long k2 = -1; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Get K2 from RRC configuration - NR_PUSCH_Config_t *pusch_config=mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; if (pusch_config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } if (pusch_TimeDomainAllocationList) { if (time_domain_ind >= pusch_TimeDomainAllocationList->list.count) { @@ -126,6 +127,7 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { */ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) { + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; //Check if request to access ul_config is for a UL slot if (is_nr_UL_slot(mac->scc, slot, mac->frame_type) == 0) { LOG_W(MAC, "Slot %d is not a UL slot. %s called for wrong slot!!!\n", slot, __FUNCTION__); @@ -135,7 +137,7 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl // Calculate the index of the UL slot in mac->ul_config_request list. This is // based on the TDD pattern (slot configuration period) and number of UL+mixed // slots in the period. TS 38.213 Sec 11.1 - int mu = mac->ULbwp[0]->bwp_Common->genericParameters.subcarrierSpacing; + int mu = mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; NR_TDD_UL_DL_Pattern_t *tdd_pattern = &mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1; const int num_slots_per_tdd = nr_slots_per_frame[mu] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity); const int num_slots_ul = tdd_pattern->nrofUplinkSlots + (tdd_pattern->nrofUplinkSymbols!=0); @@ -152,7 +154,8 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) { NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -290,9 +293,9 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf /* ANTENNA_PORTS */ uint8_t rank = 0; // We need to initialize rank FIXME!!! - + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -498,6 +501,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t *scc = mac->scc; int rnti_type = get_rnti_type(mac, rnti); + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Common configuration pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; @@ -512,7 +516,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, // Note: for Msg3 or MsgA PUSCH transmission the N_PRB_oh is always set to 0 - NR_BWP_Uplink_t *ubwp = mac->ULbwp[0]; + NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id-1]; NR_BWP_UplinkDedicated_t *ibwp = mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP; NR_PUSCH_Config_t *pusch_Config = ibwp->pusch_Config->choice.setup; int startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->startSymbolAndLength; @@ -599,8 +603,8 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int target_ss; bool valid_ptrs_setup = 0; - uint16_t n_RB_ULBWP = NRRIV2BW(mac->ULbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup; + uint16_t n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; // Basic sanity check for MCS value to check for a false or erroneous DCI if (dci->mcs > 28) { @@ -642,8 +646,8 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (pusch_Config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_Config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } int mappingtype = pusch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; @@ -1021,7 +1025,7 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, uint8_t tda_id){ int delta = 0; - NR_BWP_Uplink_t *ubwp = mac->ULbwp[0]; + NR_BWP_Uplink_t *ubwp = mac->ULbwp[mac->UL_BWP_Id-1]; // Get the numerology to calculate the Tx frame and slot int mu = ubwp->bwp_Common->genericParameters.subcarrierSpacing; struct NR_PUSCH_TimeDomainResourceAllocationList *pusch_TimeDomainAllocationList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; From af6807d30b547e36100f2e6dc083f7a816c63d25 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Wed, 26 May 2021 21:20:02 +0530 Subject: [PATCH 015/233] pucch errors fixed --- .vscode/launch.json | 108 ----------------- openair2/GNB_APP/gnb_config.c | 113 +++++++++--------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 4 +- .../CONF/gnb.band78.tm1.106PRB.usrpn300.conf | 39 +----- 4 files changed, 64 insertions(+), 200 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 01a7af51a0..0000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - - { - "name": "(gdb) Launch UE PHY Simu", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", - "args": ["--rfsimulator.serveraddr","127.0.0.1", - "--rfsim", - "--phy-test", - - "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, - { - "name": "(gdb) Launch UE RA", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem", - "args": ["--rfsimulator.serveraddr","127.0.0.1", - "--rfsim", - "--do-ra", - - "--rrc_config_path" ,"/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, - { - "name": "(gdb) Launch gNB PHY Simu", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", - "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", - "--rfsim", - "--phy-test" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - }, - { - "name": "(gdb) Launch gNB RA", - "type": "cppdbg", - "request": "launch", - "program": "/home/spclab/SCS60/rem/openairinterface5g/cmake_targets/ran_build/build/nr-softmodem", - "args": ["--rfsimulator.serveraddr","server","-O", - "/home/spclab/SCS60/rem/openairinterface5g/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf", - "--rfsim", - "--do-ra" - ], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } - - ] -} diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index cbc710c0cc..c0cac495cd 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -421,76 +421,79 @@ for (int j = 0 ;j < 4 ; j++) /* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { // Check for DL PTRS parameters validity - if (scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { - // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.count-1; i >= 0; i--) { - if ((*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] < 1) - || (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] > 276)) { - LOG_I(RRC, "DL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; - break; + for (int bwp_i = 0 ; bwp_i< MAX_NUM_BWP; bwp_i++) + { + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.count-1; i >= 0; i--) { + if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] < 1) + || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] > 276)) { + LOG_I(RRC, "DL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { - // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.count-1; i >= 0; i--) { - if ((*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] < 0) - || (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] > 29)) { - LOG_I(RRC, "DL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; - break; + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.count-1; i >= 0; i--) { + if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] < 0) + || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] > 29)) { + LOG_I(RRC, "DL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { - if (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset > 2) { - LOG_I(RRC, "Freeing DL PTRS resourceElementOffset \n"); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset = NULL; + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + if (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset > 2) { + LOG_I(RRC, "Freeing DL PTRS resourceElementOffset \n"); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset = NULL; + } + if (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio > 1) { + LOG_I(RRC, "Freeing DL PTRS epre_Ratio \n"); + free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio); + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio = NULL; + } } - if (*scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio > 1) { - LOG_I(RRC, "Freeing DL PTRS epre_Ratio \n"); - free(scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio); - scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio = NULL; - } - } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { - // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.count-1; i >= 0; i--) { - if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) - || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] > 276)) { - LOG_I(RRC, "UL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; - break; + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.count-1; i >= 0; i--) { + if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) + || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] > 276)) { + LOG_I(RRC, "UL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { - // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.count-1; i >= 0; i--) { - if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] < 0) - || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] > 29)) { - LOG_I(RRC, "UL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; - break; + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present + for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.count-1; i >= 0; i--) { + if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] < 0) + || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] > 29)) { + LOG_I(RRC, "UL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); + free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; + break; + } } } - } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { // Check for UL PTRS parameters validity - if (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { + if (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { LOG_I(RRC, "Freeing UL PTRS resourceElementOffset \n"); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; + free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; } } + } } void RCconfig_nr_flexran() diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index d23c489bc6..689f71cf88 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -748,11 +748,11 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } /* PTRS */ - if (mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS != NULL) { + if (mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS != NULL) { if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; - valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, + valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[mac->UL_BWP_Id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, pusch_config_pdu->rb_size, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table, &pusch_config_pdu->pusch_ptrs.ptrs_freq_density,&pusch_config_pdu->pusch_ptrs.ptrs_time_density, &pusch_config_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset,&pusch_config_pdu->pusch_ptrs.num_ptrs_ports, diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf index 51a78bed40..d3f886332e 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf @@ -199,25 +199,9 @@ gNBs = subcarrierSpacing_1 = 1 ; - # frequencyDensity value range: 1-276 - dl_ptrsFreqDensity0_0 = 25; - dl_ptrsFreqDensity1_0 = 75; + - # timeDensity value range: 0-29 - dl_ptrsTimeDensity0_0 = 2; - dl_ptrsTimeDensity1_0 = 4; - dl_ptrsTimeDensity2_0 = 10; - - # epre_Ratio - # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 - dl_ptrsEpreRatio_0 = 0; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 - dl_ptrsReOffset_0 = 0; - - firstActiveDownlinkBWP-Id = 1; #BWP-Id + firstActiveDownlinkBWP-Id = 2; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -227,29 +211,14 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 1 ; #BWP-Id + firstActiveUplinkBWP-Id = 2 ; #BWP-Id #BWP-Uplink # UL PTRS related parameters. # 3GPP TS 38.331 PTRS-UplinkConfig # frequencyDensity value range: 1-276 - ul_ptrsFreqDensity0_0 = 25; - ul_ptrsFreqDensity1_0 = 75; - - # timeDensity value range: 0-29 - ul_ptrsTimeDensity0_0 = 2; - ul_ptrsTimeDensity1_0 = 4; - ul_ptrsTimeDensity2_0 = 10; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 - ul_ptrsReOffset_0 = 0; - - # maxNrofPorts - # 0 = n1, 1 = n2 - ul_ptrsMaxPorts_0 = 0; + # ptrs-Power # 0 = p00, 1 = p01, 2 = p10, 3 = p11 From 4f9aaa927ecee2947a2f5f1a374eba10f72bf317 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Thu, 27 May 2021 18:10:46 +0530 Subject: [PATCH 016/233] Access SCD parameters from config file --- openair2/GNB_APP/RRC_nr_paramsvalues.h | 37 +- openair2/GNB_APP/gnb_config.c | 32 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 1 - openair2/LAYER2/NR_MAC_UE/config_ue.c | 31 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 1 - openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 7 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 6 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 9 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- openair2/RRC/NR/rrc_gNB_reconfig.c | 51 +-- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 320 ++++++++++++++++++ .../CONF/gnb.band78.tm1.106PRB.usrpn300.conf | 51 --- 13 files changed, 407 insertions(+), 145 deletions(-) create mode 100644 targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index c4092c132d..a410bcac6a 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -228,13 +228,19 @@ #define GNB_CONFIG_STRING_ULPTRSREOFFSET_0 "ul_ptrsReOffset_0" #define GNB_CONFIG_STRING_ULPTRSMAXPORTS_0 "ul_ptrsMaxPorts_0" #define GNB_CONFIG_STRING_ULPTRSPOWER_0 "ul_ptrsPower_0" +#define GNB_CONFIG_STRING_DLBWP1_ID "bwp-Id_1" +#define GNB_CONFIG_STRING_DLBWP2_ID "bwp-Id_2" +#define GNB_CONFIG_STRING_DLBWP3_ID "bwp-Id_3" +#define GNB_CONFIG_STRING_DLBWP4_ID "bwp-Id_4" #define GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID "firstActiveDownlinkBWP-Id" -#define GNB_CONFIG_STRING_BWP_ID_0 "bwp-Id_0" -#define GNB_CONFIG_STRING_BWP_ID_1 "bwp-Id_1" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_0 "locationAndBandwidth_0" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_1 "locationAndBandwidth_1" -#define GNB_CONFIG_STRING_SCS_0 "subcarrierSpacing_0" -#define GNB_CONFIG_STRING_SCS_1 "subcarrierSpacing_1" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP1 "bwp1_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP2 "bwp2_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP3 "bwp3_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP4 "bwp4_locationAndBandwidth" +#define GNB_CONFIG_STRING_SCS_BWP1 "bwp1_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_BWP2 "bwp2_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_BWP3 "bwp3_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_BWP4 "bwp4_subcarrierSpacing" #define GNB_CONFIG_STRING_DEFAULTDLBWP_ID "defaultDownlinkBWP-Id" #define GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID "firstActiveUplinkBWP-Id" @@ -443,10 +449,21 @@ {GNB_CONFIG_STRING_ULPTRSREOFFSET_0,NULL,0,i64ptr:scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset,defint64val:-1,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSMAXPORTS_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->maxNrofPorts,defint64val:0,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_ULPTRSPOWER_0,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->ptrs_Power,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID,NULL,0,i64ptr:scd->firstActiveDownlinkBWP_Id,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID,NULL,0,i64ptr:scd->uplinkConfig->firstActiveUplinkBWP_Id,defint64val:0,TYPE_INT64,0}} //, \ -{GNB_CONFIG_STRING_BWP_ID_0,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[0].bwp_Id,defint64val:0,TYPE_INT64,0}}//, \ -{GNB_CONFIG_STRING_BWP_ID_1,NULL,0,i64ptr:scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:0,TYPE_INT64,0}} +{GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID,NULL,0,i64ptr:scd->firstActiveDownlinkBWP_Id,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID,NULL,0,i64ptr:scd->uplinkConfig->firstActiveUplinkBWP_Id,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP1_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Id,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP2_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:2,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP3_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Id,defint64val:3,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DLBWP4_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Id,defint64val:4,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_BWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}} + diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index c0cac495cd..b6d3f5657e 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -326,24 +326,32 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } /* Function to allocate dedicated serving cell config strutures */ -//Abhi : error at this function : when adding for loop to add new elements of downlinkBWP-ToAddModList. -void prepare_scd(NR_ServingCellConfig_t *scd) { +void prepare_scd(NR_ServingCellConfig_t *scd, int scd_present) { // Allocate downlink structures scd->downlinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->downlinkBWP_ToAddModList)); scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); + scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); + + if(scd_present) + { scd->bwp_InactivityTimer = CALLOC(1, sizeof(*scd->bwp_InactivityTimer)); scd->uplinkConfig->firstActiveUplinkBWP_Id = CALLOC(1, sizeof(*scd->uplinkConfig->firstActiveUplinkBWP_Id)); - scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); scd->firstActiveDownlinkBWP_Id = CALLOC(1, sizeof(*scd->firstActiveDownlinkBWP_Id)); + *scd->firstActiveDownlinkBWP_Id = 1; + *scd->uplinkConfig->firstActiveUplinkBWP_Id = 1; + } + for (int j = 0 ;j < 4 ; j++) { // Downlink bandwidth part NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); - bwp->bwp_Id = 1; + bwp->bwp_Id = j+1; // Allocate downlink dedicated bandwidth part and PDSCH structures bwp->bwp_Common = calloc(1, sizeof(*bwp->bwp_Common)); + // bwp->bwp_Common->genericParameters.locationAndBandwidth = calloc(1, sizeof(*bwp->bwp_Common->genericParameters.locationAndBandwidth) ); + // bwp->bwp_Common->genericParameters.subcarrierSpacing = calloc(1, sizeof(*bwp->bwp_Common->genericParameters.subcarrierSpacing) ); bwp->bwp_Common->pdcch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Dedicated = calloc(1, sizeof(*bwp->bwp_Dedicated)); @@ -406,7 +414,7 @@ for (int j = 0 ;j < 4 ; j++) // UL bandwidth part NR_BWP_Uplink_t *ubwp = CALLOC(1, sizeof(*ubwp)); - ubwp->bwp_Id = 1; + ubwp->bwp_Id = j+1; ubwp->bwp_Common = CALLOC(1, sizeof(*ubwp->bwp_Common)); ubwp->bwp_Dedicated = CALLOC(1, sizeof(*ubwp->bwp_Dedicated)); @@ -837,11 +845,10 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { // Serving Cell Config Dedicated NR_ServingCellConfig_t *scd = calloc(1,sizeof(NR_ServingCellConfig_t)); memset((void*)scd,0,sizeof(NR_ServingCellConfig_t)); - prepare_scd(scd); - paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); - printf("RRC BWP 0: %d", scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Id); - printf("RRC BWP 1: %d", scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id); paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; + prepare_scd(scd, SCDsParamList.numelt); + paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); + ////////// Physical parameters @@ -922,14 +929,17 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { if (SCDsParamList.numelt > 0) { sprintf(aprefix, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, 0); config_get( SCDsParams,sizeof(SCDsParams)/sizeof(paramdef_t),aprefix); - LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d, BWP_ID %d \n", + LOG_I(RRC,"Read in ServingCellConfigDedicated UL (FreqDensity_0 %d, FreqDensity_1 %d, TimeDensity_0 %d, TimeDensity_1 %d, TimeDensity_2 %d, RE offset %d, First_active_BWP_ID %d SCS %d, LocationandBW %d \n", (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[0], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[1], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[2], (int)*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset, - (int)*scd->firstActiveDownlinkBWP_Id); + (int)*scd->firstActiveDownlinkBWP_Id, + (int)scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.subcarrierSpacing, + (int)scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth + ); } fix_scd(scd); diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 4a03f81d98..91f009da30 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2880,7 +2880,6 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, dci_pdu->antenna_ports.nbits = getAntPortBitWidth(typeA,typeB); size += dci_pdu->antenna_ports.nbits; // Tx Config Indication - //Abhi : Check this -- bwp_id to replaced with corset_id ? each BWP can have multiple CORSETs .. this has to be corset_id int corset_id = 1 ; long *isTciEnable = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI; if (isTciEnable != NULL) { diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 85e7dc7afc..2b10ff1c0b 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -333,21 +333,20 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; -//Abhi : TODO : uncomment this when DCI based swithching is available - // if (bwp_ind && dci_format){ + if (bwp_ind && dci_format){ - // switch(*dci_format){ - // case NR_UL_DCI_FORMAT_0_1: - // mac->UL_BWP_Id = *bwp_ind; - // break; - // case NR_DL_DCI_FORMAT_1_1: - // mac->DL_BWP_Id = *bwp_ind; - // break; - // default: - // LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); - // } + switch(*dci_format){ + case NR_UL_DCI_FORMAT_0_1: + mac->UL_BWP_Id = *bwp_ind; + break; + case NR_DL_DCI_FORMAT_1_1: + mac->DL_BWP_Id = *bwp_ind; + break; + default: + LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); + } - // } else { + } else { if (scd->firstActiveDownlinkBWP_Id) mac->DL_BWP_Id = *scd->firstActiveDownlinkBWP_Id; @@ -361,7 +360,7 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format else mac->UL_BWP_Id = 1; - // } + } LOG_I(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); @@ -374,12 +373,11 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format */ void config_control_ue(NR_UE_MAC_INST_t *mac){ - //Abhi : Hardcoded CORSET ID here uint8_t coreset_id = 1, ss_id; NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; AssertFatal(scd->downlinkBWP_ToAddModList != NULL, "downlinkBWP_ToAddModList is null\n"); - //AssertFatal(scd->downlinkBWP_ToAddModList->list.count == 1, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); + AssertFatal(scd->downlinkBWP_ToAddModList->list.count <= 4, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); config_bwp_ue(mac, NULL, NULL); NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; @@ -393,7 +391,6 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ NR_BWP_DownlinkDedicated_t *dl_bwp_Dedicated = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Dedicated; AssertFatal(dl_bwp_Dedicated != NULL, "dl_bwp_Dedicated is null\n"); - //Abhi : TODO : add assert statements for UL BWP! NR_SetupRelease_PDCCH_Config_t *pdcch_Config = dl_bwp_Dedicated->pdcch_Config; AssertFatal(pdcch_Config != NULL, "pdcch_Config is null\n"); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 8875695eaf..cea476354f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -224,7 +224,6 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - //Abhi : harcoded CORSET ID here uint8_t bwp_id = mac->DL_BWP_Id; uint8_t coreset_id = 1; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 83fb5924aa..f8fe612e5f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -438,7 +438,6 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr AssertFatal(mac->DLbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); AssertFatal(mac->ULbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); - //Abhi : TODO const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); LOG_D(MAC, "In %s: Processing received DCI format %s (DL BWP %d)\n", __FUNCTION__, dci_formats[dci_format], n_RB_DLBWP); @@ -791,11 +790,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr * 47 DMRS_SEQ_INI: */ - //Abhi : temporarily commenting this out - // if (dci->bwp_indicator.val != 1) { - // LOG_W(MAC, "[%d.%d] bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n", frame, slot); - // return -1; - // } + config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 689f71cf88..72e15afe68 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -625,11 +625,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } else if (*dci_format == NR_UL_DCI_FORMAT_0_1) { /* BANDWIDTH_PART_IND */ - //Abhi : why must BWP always be 1 ? - // if (dci->bwp_indicator.val != 1) { - // LOG_W(MAC, "bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n"); - // return -1; - // } + config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format); target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index e16b43f84f..bab78d2f57 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1067,7 +1067,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.mcs = pdsch_pdu_rel15->mcsIndex[0]; dci_payload.tb_scaling = tb_scaling; - LOG_D(NR_MAC, + LOG_I(NR_MAC, "[RAPROC] DCI type 1 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d \n", dci_payload.frequency_domain_assignment.val, pdsch_pdu_rel15->rbStart, @@ -1078,7 +1078,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.mcs, dci_payload.tb_scaling); - LOG_D(NR_MAC, + LOG_I(NR_MAC, "[RAPROC] DCI params: rnti 0x%x, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", pdcch_pdu_rel15->dci_pdu[0].RNTI, NR_RNTI_RA, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index ba32c9d361..8b31043c54 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -954,12 +954,11 @@ void nr_schedule_ue_spec(module_id_t module_id, // bwp indicator const int n_dl_bwp = UE_info->secondaryCellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; - // AssertFatal(n_dl_bwp == 1, - // "downlinkBWP_ToAddModList has %d BWP!\n", - // n_dl_bwp); + AssertFatal(n_dl_bwp <= 4, + "downlinkBWP_ToAddModList has %d BWP!\n", + n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 - // Abhi :look into this - dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; + dci_payload.bwp_indicator.val = n_dl_bwp <= 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, "Only frequency resource allocation type 1 is currently supported\n"); dci_payload.frequency_domain_assignment.val = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 160232cfd8..f24ddd0eb4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -615,7 +615,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, case NR_UL_DCI_FORMAT_0_1: dci_pdu_rel15->dai[0].val = 0; //TODO // bwp indicator as per table 7.3.1.1.2-1 in 38.212 - dci_pdu_rel15->bwp_indicator.val = n_ubwp < 4 ? bwp_id : bwp_id - 1; + dci_pdu_rel15->bwp_indicator.val = n_ubwp <= 4 ? bwp_id : bwp_id - 1; // SRS resource indicator if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) { AssertFatal(*ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig == NR_PUSCH_Config__txConfig_codebook, diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 0d153ff5cb..52fc295dad 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -461,8 +461,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; - //Abhi : Fix me - // Code has to loop thorough multiple BWP wherever necessary NR_BWP_Downlink_t *bwp = NULL; for (int bwp_loop = 0; bwp_loop < 4 ; bwp_loop++ ) { @@ -473,19 +471,16 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); bwp=calloc(1,sizeof(*bwp)); + // copy common BWP size from initial BWP except for bandwdith + bwp->bwp_Id = bwp_loop +1; + + memcpy((void*)&bwp->bwp_Common->genericParameters, + &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, + sizeof(bwp->bwp_Common->genericParameters)); + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); } - bwp->bwp_Id = bwp_loop +1; //Abhi: remove this ? BWP is added to asn only when scd is not present -#pragma region bwp-downlink-Common - bwp->bwp_Common=calloc(1,sizeof(*bwp->bwp_Common)); - // copy common BWP size from initial BWP except for bandwdith - memcpy((void*)&bwp->bwp_Common->genericParameters, - &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, - sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; //Abhi : hardcoded - - #pragma region bwp_common->pdcch_ConfigCommon bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); @@ -494,7 +489,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); -#pragma region corset NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); coreset->controlResourceSetId=1; // frequency domain resources depends on BWP size @@ -532,13 +526,11 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco coreset->pdcch_DMRS_ScramblingID = NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; -#pragma endregion coreset bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); -#pragma region CommonSearchSpace NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); ss->searchSpaceId = 1; ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); @@ -574,14 +566,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; -#pragma endregion pdcch_ConfigCommon bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); -#pragma endregion bwp_Common->pdcch_ConfigCommon // copy PDSCH TimeDomainResourceAllocation from InitialBWP NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; @@ -595,14 +585,12 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pdschi->startSymbolAndLength = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); } -#pragma endregion bwp_common if (!servingcellconfigdedicated) { bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); } - #pragma region bwp-downlink-Dedicated bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; @@ -773,12 +761,11 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - #pragma endregion bwp-downlink-Dedicated - //Abhi : FIX ME! - //secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - - //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; //Abhi: this has to be removed + if (!servingcellconfigdedicated) { //TODO : check if code goes here + secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + } secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); @@ -788,7 +775,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); } -#pragma region bwp-uplink NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; initialUplinkBWP->pucch_Config = NULL; @@ -801,7 +787,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pusch_Config = calloc(1,sizeof(*pusch_Config)); } - #pragma region pusch-config-ubwp-dedicated initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; @@ -895,7 +880,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco } /*----------------------------------------------------------------------------------------------------------------------------*/ - #pragma endregion PUSCH-Config-ubwp-dedicated initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; @@ -903,7 +887,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco initialUplinkBWP->srs_Config->choice.setup=srs_Config; srs_Config->srs_ResourceSetToReleaseList=NULL; - #pragma region srs_Config-ubwp-dedicated srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); srs_resset0->srs_ResourceSetId = 0; @@ -955,7 +938,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); -#pragma endregion srs_config secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; NR_BWP_Uplink_t *ubwp = NULL; @@ -981,7 +963,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); } - #pragma region bwp-uplink-Dedicated ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; @@ -1110,16 +1091,16 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; -#pragma endregion bwp-uplink-Dedicated if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } -#pragma endregion bwp-uplink-Config } - //secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - //*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; + if (!servingcellconfigdedicated) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; + } secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig)); NR_PUSCH_ServingCellConfig_t *pusch_scc = calloc(1,sizeof(*pusch_scc)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->present = NR_SetupRelease_PUSCH_ServingCellConfig_PR_setup; diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf new file mode 100644 index 0000000000..73217360ff --- /dev/null +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -0,0 +1,320 @@ +Active_gNBs = ( "gNB-Eurecom-5GNRBox"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + + cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-Eurecom-5GNRBox"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + + plmn_list = ({mcc = 208; mnc = 93; mnc_length = 2;}); + + tr_s_preference = "local_mac" + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + pusch_AntennaPorts = 1; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP), points to Subcarrier 0 of RB#10 of SSB block + absoluteFrequencySSB = 641032; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640000; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=33 (Start RB of SSB block),L=24 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 6358; #6368; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 0; #12; + initialDLBWPsearchSpaceZero = 0; + #pdsch-ConfigCommon + #pdschTimeDomainAllocationList (up to 16 entries) + initialDLBWPk0_0 = 0; # used for (full) DL slot + initialDLBWPmappingType_0 = 0; # 0=typeA,1=typeB + initialDLBWPstartSymbolAndLength_0 = 40; # this is SS=1,L=13 + + initialDLBWPk0_1 = 0; # used for DL part mixed slot + initialDLBWPmappingType_1 = 0; + initialDLBWPstartSymbolAndLength_1 = 57; # this is SS=1,L=5 + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 6358;#6368; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -118; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + # pusch-ConfigCommon (up to 16 elements) + initialULBWPk2_0 = 6; # used for (full) UL slot + initialULBWPmappingType_0 = 1 + initialULBWPstartSymbolAndLength_0 = 41; # this is SS=0 L=13 + + initialULBWPk2_1 = 6; # used for UL part mixed slot + initialULBWPmappingType_1 = 1; + initialULBWPstartSymbolAndLength_1 = 69; # this is SS=10 L=2 + + initialULBWPk2_2 = 7; # used for Msg.3 during RA + initialULBWPmappingType_2 = 1; + initialULBWPstartSymbolAndLength_2 = 52; # this is SS=10 L=4 + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 0x1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + +# Dedicated Serving Cell Configuration + servingCellConfigDedicated = ( + { + # BWP-Downlink + # BWP 1 Configuration + bwp-Id_1 = 1; + bwp1_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp1_subcarrierSpacing = 1 ; + # BWP 2 Configuration + bwp-Id_2 = 2; + bwp2_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp2_subcarrierSpacing = 1 ; + # BWP 3 Configuration + bwp-Id_3 = 3; + bwp3_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp3_subcarrierSpacing = 1 ; + # BWP 4 Configuration + bwp-Id_4 = 4; + bwp4_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp4_subcarrierSpacing = 1 ; + + firstActiveDownlinkBWP-Id = 2; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + defaultDownlinkBWP-Id = 1; #BWP-Id + + # UplinkConfig + firstActiveUplinkBWP-Id = 2 ; #BWP-Id + + #BWP-Uplink + + + } + ); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// MME parameters: + mme_ip_address = ( { ipv4 = "192.168.12.26"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + NETWORK_INTERFACES : + { + + GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1_MME = "192.168.12.111/24"; + GNB_INTERFACE_NAME_FOR_S1U = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1U = "192.168.12.111/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + } +); + +L1s = ( + { + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + } +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [7]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 50; + eNB_instances = [0]; + ## beamforming 1x2 matrix: 1 layer x 2 antennas + #bf_weights = [0x00007fff, 0x00007fff]; + ## beamforming 1x4 matrix: 1 layer x 4 antennas + #bf_weights = [0x00007fff, 0x0000,0x0000, 0x0000]; + ## beamforming 2x2 matrix: + #bf_weights = [0x00007fff, 0x00000000, 0x00000000, 0x00007fff]; + ## beamforming 4x4 matrix: + #bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff]; + sdr_addrs = "addr=192.168.10.2,mgmt_addr=192.168.10.2,second_addr=192.168.20.2"; + clock_src = "external"; + } +); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + + log_config : + { + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + }; + diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf index d3f886332e..b57cb00779 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf @@ -175,57 +175,6 @@ gNBs = ); -# Dedicated Serving Cell Configuration - servingCellConfigDedicated = ( - { - # BWP-Downlink - # DL PTRS related parameters. - # 3GPP TS 38.331 PTRS-DownlinkConfig - - # BWP 1 Configuration - - bwp-Id_0 = 1; - locationAndBandwidth_0 = 1234; //TODO - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - subcarrierSpacing_0 = 1 ; - - # BWP 2 Configuration - - bwp-Id_1 = 1; - locationAndBandwidth_1 = 1234; //TODO - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - subcarrierSpacing_1 = 1 ; - - - - - firstActiveDownlinkBWP-Id = 2; #BWP-Id - - # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, - # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, - # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, - # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } - - defaultDownlinkBWP-Id = 1; #BWP-Id - - # UplinkConfig - firstActiveUplinkBWP-Id = 2 ; #BWP-Id - - #BWP-Uplink - # UL PTRS related parameters. - # 3GPP TS 38.331 PTRS-UplinkConfig - - # frequencyDensity value range: 1-276 - - - # ptrs-Power - # 0 = p00, 1 = p01, 2 = p10, 3 = p11 - #ul_ptrsPower_0 = 0; - - } - ); # ------- SCTP definitions SCTP : From 24acc0e8b0c0a95fae49a03b8734a20b04102b56 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Thu, 27 May 2021 20:49:55 +0530 Subject: [PATCH 017/233] scs and bwp location error fixed --- openair2/GNB_APP/gnb_config.c | 12 +++++------- openair2/RRC/NR/rrc_gNB_reconfig.c | 14 ++++++-------- .../CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 4 ++-- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index b6d3f5657e..bfbe454a32 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -326,20 +326,17 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } /* Function to allocate dedicated serving cell config strutures */ -void prepare_scd(NR_ServingCellConfig_t *scd, int scd_present) { +void prepare_scd(NR_ServingCellConfig_t *scd) { // Allocate downlink structures scd->downlinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->downlinkBWP_ToAddModList)); scd->uplinkConfig = CALLOC(1, sizeof(*scd->uplinkConfig)); scd->uplinkConfig->uplinkBWP_ToAddModList = CALLOC(1, sizeof(*scd->uplinkConfig->uplinkBWP_ToAddModList)); - - if(scd_present) - { scd->bwp_InactivityTimer = CALLOC(1, sizeof(*scd->bwp_InactivityTimer)); scd->uplinkConfig->firstActiveUplinkBWP_Id = CALLOC(1, sizeof(*scd->uplinkConfig->firstActiveUplinkBWP_Id)); scd->firstActiveDownlinkBWP_Id = CALLOC(1, sizeof(*scd->firstActiveDownlinkBWP_Id)); *scd->firstActiveDownlinkBWP_Id = 1; *scd->uplinkConfig->firstActiveUplinkBWP_Id = 1; - } + for (int j = 0 ;j < 4 ; j++) { @@ -845,9 +842,10 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { // Serving Cell Config Dedicated NR_ServingCellConfig_t *scd = calloc(1,sizeof(NR_ServingCellConfig_t)); memset((void*)scd,0,sizeof(NR_ServingCellConfig_t)); - paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; - prepare_scd(scd, SCDsParamList.numelt); + prepare_scd(scd); paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); + paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; + ////////// Physical parameters diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 52fc295dad..72b0fd1d34 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -473,12 +473,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp=calloc(1,sizeof(*bwp)); // copy common BWP size from initial BWP except for bandwdith bwp->bwp_Id = bwp_loop +1; - - memcpy((void*)&bwp->bwp_Common->genericParameters, + } + if(bwp->bwp_Common->genericParameters.subcarrierSpacing == 0 || bwp->bwp_Common->genericParameters.locationAndBandwidth == 0) + { + memcpy((void*)&bwp->bwp_Common->genericParameters, &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); } bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); @@ -762,10 +764,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - if (!servingcellconfigdedicated) { //TODO : check if code goes here - secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; - } secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 73217360ff..9212bff4b8 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -204,7 +204,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp4_subcarrierSpacing = 1 ; - firstActiveDownlinkBWP-Id = 2; #BWP-Id + firstActiveDownlinkBWP-Id = 1; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -214,7 +214,7 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 2 ; #BWP-Id + firstActiveUplinkBWP-Id = 1 ; #BWP-Id #BWP-Uplink From ea1b995a753e42f1f7d4192a8cf67f25e6d44448 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Tue, 1 Jun 2021 15:50:16 +0530 Subject: [PATCH 018/233] Fixed error with BWP_Id 4 --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 4 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 8 +-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 28 +++++------ .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 6 +-- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 11 +++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 24 ++++----- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 49 ++++++++++++++++++- 9 files changed, 91 insertions(+), 45 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 2b10ff1c0b..0ef76d6c8b 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -337,10 +337,10 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format switch(*dci_format){ case NR_UL_DCI_FORMAT_0_1: - mac->UL_BWP_Id = *bwp_ind; + mac->UL_BWP_Id = *bwp_ind +1 ; break; case NR_DL_DCI_FORMAT_1_1: - mac->DL_BWP_Id = *bwp_ind; + mac->DL_BWP_Id = *bwp_ind +1; break; default: LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 72e15afe68..ecf1ce1a83 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -748,7 +748,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; - valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[mac->UL_BWP_Id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, + valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, pusch_config_pdu->rb_size, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table, &pusch_config_pdu->pusch_ptrs.ptrs_freq_density,&pusch_config_pdu->pusch_ptrs.ptrs_time_density, &pusch_config_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset,&pusch_config_pdu->pusch_ptrs.num_ptrs_ports, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index a0c172a700..17bec32905 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -397,8 +397,8 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing); } - memset(RC.nrmac[module_idP]->cce_list[bwp_id][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - memset(RC.nrmac[module_idP]->cce_list[bwp_id][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 + memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 + memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) for (int i=0; icontrolResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -971,7 +971,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1284,7 +1284,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -1293,7 +1293,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 8b31043c54..98d5362e75 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -60,7 +60,7 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = bwp->bwp_Id; - if (nrmac->preferred_dl_tda[bwp_id]) + if (nrmac->preferred_dl_tda[bwp_id-1]) return; /* there is a mixed slot only when in TDD */ @@ -118,17 +118,17 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_dl_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id])); + nrmac->preferred_dl_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id-1])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int i = 0; i < n; ++i) { - nrmac->preferred_dl_tda[bwp_id][i] = -1; + nrmac->preferred_dl_tda[bwp_id-1][i] = -1; if (!tdd || i % nr_slots_period < tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id][i] = 0; + nrmac->preferred_dl_tda[bwp_id-1][i] = 0; else if (tdd && nr_mix_slots && i % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id][i] = tdaMi; - LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id][i]); + nrmac->preferred_dl_tda[bwp_id-1][i] = tdaMi; + LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id-1][i]); } } @@ -429,7 +429,7 @@ bool allocate_dl_retransmission(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = 1; int rbSize = 0; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; if (tda == retInfo->time_domain_allocation) { /* Check that there are enough resources for retransmission */ while (rbSize < retInfo->rbSize) { @@ -503,7 +503,7 @@ bool allocate_dl_retransmission(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return false; @@ -628,7 +628,7 @@ void pf_dl(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -642,7 +642,7 @@ void pf_dl(module_id_t module_id, /* MCS has been set above */ const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) @@ -688,7 +688,7 @@ void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; if (tda < 0) return; @@ -844,7 +844,7 @@ void nr_schedule_ue_spec(module_id_t module_id, * exist, create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; if (!pdcch_pdu) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -853,7 +853,7 @@ void nr_schedule_ue_spec(module_id_t module_id, dl_req->nPDUs += 1; pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, bwp); - gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu; + gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -958,7 +958,7 @@ void nr_schedule_ue_spec(module_id_t module_id, "downlinkBWP_ToAddModList has %d BWP!\n", n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 - dci_payload.bwp_indicator.val = n_dl_bwp <= 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; + dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, "Only frequency resource allocation type 1 is currently supported\n"); dci_payload.frequency_domain_assignment.val = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index adfcd79b8e..0eef5a59b9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -349,7 +349,7 @@ void nr_preprocessor_phytest(module_id_t module_id, frame, slot); UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -364,7 +364,7 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_pdsch->pucch_allocation = alloc; sched_pdsch->rbStart = rbStart; sched_pdsch->rbSize = rbSize; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; const uint8_t num_dmrs_cdm_grps_no_data = 1; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( @@ -414,7 +414,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (tda < 0) return false; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index f24ddd0eb4..47e5d4e6e4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -177,9 +177,9 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, int *cce_list; if( bwp==NULL || bwp->bwp_Id == 0 ) { - cce_list = nr_mac->cce_list[1][0]; + cce_list = nr_mac->cce_list[0][0]; } else { - cce_list = nr_mac->cce_list[bwp->bwp_Id][coreset_id]; + cce_list = nr_mac->cce_list[bwp->bwp_Id-1][coreset_id]; } int n_rb=0; @@ -615,7 +615,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, case NR_UL_DCI_FORMAT_0_1: dci_pdu_rel15->dai[0].val = 0; //TODO // bwp indicator as per table 7.3.1.1.2-1 in 38.212 - dci_pdu_rel15->bwp_indicator.val = n_ubwp <= 4 ? bwp_id : bwp_id - 1; + dci_pdu_rel15->bwp_indicator.val = n_ubwp < 4 ? bwp_id : bwp_id - 1; // SRS resource indicator if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) { AssertFatal(*ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig == NR_PUSCH_Config__txConfig_codebook, @@ -632,7 +632,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, } LOG_D(MAC, - "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n", + "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d UL_BWP_ID %d\n", __func__, dci_pdu_rel15->frequency_domain_assignment.val, dci_pdu_rel15->time_domain_assignment.val, @@ -640,7 +640,8 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, dci_pdu_rel15->mcs, dci_pdu_rel15->tpc, dci_pdu_rel15->ndi, - dci_pdu_rel15->rv); + dci_pdu_rel15->rv, + dci_pdu_rel15->bwp_indicator.val); } void nr_configure_pdcch(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 323baa48a7..c881f19de6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -65,7 +65,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = ubwp->bwp_Id; - if (nrmac->preferred_ul_tda[bwp_id]) + if (nrmac->preferred_ul_tda[bwp_id-1]) return; /* there is a mixed slot only when in TDD */ @@ -151,18 +151,18 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_ul_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id])); + nrmac->preferred_ul_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id-1])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int slot = 0; slot < n; ++slot) { const int sched_slot = (slot + k2) % n; - nrmac->preferred_ul_tda[bwp_id][slot] = -1; + nrmac->preferred_ul_tda[bwp_id-1][slot] = -1; if (!tdd || sched_slot % nr_slots_period >= tdd->nrofDownlinkSlots + nr_mix_slots) - nrmac->preferred_ul_tda[bwp_id][slot] = 0; + nrmac->preferred_ul_tda[bwp_id-1][slot] = 0; else if (tdd && nr_mix_slots && sched_slot % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_ul_tda[bwp_id][slot] = tdaMi; - LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id][slot]); + nrmac->preferred_ul_tda[bwp_id-1][slot] = tdaMi; + LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id-1][slot]); } if (k2 < tdd->nrofUplinkSlots) @@ -799,7 +799,7 @@ bool allocate_ul_retransmission(module_id_t module_id, NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (tda == retInfo->time_domain_allocation) { /* Check the resource is enough for retransmission */ while (rbStart < bwpSize && !rballoc_mask[rbStart]) @@ -986,7 +986,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1076,7 +1076,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1126,7 +1126,7 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * schedule now (slot + k2 is not UL slot) */ int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; if (tda < 0) return false; int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu); @@ -1461,7 +1461,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) * create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid-1][coresetid]; if (!pdcch_pdu) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); @@ -1470,7 +1470,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, sched_ctrl->active_bwp); - pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu; + pdcch_pdu_bwp_coreset[bwpid-1][coresetid] = pdcch_pdu; } LOG_D(MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 9212bff4b8..74777ecced 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -204,7 +204,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp4_subcarrierSpacing = 1 ; - firstActiveDownlinkBWP-Id = 1; #BWP-Id + firstActiveDownlinkBWP-Id = 4; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -214,9 +214,54 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 1 ; #BWP-Id + firstActiveUplinkBWP-Id = 4 ; #BWP-Id #BWP-Uplink + # DL PTRS related parameters. + # 3GPP TS 38.331 PTRS-DownlinkConfig + + # frequencyDensity value range: 1-276 + dl_ptrsFreqDensity0_0 = 25; + dl_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + dl_ptrsTimeDensity0_0 = 2; + dl_ptrsTimeDensity1_0 = 4; + dl_ptrsTimeDensity2_0 = 10; + + # epre_Ratio + # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 + dl_ptrsEpreRatio_0 = 0; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 + dl_ptrsReOffset_0 = 0; + + # UL PTRS related parameters. + # 3GPP TS 38.331 PTRS-UplinkConfig + + # frequencyDensity value range: 1-276 + ul_ptrsFreqDensity0_0 = 25; + ul_ptrsFreqDensity1_0 = 75; + + # timeDensity value range: 0-29 + ul_ptrsTimeDensity0_0 = 2; + ul_ptrsTimeDensity1_0 = 4; + ul_ptrsTimeDensity2_0 = 10; + + # resourceElementOffset + # 0 = offset01 1 = offset02, 2 = offset02 + # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 + ul_ptrsReOffset_0 = 0; + + # maxNrofPorts + # 0 = n1, 1 = n2 + ul_ptrsMaxPorts_0 = 0; + + # ptrs-Power + # 0 = p00, 1 = p01, 2 = p10, 3 = p11 + #ul_ptrsPower_0 = 0; } From d3674a25e1d01366128244aea932c20bdcbb47de Mon Sep 17 00:00:00 2001 From: Abhijith Atreya Date: Tue, 1 Jun 2021 11:35:03 +0000 Subject: [PATCH 019/233] Fixed PTRS errors --- openair2/GNB_APP/gnb_config.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index bfbe454a32..ff295a1757 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -368,7 +368,6 @@ for (int j = 0 ;j < 4 ; j++) NR_PTRS_DownlinkCfg->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->frequencyDensity)); long *dl_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { - dl_rbs[i]=1; ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->frequencyDensity->list, &dl_rbs[i]); } NR_PTRS_DownlinkCfg->timeDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->timeDensity)); @@ -398,7 +397,6 @@ for (int j = 0 ;j < 4 ; j++) NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity)); long *n_rbs = CALLOC(2, sizeof(long)); for (int i=0;i<2;i++) { - n_rbs[i] = 1; ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity->list, &n_rbs[i]); } NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity)); From 6602bdeed87a63a66a31dd7dde42306c597a45a4 Mon Sep 17 00:00:00 2001 From: Shruthi Date: Wed, 2 Jun 2021 12:17:08 +0530 Subject: [PATCH 020/233] PTRS paramaters removed --- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 50 +------------------ 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 74777ecced..6bbf4541ff 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -204,7 +204,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp4_subcarrierSpacing = 1 ; - firstActiveDownlinkBWP-Id = 4; #BWP-Id + firstActiveDownlinkBWP-Id = 1; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, @@ -214,55 +214,9 @@ gNBs = defaultDownlinkBWP-Id = 1; #BWP-Id # UplinkConfig - firstActiveUplinkBWP-Id = 4 ; #BWP-Id + firstActiveUplinkBWP-Id = 1; #BWP-Id #BWP-Uplink - # DL PTRS related parameters. - # 3GPP TS 38.331 PTRS-DownlinkConfig - - # frequencyDensity value range: 1-276 - dl_ptrsFreqDensity0_0 = 25; - dl_ptrsFreqDensity1_0 = 75; - - # timeDensity value range: 0-29 - dl_ptrsTimeDensity0_0 = 2; - dl_ptrsTimeDensity1_0 = 4; - dl_ptrsTimeDensity2_0 = 10; - - # epre_Ratio - # 0 = "00", 1 = "01" in TS 38.214, table 4.1-2 - dl_ptrsEpreRatio_0 = 0; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # 3GPP TS 38.331 PTRS-DownlinkConfig; TS 38.211 sec. 7.4.1.2.2 - dl_ptrsReOffset_0 = 0; - - # UL PTRS related parameters. - # 3GPP TS 38.331 PTRS-UplinkConfig - - # frequencyDensity value range: 1-276 - ul_ptrsFreqDensity0_0 = 25; - ul_ptrsFreqDensity1_0 = 75; - - # timeDensity value range: 0-29 - ul_ptrsTimeDensity0_0 = 2; - ul_ptrsTimeDensity1_0 = 4; - ul_ptrsTimeDensity2_0 = 10; - - # resourceElementOffset - # 0 = offset01 1 = offset02, 2 = offset02 - # TS 38.331 PTRS-UplinkConfig; TS 38.211 sec. 6.4.1.2.2.1 - ul_ptrsReOffset_0 = 0; - - # maxNrofPorts - # 0 = n1, 1 = n2 - ul_ptrsMaxPorts_0 = 0; - - # ptrs-Power - # 0 = p00, 1 = p01, 2 = p10, 3 = p11 - #ul_ptrsPower_0 = 0; - } ); From 039442c1d453f9984dfbceed0ad73b2cbecd6926 Mon Sep 17 00:00:00 2001 From: Abhijith Atreya Date: Wed, 2 Jun 2021 14:32:32 +0000 Subject: [PATCH 021/233] Fixed warnings and extra Log messages removed --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 0ef76d6c8b..2c1fe2e998 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -362,7 +362,7 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format } - LOG_I(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); + LOG_D(MAC, "In %s setting DL_BWP_Id %ld UL_BWP_Id %ld \n", __FUNCTION__, mac->DL_BWP_Id, mac->UL_BWP_Id); } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index f8fe612e5f..bdf7daa9ee 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -1790,7 +1790,7 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, return; } - LOG_I(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); + LOG_D(MAC, "In %s [%d.%d]: processing PDU %d (with length %d) of %d total number of PDUs...\n", __FUNCTION__, frameP, slot, pdu_id, pdu_len, dl_info->rx_ind->number_pdus); while (!done && pdu_len > 0){ mac_ce_len = 0x0000; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 17bec32905..42af216a6b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -325,7 +325,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - int nb_periods_per_frame; + //int nb_periods_per_frame; gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 98d5362e75..427f6dedcc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -819,8 +819,8 @@ void nr_schedule_ue_spec(module_id_t module_id, harq->is_waiting = true; UE_info->mac_stats[UE_id].dlsch_rounds[harq->round]++; - LOG_I(MAC, - "%4d.%2d RNTI %04x start %d RBs %d startSymbol %d nb_symbsol %d MCS %d TBS %d HARQ PID %d round %d NDI %d\n", + LOG_D(MAC, + "%4d.%2d RNTI %04x start %3d RBs %3d startSymbol %2d nb_symbol %2d MCS %2d TBS %4d HARQ PID %2d round %d NDI %d\n", frame, slot, rnti, From c623d904535b61ef238abc929255fc1c4518c5be Mon Sep 17 00:00:00 2001 From: Abhijith Date: Fri, 25 Jun 2021 13:18:52 +0530 Subject: [PATCH 022/233] Review comments incorporated --- .vscode/launch.json | 29 ++++++++++++ openair2/GNB_APP/gnb_config.c | 2 +- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 46 +------------------ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 8 ++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 26 +++++------ .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 21 +++++++-- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 24 +++++----- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 8 ++-- 12 files changed, 86 insertions(+), 88 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..243518f81e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "enter program name, for example ${workspaceFolder}/a.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/path/to/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index ff295a1757..b9f394dad9 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -424,7 +424,7 @@ for (int j = 0 ;j < 4 ; j++) /* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { // Check for DL PTRS parameters validity - for (int bwp_i = 0 ; bwp_i< MAX_NUM_BWP; bwp_i++) + for (int bwp_i = 0 ; bwp_i< 4; bwp_i++) { if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 2c1fe2e998..111fee4883 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -418,7 +418,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ // check pdcch_Config, pdcch_ConfigCommon and DL BWP // Check dedicated UL BWP and pass to MAC - for (int num_BWP = 0; num_BWP DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP]; mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP]; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 8d7b26bed9..c33173cf38 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -74,7 +74,7 @@ // ========== #define NB_NR_UE_MAC_INST 1 -#define MAX_NUM_BWP 4 +#define MAX_NUM_BWP 5 #define NUM_SLOT_FRAME 10 /*!\brief value for indicating BSR Timer is not running */ diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 42af216a6b..f655d3d65e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -325,54 +325,12 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - //int nb_periods_per_frame; gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; - - // NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; - - // switch(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity) { - // case 0: - // nb_periods_per_frame = 20; // 10ms/0p5ms - // break; - - // case 1: - // nb_periods_per_frame = 16; // 10ms/0p625ms - // break; - - // case 2: - // nb_periods_per_frame = 10; // 10ms/1ms - // break; - - // case 3: - // nb_periods_per_frame = 8; // 10ms/1p25ms - // break; - - // case 4: - // nb_periods_per_frame = 5; // 10ms/2ms - // break; - - // case 5: - // nb_periods_per_frame = 4; // 10ms/2p5ms - // break; - - // case 6: - // nb_periods_per_frame = 2; // 10ms/5ms - // break; - - // case 7: - // nb_periods_per_frame = 1; // 10ms/10ms - // break; - - // default: - // AssertFatal(1==0,"Undefined tdd period %ld\n", scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); - - // } - if (slot==0 && (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]>=257)) { const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; const int n = nr_slots_per_frame[*scc->ssbSubcarrierSpacing]; @@ -397,8 +355,8 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing); } - memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - memset(RC.nrmac[module_idP]->cce_list[bwp_id-1][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 + memset(RC.nrmac[module_idP]->cce_list[bwp_id][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 + memset(RC.nrmac[module_idP]->cce_list[bwp_id][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) for (int i=0; icontrolResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -971,7 +971,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1284,7 +1284,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -1293,7 +1293,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id-1][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 427f6dedcc..b43c9e2e2c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -60,7 +60,7 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = bwp->bwp_Id; - if (nrmac->preferred_dl_tda[bwp_id-1]) + if (nrmac->preferred_dl_tda[bwp_id]) return; /* there is a mixed slot only when in TDD */ @@ -118,17 +118,17 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_dl_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id-1])); + nrmac->preferred_dl_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int i = 0; i < n; ++i) { - nrmac->preferred_dl_tda[bwp_id-1][i] = -1; + nrmac->preferred_dl_tda[bwp_id][i] = -1; if (!tdd || i % nr_slots_period < tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id-1][i] = 0; + nrmac->preferred_dl_tda[bwp_id][i] = 0; else if (tdd && nr_mix_slots && i % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id-1][i] = tdaMi; - LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id-1][i]); + nrmac->preferred_dl_tda[bwp_id][i] = tdaMi; + LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id][i]); } } @@ -429,7 +429,7 @@ bool allocate_dl_retransmission(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = 1; int rbSize = 0; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; if (tda == retInfo->time_domain_allocation) { /* Check that there are enough resources for retransmission */ while (rbSize < retInfo->rbSize) { @@ -503,7 +503,7 @@ bool allocate_dl_retransmission(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return false; @@ -628,7 +628,7 @@ void pf_dl(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -642,7 +642,7 @@ void pf_dl(module_id_t module_id, /* MCS has been set above */ const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) @@ -688,7 +688,7 @@ void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; if (tda < 0) return; @@ -844,7 +844,7 @@ void nr_schedule_ue_spec(module_id_t module_id, * exist, create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -853,7 +853,7 @@ void nr_schedule_ue_spec(module_id_t module_id, dl_req->nPDUs += 1; pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, bwp); - gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu; + gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 0eef5a59b9..4986ed747d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -282,6 +282,8 @@ void nr_preprocessor_phytest(module_id_t module_id, NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; /* find largest unallocated chunk */ const int bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + + target_dl_bw = (bwpSize < target_dl_bw) ?bwpSize : target_dl_bw; int rbStart = 0; int rbSize = 0; uint16_t *vrb_map = RC.nrmac[module_id]->common_channels[CC_id].vrb_map; @@ -295,7 +297,7 @@ void nr_preprocessor_phytest(module_id_t module_id, while (rbStart + rbSize < bwpSize && !vrb_map[rbStart + rbSize] && rbSize < target_dl_bw) rbSize++; /* found target_dl_bw? */ - if (rbSize == target_dl_bw) + if (rbStart+ rbSize == target_dl_bw) break; /* at end and below target_dl_bw? */ if (rbStart + rbSize >= bwpSize) @@ -349,7 +351,7 @@ void nr_preprocessor_phytest(module_id_t module_id, frame, slot); UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -364,7 +366,7 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_pdsch->pucch_allocation = alloc; sched_pdsch->rbStart = rbStart; sched_pdsch->rbSize = rbSize; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; const uint8_t num_dmrs_cdm_grps_no_data = 1; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( @@ -414,7 +416,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (tda < 0) return false; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = @@ -445,7 +447,16 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); uint16_t rbStart = 0; - uint16_t rbSize = target_ul_bw; + uint16_t rbSize; + + const int bw = NRRIV2BW(sched_ctrl->active_ubwp ? + sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth : + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + + if (target_ul_bw>bw) + rbSize = bw; + else + rbSize = target_ul_bw; uint16_t *vrb_map_UL = &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[sched_slot * MAX_BWP_SIZE]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 47e5d4e6e4..0ff4136057 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -177,9 +177,9 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, int *cce_list; if( bwp==NULL || bwp->bwp_Id == 0 ) { - cce_list = nr_mac->cce_list[0][0]; + cce_list = nr_mac->cce_list[1][0]; } else { - cce_list = nr_mac->cce_list[bwp->bwp_Id-1][coreset_id]; + cce_list = nr_mac->cce_list[bwp->bwp_Id][coreset_id]; } int n_rb=0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index c881f19de6..323baa48a7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -65,7 +65,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = ubwp->bwp_Id; - if (nrmac->preferred_ul_tda[bwp_id-1]) + if (nrmac->preferred_ul_tda[bwp_id]) return; /* there is a mixed slot only when in TDD */ @@ -151,18 +151,18 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_ul_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id-1])); + nrmac->preferred_ul_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int slot = 0; slot < n; ++slot) { const int sched_slot = (slot + k2) % n; - nrmac->preferred_ul_tda[bwp_id-1][slot] = -1; + nrmac->preferred_ul_tda[bwp_id][slot] = -1; if (!tdd || sched_slot % nr_slots_period >= tdd->nrofDownlinkSlots + nr_mix_slots) - nrmac->preferred_ul_tda[bwp_id-1][slot] = 0; + nrmac->preferred_ul_tda[bwp_id][slot] = 0; else if (tdd && nr_mix_slots && sched_slot % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_ul_tda[bwp_id-1][slot] = tdaMi; - LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id-1][slot]); + nrmac->preferred_ul_tda[bwp_id][slot] = tdaMi; + LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id][slot]); } if (k2 < tdd->nrofUplinkSlots) @@ -799,7 +799,7 @@ bool allocate_ul_retransmission(module_id_t module_id, NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (tda == retInfo->time_domain_allocation) { /* Check the resource is enough for retransmission */ while (rbStart < bwpSize && !rballoc_mask[rbStart]) @@ -986,7 +986,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1076,7 +1076,7 @@ void pf_ul(module_id_t module_id, * num_dmrs_cdm_grps_no_data has changed and only then recompute */ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1126,7 +1126,7 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * schedule now (slot + k2 is not UL slot) */ int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot]; + const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; if (tda < 0) return false; int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu); @@ -1461,7 +1461,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) * create it */ const int bwpid = sched_ctrl->active_bwp->bwp_Id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); @@ -1470,7 +1470,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, sched_ctrl->active_bwp); - pdcch_pdu_bwp_coreset[bwpid-1][coresetid] = pdcch_pdu; + pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu; } LOG_D(MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 36c8ac7932..bd7304ad8f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -72,7 +72,7 @@ /* Defs */ -#define MAX_NUM_BWP 4 +#define MAX_NUM_BWP 5 #define MAX_NUM_CORESET 2 #define MAX_NUM_CCE 90 #define MAX_HARQ_ROUNDS 4 diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 6bbf4541ff..c21f586d0a 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -43,7 +43,7 @@ gNBs = # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_subcarrierSpacing = 1; - dl_carrierBandwidth = 106; + dl_carrierBandwidth = 106; //when changing BWPs, change this also to the same RBs #initialDownlinkBWP #genericParameters # this is RBstart=33 (Start RB of SSB block),L=24 (275*(L-1))+RBstart @@ -72,7 +72,7 @@ gNBs = # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_subcarrierSpacing = 1; - ul_carrierBandwidth = 106; + ul_carrierBandwidth = 106; //when changing BWPs, change this also to the same RBs pMax = 20; #initialUplinkBWP #genericParameters @@ -187,13 +187,13 @@ gNBs = bwp1_subcarrierSpacing = 1 ; # BWP 2 Configuration bwp-Id_2 = 2; - bwp2_locationAndBandwidth = 28908; // RBstart=33,L=106 + bwp2_locationAndBandwidth = 6325; // RBstart=0,L=24 ( 10 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp2_subcarrierSpacing = 1 ; # BWP 3 Configuration bwp-Id_3 = 3; - bwp3_locationAndBandwidth = 28908; // RBstart=33,L=106 + bwp3_locationAndBandwidth = 13750; // RBstart=0,L=51 ( 20 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 bwp3_subcarrierSpacing = 1 ; From 8507a3d28156d81429836863132ffca56393acab Mon Sep 17 00:00:00 2001 From: Abhijith Date: Fri, 25 Jun 2021 14:04:32 +0530 Subject: [PATCH 023/233] Log level changed --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index bab78d2f57..3cbd188ecd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -790,8 +790,7 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->crnti); - // AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - // "downlinkBWP_ToAddModList has %d BWP!\n", ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); + NR_BWP_Uplink_t *ubwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; LOG_D(NR_MAC, "Frame %d, Subframe %d Adding Msg3 UL Config Request for (%d,%d) : (%d,%d,%d) for rnti: %d\n", frameP, @@ -986,10 +985,6 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->RA_rnti); - // AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - // "downlinkBWP_ToAddModList has %d BWP!\n", - // ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); - // SCF222: PDU index incremented for each PDSCH PDU sent in TX control message. This is used to associate control // information to data and is reset every slot. const int pduindex = nr_mac->pdu_index[CC_id]++; @@ -1067,7 +1062,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.mcs = pdsch_pdu_rel15->mcsIndex[0]; dci_payload.tb_scaling = tb_scaling; - LOG_I(NR_MAC, + LOG_D(NR_MAC, "[RAPROC] DCI type 1 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d \n", dci_payload.frequency_domain_assignment.val, pdsch_pdu_rel15->rbStart, @@ -1078,7 +1073,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.mcs, dci_payload.tb_scaling); - LOG_I(NR_MAC, + LOG_D(NR_MAC, "[RAPROC] DCI params: rnti 0x%x, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", pdcch_pdu_rel15->dci_pdu[0].RNTI, NR_RNTI_RA, From 1f94285040958891838323a679bdd46e34d411e2 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Fri, 2 Jul 2021 17:03:20 +0530 Subject: [PATCH 024/233] Rebased to develop after SA merge --- ci-scripts/Jenkinsfile-trig-nsa | 27 +- ci-scripts/cls_module_ue.py | 51 +- ci-scripts/cls_oaicitest.py | 141 +- .../enb.band7.tm1.fr1.25PRB.usrpb210.conf | 2 +- .../gnb.band78.sa.fr1.106PRB.usrpb210.conf | 297 +++ .../gnb.band78.tm1.fr1.106PRB.usrpb210.conf | 4 +- ci-scripts/main.py | 20 +- ci-scripts/reportTestLocally.sh | 76 + ci-scripts/runTestOnVM.sh | 149 +- ci-scripts/xml_files/fr1_nsa_quectel.xml | 5 +- .../fr1_nsa_quectel_attach_detach.xml | 168 ++ ci-scripts/xml_files/fr1_nsa_quectel_long.xml | 148 ++ cmake_targets/CMakeLists.txt | 176 +- cmake_targets/build_oai | 12 +- common/config/config_cmdline.c | 2 +- common/utils/LOG/log.c | 2 +- common/utils/nr/nr_common.c | 4 - common/utils/ocp_itti/intertask_interface.h | 1 + common/utils/system.c | 11 +- configuration/bladeRF/enb-band7-5mhz.conf | 2 +- doc/RUNMODEM.md | 26 +- doc/TESTING_5GSA_setup.md | 162 ++ doc/testing_gnb_w_cots_ue_resources/enb.conf | 2 +- executables/main-ocp.c | 1 + executables/nr-gnb.c | 22 +- executables/nr-ru.c | 34 +- executables/nr-softmodem.c | 131 +- executables/nr-ue.c | 13 +- executables/nr-uesoftmodem.c | 42 +- executables/nr-uesoftmodem.h | 2 +- executables/softmodem-common.h | 26 +- .../nfapi/public_inc/fapi_nr_ue_constants.h | 6 +- .../nfapi/public_inc/fapi_nr_ue_interface.h | 6 +- openair1/PHY/CODING/nr_rate_matching.c | 4 +- openair1/PHY/INIT/nr_init_ru.c | 2 +- openair1/PHY/INIT/nr_parms.c | 5 +- .../lte_ul_channel_estimation.c | 2 +- openair1/PHY/LTE_TRANSPORT/dci_tools.c | 25 +- openair1/PHY/LTE_TRANSPORT/pucch.c | 9 +- openair1/PHY/LTE_TRANSPORT/transport_common.h | 3 +- openair1/PHY/MODULATION/slot_fep_nr.c | 19 +- .../PHY/NR_ESTIMATION/nr_measurements_gNB.c | 94 +- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 2 +- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 4 +- openair1/PHY/NR_TRANSPORT/nr_dci.c | 1 + openair1/PHY/NR_TRANSPORT/nr_dci_tools.c | 7 +- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 5 +- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 1 + openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 47 +- openair1/PHY/NR_TRANSPORT/nr_ulsch.h | 4 +- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 15 +- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 18 +- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 259 +- .../PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c | 2 +- .../PHY/NR_UE_ESTIMATION/nr_ue_measurements.c | 4 +- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 22 +- .../PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c | 6 +- .../NR_UE_TRANSPORT/nr_dlsch_demodulation.c | 93 - openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c | 2 +- openair1/PHY/NR_UE_TRANSPORT/nr_prach.c | 15 +- .../PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c | 8 +- .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 14 +- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 4 + openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c | 47 +- openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h | 12 +- openair1/PHY/defs_common.h | 3 +- openair1/PHY/defs_gNB.h | 44 +- openair1/SCHED/phy_procedures_lte_eNb.c | 1 - openair1/SCHED_NR/nr_prach_procedures.c | 3 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 85 +- openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 14 +- openair1/SCHED_NR_UE/phy_frame_config_nr_ue.c | 2 + openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 24 +- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 244 +- openair1/SIMULATION/NR_PHY/dlsim.c | 46 +- openair1/SIMULATION/NR_PHY/prachsim.c | 25 +- openair1/SIMULATION/NR_PHY/pucchsim.c | 11 +- openair1/SIMULATION/NR_PHY/ulsim.c | 33 +- openair2/COMMON/f1ap_messages_def.h | 3 + openair2/COMMON/f1ap_messages_types.h | 90 +- openair2/COMMON/gtpv1_u_messages_def.h | 3 +- openair2/COMMON/gtpv1_u_messages_types.h | 19 +- openair2/COMMON/mac_messages_types.h | 2 + openair2/COMMON/pdcp_messages_def.h | 1 + openair2/COMMON/pdcp_messages_types.h | 15 + openair2/COMMON/platform_constants.h | 1 + openair2/ENB_APP/enb_config.c | 26 +- openair2/F1AP/dummy_enb.c | 61 + openair2/F1AP/f1ap_cu_interface_management.c | 800 ++++--- openair2/F1AP/f1ap_cu_interface_management.h | 3 +- openair2/F1AP/f1ap_cu_rrc_message_transfer.c | 111 +- openair2/F1AP/f1ap_cu_task.c | 33 +- openair2/F1AP/f1ap_cu_ue_context_management.c | 93 +- openair2/F1AP/f1ap_decoder.c | 14 +- openair2/F1AP/f1ap_du_interface_management.c | 598 +++-- openair2/F1AP/f1ap_du_interface_management.h | 6 +- openair2/F1AP/f1ap_du_rrc_message_transfer.c | 750 +++++- openair2/F1AP/f1ap_du_rrc_message_transfer.h | 5 +- openair2/F1AP/f1ap_du_task.c | 35 +- openair2/F1AP/f1ap_du_ue_context_management.c | 113 +- openair2/F1AP/f1ap_handlers.c | 2 +- openair2/GNB_APP/L1_nr_paramdef.h | 12 +- openair2/GNB_APP/MACRLC_nr_paramdef.h | 12 + openair2/GNB_APP/RRC_nr_paramsvalues.h | 2 + openair2/GNB_APP/gnb_app.c | 128 +- openair2/GNB_APP/gnb_config.c | 549 ++++- openair2/GNB_APP/gnb_config.h | 5 + openair2/GNB_APP/gnb_paramdef.h | 9 +- openair2/LAYER2/MAC/eNB_scheduler_ulsch.c | 44 +- openair2/LAYER2/MAC/mac.h | 2 + .../NR_MAC_COMMON/nr_compute_tbs_common.c | 2 + openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 126 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 16 +- openair2/LAYER2/NR_MAC_UE/config_ue.c | 596 +++-- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 12 +- openair2/LAYER2/NR_MAC_UE/mac_proto.h | 15 +- openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 33 +- openair2/LAYER2/NR_MAC_UE/nr_l1_helpers.c | 24 +- openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 77 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 366 +-- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 361 ++- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 422 ++-- openair2/LAYER2/NR_MAC_gNB/config.c | 180 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 32 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 281 ++- .../LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 15 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 212 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 14 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 853 +++---- .../LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 86 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 319 ++- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 43 +- openair2/LAYER2/NR_MAC_gNB/main.c | 4 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 21 +- openair2/LAYER2/PROTO_AGENT/proto_agent.h | 2 + .../LAYER2/PROTO_AGENT/proto_agent_common.c | 40 +- openair2/LAYER2/RLC/rlc.h | 8 + openair2/LAYER2/nr_pdcp/nr_pdcp.h | 30 + openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c | 118 +- openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h | 21 +- openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.c | 75 + openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.h | 38 + .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c | 3 +- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 360 ++- openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c | 2 +- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 140 +- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h | 2 +- openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c | 2 +- openair2/LAYER2/rlc_v2/rlc_oai_api.c | 11 + openair2/NETWORK_DRIVER/MESH/mesh.c | 1 + openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 1 - openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c | 26 +- openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h | 10 +- openair2/RRC/LITE/rrc_common.c | 2 +- openair2/RRC/LTE/L2_interface.c | 5 +- openair2/RRC/LTE/rrc_eNB.c | 63 +- openair2/RRC/NR/L2_nr_interface.c | 55 +- openair2/RRC/NR/MESSAGES/asn1_msg.c | 613 ++++- openair2/RRC/NR/MESSAGES/asn1_msg.h | 30 +- openair2/RRC/NR/nr_rrc_defs.h | 29 +- openair2/RRC/NR/nr_rrc_proto.h | 14 + openair2/RRC/NR/rrc_gNB.c | 1057 +++++++-- openair2/RRC/NR/rrc_gNB_NGAP.c | 242 +- openair2/RRC/NR/rrc_gNB_nsa.c | 84 +- openair2/RRC/NR/rrc_gNB_reconfig.c | 28 +- openair2/RRC/NR_UE/L2_interface_ue.c | 8 +- openair2/RRC/NR_UE/rrc_UE.c | 2082 ++++++++--------- openair2/RRC/NR_UE/rrc_defs.h | 7 +- openair2/SIMULATION/NR_RRC/itti_sim.c | 15 + openair3/GTPV1-U/gtpv1u_gNB.c | 198 +- openair3/GTPV1-U/nw-gtpv1u/src/NwGtpv1u.c | 3 +- .../ESM/MSG/PduSessionEstablishRequest.c | 2 + .../ESM/MSG/PduSessionEstablishRequest.h | 1 + openair3/NAS/NR_UE/nr_nas_msg_sim.c | 231 +- openair3/NAS/NR_UE/nr_nas_msg_sim.h | 1 + openair3/NGAP/ngap_common.h | 1 - openair3/NGAP/ngap_gNB_handlers.c | 34 +- openair3/NGAP/ngap_gNB_nas_procedures.c | 2 +- openair3/UDP/udp_eNB_task.c | 4 +- openair3/ocp-gtpu/gtp_itf.cpp | 216 +- openair3/ocp-gtpu/gtp_itf.h | 4 +- targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp | 28 +- .../CONF/testing_eNB_band13.conf | 2 +- .../GENERIC-LTE-EPC/CONF/testing_enb.conf | 2 +- .../GENERIC-LTE-EPC/CONF/testing_gnb.conf | 12 +- .../PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf | 288 +++ .../PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf | 302 +++ ...b.band78.tm1.106PRB.usrpn300.gtp-itti.conf | 304 +++ .../gnb.sa.band41.fr1.106PRB.usrpb210.conf | 57 +- .../gnb.sa.band78.fr1.106PRB.usrpb210.conf | 51 +- targets/RT/USER/lte-softmodem.c | 4 +- targets/RT/USER/lte-uesoftmodem.c | 1 - 193 files changed, 12538 insertions(+), 5154 deletions(-) create mode 100644 ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpb210.conf create mode 100644 ci-scripts/xml_files/fr1_nsa_quectel_attach_detach.xml create mode 100644 ci-scripts/xml_files/fr1_nsa_quectel_long.xml create mode 100644 doc/TESTING_5GSA_setup.md create mode 100644 openair2/F1AP/dummy_enb.c create mode 100644 openair2/LAYER2/nr_pdcp/nr_pdcp.h create mode 100644 openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.c create mode 100644 openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.h mode change 100644 => 100755 openair2/RRC/NR/MESSAGES/asn1_msg.c create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf diff --git a/ci-scripts/Jenkinsfile-trig-nsa b/ci-scripts/Jenkinsfile-trig-nsa index 8f623ffb05..44527a1a38 100644 --- a/ci-scripts/Jenkinsfile-trig-nsa +++ b/ci-scripts/Jenkinsfile-trig-nsa @@ -48,21 +48,30 @@ pipeline { COMMIT_ID=COMMIT_ID.trim() echo "Testing NSA on : ${MR} ${SRC_BRANCH} ${COMMIT_ID}" //calling NSA sub job - build job: "RAN-NSA-Mini-Module", wait : false, propagate : false, parameters: [ + build job: "RAN-NSA-Mini-Module", wait : true, propagate : false, parameters: [ string(name: 'eNB_MR', value: String.valueOf(MR)), string(name: 'eNB_Branch', value: String.valueOf(SRC_BRANCH)), string(name: 'eNB_CommitID', value: String.valueOf(COMMIT_ID)), string(name: 'eNB_TargetBranch', value: String.valueOf(TARGET_BRANCH)), booleanParam(name: 'eNB_AllowMergeRequestProcess', value: Boolean.valueOf(ALLOW_MERGE)) ] - //calling Benetel sub job - //build job: "RAN-CI-BENETEL", wait : false, propagate : false, parameters: [ - // string(name: 'eNB_MR', value: String.valueOf(MR)), - // string(name: 'eNB_Branch', value: String.valueOf(SRC_BRANCH)), - // string(name: 'eNB_CommitID', value: String.valueOf(COMMIT_ID)), - // string(name: 'eNB_TargetBranch', value: String.valueOf(TARGET_BRANCH)), - // booleanParam(name: 'eNB_AllowMergeRequestProcess', value: Boolean.valueOf(ALLOW_MERGE)) - //] + //calling NSA long sub job + build job: "RAN-NSA-Mini-Module-Long", wait : true, propagate : false, parameters: [ + string(name: 'eNB_MR', value: String.valueOf(MR)), + string(name: 'eNB_Branch', value: String.valueOf(SRC_BRANCH)), + string(name: 'eNB_CommitID', value: String.valueOf(COMMIT_ID)), + string(name: 'eNB_TargetBranch', value: String.valueOf(TARGET_BRANCH)), + booleanParam(name: 'eNB_AllowMergeRequestProcess', value: Boolean.valueOf(ALLOW_MERGE)) + ] + //calling NSA attach/detach job + build job: "RAN-NSA-Mini-Module-Attach-Detach", wait : true, propagate : false, parameters: [ + string(name: 'eNB_MR', value: String.valueOf(MR)), + string(name: 'eNB_Branch', value: String.valueOf(SRC_BRANCH)), + string(name: 'eNB_CommitID', value: String.valueOf(COMMIT_ID)), + string(name: 'eNB_TargetBranch', value: String.valueOf(TARGET_BRANCH)), + booleanParam(name: 'eNB_AllowMergeRequestProcess', value: Boolean.valueOf(ALLOW_MERGE)) + ] + } } } diff --git a/ci-scripts/cls_module_ue.py b/ci-scripts/cls_module_ue.py index cb5c81e271..75b628fd1f 100644 --- a/ci-scripts/cls_module_ue.py +++ b/ci-scripts/cls_module_ue.py @@ -50,7 +50,8 @@ class Module_UE: setattr(self, k, v) self.UEIPAddress = "" #dictionary linking command names and related module scripts - self.cmd_dict= {"wup": self.WakeupScript,"detach":self.DetachScript}#dictionary of function scripts + self.cmd_dict= {"wup": self.WakeupScript,"detach":self.DetachScript}#dictionary of function scripts + self.ue_trace='' @@ -75,9 +76,10 @@ class Module_UE: logging.debug('Starting ' + self.Process['Name']) mySSH = sshconnection.SSHConnection() mySSH.open(self.HostIPAddress, self.HostUsername, self.HostPassword) - mySSH.command('echo ' + self.HostPassword + ' | sudo -S ' + self.Process['Cmd'] + ' &','\$',5) + mySSH.command('echo $USER; echo ' + self.HostPassword + ' | nohup sudo -S ' + self.Process['Cmd'] + ' &','\$',5) mySSH.close() #checking the process + time.sleep(5) HOST=self.HostIPAddress COMMAND="ps aux | grep " + self.Process['Name'] + " | grep -v grep " logging.debug(COMMAND) @@ -130,13 +132,14 @@ class Module_UE: return -1 def EnableTrace(self): - mySSH = sshconnection.SSHConnection() - mySSH.open(self.HostIPAddress, self.HostUsername, self.HostPassword) - #delete old artifacts - mySSH.command('echo ' + self.HostPassword + ' | sudo -S rm -rf ci_qlog','\$',5) - #start Trace, artifact is created in home dir - mySSH.command('echo $USER; nohup sudo -E QLog/QLog -s ci_qlog -f NR5G.cfg &','\$', 5) - mySSH.close() + if self.ue_trace=="yes": + mySSH = sshconnection.SSHConnection() + mySSH.open(self.HostIPAddress, self.HostUsername, self.HostPassword) + #delete old artifacts + mySSH.command('echo ' + self.HostPassword + ' | sudo -S rm -rf ci_qlog','\$',5) + #start Trace, artifact is created in home dir + mySSH.command('echo $USER; nohup sudo -E QLog/QLog -s ci_qlog -f NR5G.cfg &','\$', 5) + mySSH.close() def DisableTrace(self): mySSH = sshconnection.SSHConnection() @@ -153,18 +156,20 @@ class Module_UE: def LogCollect(self): - mySSH = sshconnection.SSHConnection() - mySSH.open(self.HostIPAddress, self.HostUsername, self.HostPassword) - #archive qlog to USB stick in /media/usb-drive/ci_qlogs with datetime suffix - now=datetime.now() - now_string = now.strftime("%Y%m%d-%H%M") - source='ci_qlog' - destination='/media/usb-drive/ci_qlogs/ci_qlog_'+now_string+'.zip' - #qlog artifact is zipped into the target folder - mySSH.command('echo $USER; echo ' + self.HostPassword + ' | nohup sudo -S zip -r '+destination+' '+source+' &','\$', 10) - mySSH.close() - #post action : log cleaning to make sure enough space is reserved for the next run - Log_Mgt=cls_log_mgt.Log_Mgt(self.HostIPAddress, self.HostPassword, "/media/usb-drive/ci_qlogs") - Log_Mgt.LogRotation() - + if self.ue_trace=="yes": + mySSH = sshconnection.SSHConnection() + mySSH.open(self.HostIPAddress, self.HostUsername, self.HostPassword) + #archive qlog to USB stick in /media/usb-drive/ci_qlogs with datetime suffix + now=datetime.now() + now_string = now.strftime("%Y%m%d-%H%M") + source='ci_qlog' + destination='/media/usb-drive/ci_qlogs/ci_qlog_'+now_string+'.zip' + #qlog artifact is zipped into the target folder + mySSH.command('echo $USER; echo ' + self.HostPassword + ' | nohup sudo -S zip -r '+destination+' '+source+' &','\$', 10) + mySSH.close() + #post action : log cleaning to make sure enough space is reserved for the next run + Log_Mgt=cls_log_mgt.Log_Mgt(self.HostIPAddress, self.HostPassword, "/media/usb-drive/ci_qlogs") + Log_Mgt.LogRotation() + else: + destination="" return destination diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index d3d1d70c13..377592781d 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -160,6 +160,7 @@ class OaiCiTest(): self.air_interface='' self.expectedNbOfConnectedUEs = 0 self.ue_id = '' #used for module identification + self.ue_trace ='' #used to enable QLog trace for Module UE, passed to Module UE object at InitializeUE() def BuildOAIUE(self,HTML): @@ -367,7 +368,7 @@ class OaiCiTest(): except: os.kill(os.getppid(),signal.SIGUSR1) - def InitializeUE(self,HTML,RAN,EPC, COTS_UE, InfraUE): + def InitializeUE(self,HTML,RAN,EPC, COTS_UE, InfraUE,ue_trace): if self.ue_id=='':#no ID specified, then it is a COTS controlled by ADB if self.ADBIPAddress == '' or self.ADBUserName == '' or self.ADBPassword == '': HELP.GenericHelp(CONST.Version) @@ -386,6 +387,7 @@ class OaiCiTest(): else: #if an ID is specified, it is a module from the yaml infrastructure file #RH Module_UE = cls_module_ue.Module_UE(InfraUE.ci_ue_infra[self.ue_id]) + Module_UE.ue_trace=ue_trace is_module=Module_UE.CheckCMProcess() if is_module: Module_UE.EnableTrace() @@ -586,7 +588,7 @@ class OaiCiTest(): SSH.command('ifconfig oaitun_ue1', '\$', 4) SSH.command('ifconfig oaitun_ue1', '\$', 4) # ifconfig output is different between ubuntu 16 and ubuntu 18 - result = re.search('inet addr:1|inet 1', SSH.getBefore()) + result = re.search('inet addr:[0-9]|inet [0-9]', SSH.getBefore()) if result is not None: logging.debug('\u001B[1m oaitun_ue1 interface is mounted and configured\u001B[0m') tunnelInterfaceStatus = True @@ -994,38 +996,55 @@ class OaiCiTest(): for job in multi_jobs: job.join() - if (status_queue.empty()): - HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ALL_PROCESSES_OK) - self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE,EPC,InfraUE) - return - else: - attach_status = True - html_queue = SimpleQueue() - while (not status_queue.empty()): - count = status_queue.get() - if (count < 0): - attach_status = False - device_id = status_queue.get() - message = status_queue.get() - if (count < 0): - html_cell = '
UE (' + device_id + ')\n' + message + '
' - else: - html_cell = '
UE (' + device_id + ')\n' + message + ' in ' + str(count + 2) + ' seconds
' - html_queue.put(html_cell) - if (attach_status): - cnt = 0 - while cnt < len(self.UEDevices): - if self.UEDevicesStatus[cnt] == CONST.UE_STATUS_ATTACHING: - self.UEDevicesStatus[cnt] = CONST.UE_STATUS_ATTACHED - cnt += 1 - HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) - result = re.search('T_stdout', str(RAN.Initialize_eNB_args)) - if result is not None: - logging.debug('Waiting 5 seconds to fill up record file') - time.sleep(5) - else: - HTML.CreateHtmlTestRowQueue('N/A', 'KO', len(self.UEDevices), html_queue) + if (status_queue.empty()): + HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ALL_PROCESSES_OK) self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE,EPC,InfraUE) + return + else: + attach_status = True + html_queue = SimpleQueue() + while (not status_queue.empty()): + count = status_queue.get() + if (count < 0): + attach_status = False + device_id = status_queue.get() + message = status_queue.get() + if (count < 0): + html_cell = '
UE (' + device_id + ')\n' + message + '
' + else: + html_cell = '
UE (' + device_id + ')\n' + message + ' in ' + str(count + 2) + ' seconds
' + html_queue.put(html_cell) + if (attach_status): + cnt = 0 + while cnt < len(self.UEDevices): + if self.UEDevicesStatus[cnt] == CONST.UE_STATUS_ATTACHING: + self.UEDevicesStatus[cnt] = CONST.UE_STATUS_ATTACHED + cnt += 1 + HTML.CreateHtmlTestRowQueue('N/A', 'OK', len(self.UEDevices), html_queue) + result = re.search('T_stdout', str(RAN.Initialize_eNB_args)) + if result is not None: + logging.debug('Waiting 5 seconds to fill up record file') + time.sleep(5) + else: + HTML.CreateHtmlTestRowQueue('N/A', 'KO', len(self.UEDevices), html_queue) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE,EPC,InfraUE) + + else: #if an ID is specified, it is a module from the yaml infrastructure file + #Attention, as opposed to InitializeUE, the connect manager process is not checked as it is supposed to be active already + #only 1- module wakeup, 2- check IP address + Module_UE = cls_module_ue.Module_UE(InfraUE.ci_ue_infra[self.ue_id]) + Module_UE.Command("wup") + logging.debug("Waiting for IP address to be assigned") + time.sleep(20) + logging.debug("Retrieve IP address") + status=Module_UE.GetModuleIPAddress() + if status==0: + HTML.CreateHtmlTestRow(Module_UE.UEIPAddress, 'OK', CONST.ALL_PROCESSES_OK) + logging.debug('UE IP addresss : '+ Module_UE.UEIPAddress) + else: #status==-1 failed to retrieve IP address + HTML.CreateHtmlTestRow('N/A', 'KO', CONST.UE_IP_ADDRESS_ISSUE) + self.AutoTerminateUEandeNB(HTML,RAN,COTS_UE,EPC,InfraUE) + return def DetachUE_common(self, device_id, idx,COTS_UE): try: @@ -1084,10 +1103,7 @@ class OaiCiTest(): else:#if an ID is specified, it is a module from the yaml infrastructure file Module_UE = cls_module_ue.Module_UE(InfraUE.ci_ue_infra[self.ue_id]) Module_UE.Command("detach") - Module_UE.DisableTrace() - Module_UE.DisableCM() - archive_destination=Module_UE.LogCollect() - HTML.CreateHtmlTestRow('QLog at : '+archive_destination, 'OK', CONST.ALL_PROCESSES_OK) + HTML.CreateHtmlTestRow('NA', 'OK', CONST.ALL_PROCESSES_OK) @@ -2248,7 +2264,7 @@ class OaiCiTest(): SSH.copyin(EPC.IPAddress, EPC.UserName, EPC.Password, 'iperf_server_' + self.testCase_id + '_' + self.ue_id + '.log', '.') #send for analysis filename='iperf_client_' + self.testCase_id + '_' + self.ue_id + '.log' - self.Iperf_analyzeV2Server(lock, UE_IPAddress, device_id, statusQueue, self.iperf_args,filename,1) + self.Iperf_analyzeV2Server(lock, UE_IPAddress, device_id, statusQueue, self.iperf_args,filename,1) else : logging.debug("Incorrect or missing IPERF direction in XML") @@ -3088,20 +3104,32 @@ class OaiCiTest(): except: os.kill(os.getppid(),signal.SIGUSR1) - def TerminateUE(self,HTML,COTS_UE): - terminate_ue_flag = False - self.GetAllUEDevices(terminate_ue_flag) - multi_jobs = [] - i = 0 - for device_id in self.UEDevices: - p = Process(target= self.TerminateUE_common, args = (device_id,i,COTS_UE,)) - p.daemon = True - p.start() - multi_jobs.append(p) - i += 1 - for job in multi_jobs: - job.join() - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + def TerminateUE(self,HTML,COTS_UE,InfraUE,ue_trace): + if self.ue_id=='':#no ID specified, then it is a COTS controlled by ADB + terminate_ue_flag = False + self.GetAllUEDevices(terminate_ue_flag) + multi_jobs = [] + i = 0 + for device_id in self.UEDevices: + p = Process(target= self.TerminateUE_common, args = (device_id,i,COTS_UE,)) + p.daemon = True + p.start() + multi_jobs.append(p) + i += 1 + for job in multi_jobs: + job.join() + HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) + else: #if an ID is specified, it is a module from the yaml infrastructure file + Module_UE = cls_module_ue.Module_UE(InfraUE.ci_ue_infra[self.ue_id]) + Module_UE.ue_trace=ue_trace + Module_UE.Command("detach") + Module_UE.DisableTrace() + Module_UE.DisableCM() + archive_destination=Module_UE.LogCollect() + if Module_UE.ue_trace=='yes': + HTML.CreateHtmlTestRow('QLog at : '+archive_destination, 'OK', CONST.ALL_PROCESSES_OK) + else: + HTML.CreateHtmlTestRow('QLog trace is disabled', 'OK', CONST.ALL_PROCESSES_OK) def TerminateOAIUE(self,HTML,RAN,COTS_UE,EPC, InfraUE): SSH = sshconnection.SSHConnection() @@ -3158,20 +3186,13 @@ class OaiCiTest(): HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) def AutoTerminateUEandeNB(self,HTML,RAN,COTS_UE,EPC,InfraUE): - if self.ue_id!='': - Module_UE = cls_module_ue.Module_UE(InfraUE.ci_ue_infra[self.ue_id]) - Module_UE.Command("detach") - Module_UE.DisableTrace() - Module_UE.DisableCM() - archive_destination=Module_UE.LogCollect() - HTML.CreateHtmlTestRow('QLog at : '+archive_destination, 'OK', CONST.ALL_PROCESSES_OK) if (self.ADBIPAddress != 'none'): self.testCase_id = 'AUTO-KILL-UE' HTML.testCase_id=self.testCase_id self.desc = 'Automatic Termination of UE' HTML.desc='Automatic Termination of UE' self.ShowTestID() - self.TerminateUE(HTML,COTS_UE) + self.TerminateUE(HTML,COTS_UE,InfraUE,self.ue_trace) if (self.Initialize_OAI_UE_args != ''): self.testCase_id = 'AUTO-KILL-OAI-UE' HTML.testCase_id=self.testCase_id diff --git a/ci-scripts/conf_files/enb.band7.tm1.fr1.25PRB.usrpb210.conf b/ci-scripts/conf_files/enb.band7.tm1.fr1.25PRB.usrpb210.conf index 056c4bdb6a..fff3cd1b9a 100644 --- a/ci-scripts/conf_files/enb.band7.tm1.fr1.25PRB.usrpb210.conf +++ b/ci-scripts/conf_files/enb.band7.tm1.fr1.25PRB.usrpb210.conf @@ -244,7 +244,7 @@ MACRLCs = ( THREAD_STRUCT = ( { parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; - worker_config = "ENABLE"; + worker_config = "WORKER_ENABLE"; } ); diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpb210.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpb210.conf new file mode 100644 index 0000000000..494be845a6 --- /dev/null +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpb210.conf @@ -0,0 +1,297 @@ +Active_gNBs = ( "gNB-Eurecom-DU"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-Eurecom-DU"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 222; + mnc = 01; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); + + }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + pusch_AntennaPorts = 1; + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 0; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641280; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640008; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 12952; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + #pdsch-ConfigCommon + #pdschTimeDomainAllocationList (up to 16 entries) + initialDLBWPk0_0 = 0; #for DL slot + initialDLBWPmappingType_0 = 0; #0=typeA,1=typeB + initialDLBWPstartSymbolAndLength_0 = 40; #this is SS=1,L=13 + + initialDLBWPk0_1 = 0; #for mixed slot + initialDLBWPmappingType_1 = 0; + initialDLBWPstartSymbolAndLength_1 = 57; #this is SS=1,L=5 + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 12952; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + # pusch-ConfigCommon (up to 16 elements) + initialULBWPk2_0 = 6; # used for UL slot + initialULBWPmappingType_0 = 1 + initialULBWPstartSymbolAndLength_0 = 41; # this is SS=0 L=13 + + initialULBWPk2_1 = 6; # used for mixed slot + initialULBWPmappingType_1 = 1; + initialULBWPstartSymbolAndLength_1 = 69; # this is SS=0 L=12 + + initialULBWPk2_2 = 7; # used for Msg.3 during RA + initialULBWPmappingType_2 = 1; + initialULBWPstartSymbolAndLength_2 = 52; # this is SS=10 L=4 + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// MME parameters: + amf_ip_address = ( { ipv4 = "CI_MME_IP_ADDR"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "eth0"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "CI_GNB_IP_ADDR"; + GNB_INTERFACE_NAME_FOR_NGU = "eth0"; + GNB_IPV4_ADDRESS_FOR_NGU = "CI_GNB_IP_ADDR"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + } +); + +L1s = ( + { + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + } +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [7]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + #beamforming 1x4 matrix: + bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000]; + clock_src = "internal"; + } +); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + +rfsimulator : +{ + serveraddr = "server"; + serverport = "4043"; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; +}; + + log_config : + { + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + ngap_log_level ="debug"; + ngap_log_verbosity ="medium"; + }; + diff --git a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf index 5838e96999..205922af0c 100644 --- a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf +++ b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf @@ -120,11 +120,11 @@ gNBs = # 0=unrestricted, 1=restricted type A, 2=restricted type B restrictedSetConfig = 0, # pusch-ConfigCommon (up to 16 elements) - initialULBWPk2_0 = 2; # used for UL slot + initialULBWPk2_0 = 6; # used for UL slot initialULBWPmappingType_0 = 1 initialULBWPstartSymbolAndLength_0 = 41; # this is SS=0 L=13 - initialULBWPk2_1 = 2; # used for mixed slot + initialULBWPk2_1 = 6; # used for mixed slot initialULBWPmappingType_1 = 1; initialULBWPstartSymbolAndLength_1 = 69; # this is SS=10 L=2 diff --git a/ci-scripts/main.py b/ci-scripts/main.py index cbd323366b..0718ee6396 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -197,6 +197,7 @@ def GetParametersFromXML(action): elif action == 'Initialize_UE': ue_id = test.findtext('id') + CiTestObj.ue_trace=test.findtext('UE_Trace')#temporary variable, to be passed to Module_UE in Initialize_UE call if (ue_id is None): CiTestObj.ue_id = "" else: @@ -210,12 +211,25 @@ def GetParametersFromXML(action): CiTestObj.ue_id = ue_id elif action == 'Attach_UE': + ue_id = test.findtext('id') + if (ue_id is None): + CiTestObj.ue_id = "" + else: + CiTestObj.ue_id = ue_id nbMaxUEtoAttach = test.findtext('nbMaxUEtoAttach') if (nbMaxUEtoAttach is None): CiTestObj.nbMaxUEtoAttach = -1 else: CiTestObj.nbMaxUEtoAttach = int(nbMaxUEtoAttach) + elif action == 'Terminate_UE': + ue_id = test.findtext('id') + if (ue_id is None): + CiTestObj.ue_id = "" + else: + CiTestObj.ue_id = ue_id + + elif action == 'CheckStatusUE': expectedNBUE = test.findtext('expectedNbOfConnectedUEs') if (expectedNBUE is None): @@ -469,7 +483,7 @@ elif re.match('^TerminateUE$', mode, re.IGNORECASE): HELP.GenericHelp(CONST.Version) sys.exit('Insufficient Parameter') signal.signal(signal.SIGUSR1, receive_signal) - CiTestObj.TerminateUE(HTML,COTS_UE) + CiTestObj.TerminateUE(HTML,COTS_UE,InfraUE,CiTestObj.ue_trace) elif re.match('^TerminateOAIUE$', mode, re.IGNORECASE): if CiTestObj.UEIPAddress == '' or CiTestObj.UEUserName == '' or CiTestObj.UEPassword == '': HELP.GenericHelp(CONST.Version) @@ -714,9 +728,9 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re elif action == 'Terminate_eNB': RAN.TerminateeNB(HTML, EPC) elif action == 'Initialize_UE': - CiTestObj.InitializeUE(HTML,RAN, EPC, COTS_UE, InfraUE) + CiTestObj.InitializeUE(HTML,RAN, EPC, COTS_UE, InfraUE, CiTestObj.ue_trace) elif action == 'Terminate_UE': - CiTestObj.TerminateUE(HTML,COTS_UE) + CiTestObj.TerminateUE(HTML,COTS_UE, InfraUE, CiTestObj.ue_trace) elif action == 'Attach_UE': CiTestObj.AttachUE(HTML,RAN,EPC,COTS_UE,InfraUE) elif action == 'Detach_UE': diff --git a/ci-scripts/reportTestLocally.sh b/ci-scripts/reportTestLocally.sh index 105c95786f..7290507b4e 100755 --- a/ci-scripts/reportTestLocally.sh +++ b/ci-scripts/reportTestLocally.sh @@ -748,6 +748,82 @@ function report_test { echo " Statistics" >> ./test_simulator_results.html echo " " >> ./test_simulator_results.html + #SA + EPC_CONFIGS=("noS1") + TRANS_MODES=("tdd") + FR_MODE=("SA") + BW_CASES=(106) + for CN_CONFIG in ${EPC_CONFIGS[@]} + do + for TMODE in ${TRANS_MODES[@]} + do + for BW in ${BW_CASES[@]} + do + echo " " >> ./test_simulator_results.html + if [[ $CN_CONFIG =~ .*wS1.* ]] + then + echo " Test with EPC (aka withS1): ${TMODE} -- ${BW}PRB -- ${FR_MODE}" >> ./test_simulator_results.html + else + echo " Test without EPC (aka noS1): ${TMODE} -- ${BW}PRB -- ${FR_MODE}" >> ./test_simulator_results.html + fi + echo " " >> ./test_simulator_results.html + + + #SA test (--sa option) + + SA_ENB_LOG=$ARCHIVES_LOC/${TMODE}_${BW}prb_${CN_CONFIG}_gnb_sa_test.log + SA_UE_LOG=$ARCHIVES_LOC/${TMODE}_${BW}prb_${CN_CONFIG}_ue_sa_test.log + if [ -f $RA_ENB_LOG ] && [ -f $RA_UE_LOG ] + then + #get rid of full path + NAME_ENB=`echo $SA_ENB_LOG | sed -e "s#$ARCHIVES_LOC/##"` + NAME_UE=`echo $SA_UE_LOG | sed -e "s#$ARCHIVES_LOC/##"` + echo " " >> ./test_simulator_results.html + echo " $NAME_ENB --- $NAME_UE" >> ./test_simulator_results.html + echo " Check if SA proc succeeded" >> ./test_simulator_results.html + + + RRC_CHECK=`egrep -c "Received rrcSetupComplete" $SA_ENB_LOG` + CBRA_CHECK=`egrep -c "Received Ack of RA-Msg4\. CBRA procedure succeeded" $SA_ENB_LOG` + SIB1_CHECK=`egrep -c "SIB1 decoded" $SA_UE_LOG` + + + if [ $RRC_CHECK -gt 0 ] && [ $CBRA_CHECK -gt 0 ] && [ $SIB1_CHECK -gt 0 ] + then + echo " OK" >> ./test_simulator_results.html + else + echo " KO" >> ./test_simulator_results.html + fi + + echo "
" >> ./test_simulator_results.html
+                    if [ $RRC_CHECK -gt 0 ]
+                    then
+                        echo "- Received rrcSetupComplete OK" >> ./test_simulator_results.html
+                    else
+                        echo "- Received rrcSetupComplete KO" >> ./test_simulator_results.html
+                    fi
+                    if [ $CBRA_CHECK -gt 0 ]
+                    then
+                        echo "- CBRA procedure succeeded OK" >> ./test_simulator_results.html
+                    else
+                        echo "- CBRA procedure succeeded KO" >> ./test_simulator_results.html
+                    fi
+                    if [ $SIB1_CHECK -gt 0 ]
+                    then
+                        echo "- SIB1 decoded OK" >> ./test_simulator_results.html
+                    else
+                        echo "- SIB1 decoded KO" >> ./test_simulator_results.html
+                    fi
+
+                    echo "        
" >> ./test_simulator_results.html + echo " " >> ./test_simulator_results.html + fi + + done + done + done + + EPC_CONFIGS=("noS1") TRANS_MODES=("tdd") FR_MODE=("FR2") diff --git a/ci-scripts/runTestOnVM.sh b/ci-scripts/runTestOnVM.sh index b9a1adb6af..058d64b0ba 100755 --- a/ci-scripts/runTestOnVM.sh +++ b/ci-scripts/runTestOnVM.sh @@ -277,6 +277,46 @@ function check_ping_result { } +function check_sa_result { + local LOC_GNB_LOG=$1 + local LOC_UE_LOG=$2 + + #if log files exist + if [ -f $LOC_GNB_LOG ] && [ -f $LOC_UE_LOG ] + then + + #gNB SA test + #console check + echo "Checking gNB Log for SA success" + egrep "Received rrcSetupComplete" $1 + egrep "Received Ack of RA-Msg4\. CBRA procedure succeeded" $1 + + #script check + local RRC_CHECK=`egrep -c "Received rrcSetupComplete" $1` + local CBRA_CHECK=`egrep -c "Received Ack of RA-Msg4\. CBRA procedure succeeded" $1` + + #UE SA test + #console check + echo 'Checking UE Log for SA success' + egrep "SIB1 decoded" $2 + #script check + local SIB1_CHECK=`egrep -c "SIB1 decoded" $2` + + #generate status + if [ $RRC_CHECK -eq 0 ] || [ $CBRA_CHECK -eq 0 ] || [ $SIB1_CHECK -eq 0 ] + then + SA_STATUS=-1 + echo "SA test FAILED, could not find the markers" + fi + #case where log files do not exist + else + echo "SA test log files not present" + SA_STATUS=-1 + fi + +} + + function check_ra_result { local LOC_GNB_LOG=$1 local LOC_UE_LOG=$2 @@ -1215,8 +1255,8 @@ function start_rf_sim_gnb { local LOC_CONF_FILE=$5 # 1 is with S1 and 0 without S1 aka noS1 local LOC_S1_CONFIGURATION=$6 - #LOC_RA_TEST=1 will run the RA test check - local LOC_RA_TEST=$7 + #LOC_RA_SA_TEST=1 will run the RA test check ; =2 will run the SA test + local LOC_RA_SA_TEST=$7 if [ -e rbconfig.raw ]; then rm -f rbconfig.raw; fi if [ -e reconfig.raw ]; then rm -f reconfig.raw; fi @@ -1239,11 +1279,14 @@ function start_rf_sim_gnb { echo "sudo rm -f r*config.raw" >> $1 if [ $LOC_S1_CONFIGURATION -eq 0 ] then - if [ $LOC_RA_TEST -eq 0 ] #no RA test => use --phy-test option + if [ $LOC_RA_SA_TEST -eq 0 ] #no RA test => use --phy-test option then echo "echo \"./nr-softmodem -O /home/ubuntu/tmp/ci-scripts/conf_files/ci-$LOC_CONF_FILE --log_config.global_log_options level,nocolor --parallel-config PARALLEL_SINGLE_THREAD --noS1 --nokrnmod 1 --rfsim --phy-test --lowmem --noS1\" > ./my-nr-softmodem-run.sh " >> $1 - else #RA test => use --do-ra option + elif [ $LOC_RA_SA_TEST -eq 1 ] #RA test => use --do-ra option + then echo "echo \"./nr-softmodem -O /home/ubuntu/tmp/ci-scripts/conf_files/ci-$LOC_CONF_FILE --log_config.global_log_options level,nocolor --parallel-config PARALLEL_SINGLE_THREAD --rfsim --do-ra --lowmem --noS1\" > ./my-nr-softmodem-run.sh " >> $1 + else #SA test => use --sa option + echo "echo \"./nr-softmodem -O /home/ubuntu/tmp/ci-scripts/conf_files/ci-$LOC_CONF_FILE --log_config.global_log_options level,nocolor --parallel-config PARALLEL_SINGLE_THREAD --rfsim --sa --lowmem \" > ./my-nr-softmodem-run.sh " >> $1 fi fi echo "chmod 775 ./my-nr-softmodem-run.sh" >> $1 @@ -1279,7 +1322,7 @@ function start_rf_sim_gnb { fi # check noS1 config only outside RA test (as it does not support noS1) - if [ $LOC_S1_CONFIGURATION -eq 0 ] && [ $LOC_RA_TEST -eq 0 ] + if [ $LOC_S1_CONFIGURATION -eq 0 ] && [ $LOC_RA_SA_TEST -eq 0 ] then echo "ifconfig oaitun_enb1 | egrep -c \"inet addr\"" > $1 # Checking oaitun_enb1 interface has now an IP address @@ -1324,8 +1367,8 @@ function start_rf_sim_nr_ue { local LOC_FREQUENCY=$6 # 1 is with S1 and 0 without S1 aka noS1 local LOC_S1_CONFIGURATION=$7 - #LOC_RA_TEST=1 will run the RA test check - local LOC_RA_TEST=$8 + #LOC_RA_SA_TEST=1 will run the RA test check ; =2 will run the SA test + local LOC_RA_SA_TEST=$8 # Copy the RAW files from the gNB run scp -o StrictHostKeyChecking=no rbconfig.raw ubuntu@$LOC_NR_UE_VM_IP_ADDR:/home/ubuntu/tmp @@ -1342,11 +1385,14 @@ function start_rf_sim_nr_ue { echo "cd /home/ubuntu/tmp/cmake_targets/ran_build/build/" >> $1 if [ $LOC_S1_CONFIGURATION -eq 0 ] then - if [ $LOC_RA_TEST -eq 0 ] #no RA test => use --phy-test option + if [ $LOC_RA_SA_TEST -eq 0 ] #no RA test => use --phy-test option then echo "echo \"./nr-uesoftmodem --nokrnmod 1 --rfsim --phy-test --rrc_config_path /home/ubuntu/tmp/cmake_targets/ran_build/build/ --log_config.global_log_options level,nocolor --noS1\" > ./my-nr-softmodem-run.sh " >> $1 - else #RA test => use --do-ra option + elif [ $LOC_RA_SA_TEST -eq 1 ] #RA test => use --do-ra option + then echo "echo \"./nr-uesoftmodem --rfsim --do-ra --log_config.global_log_options level,nocolor --rrc_config_path /home/ubuntu/tmp/cmake_targets/ran_build/build/\" > ./my-nr-softmodem-run.sh " >> $1 + else #SA test => use --sa option + echo "echo \"./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --log_config.global_log_options level,nocolor\" > ./my-nr-softmodem-run.sh " >> $1 fi fi echo "chmod 775 ./my-nr-softmodem-run.sh" >> $1 @@ -1381,7 +1427,7 @@ function start_rf_sim_nr_ue { echo "RF-SIM NR-UE is sync'ed w/ gNB" fi # Checking oaitun_ue1 interface has now an IP address (only outside RA test) - if [ $LOC_RA_TEST -eq 0 ] + if [ $LOC_RA_SA_TEST -eq 0 ] then i="0" echo "ifconfig oaitun_ue1 | egrep -c \"inet addr\"" > $1 @@ -2195,6 +2241,88 @@ function run_test_on_vm { if [[ "$RUN_OPTIONS" == "complex" ]] && [[ $VM_NAME =~ .*-rf-sim.* ]] then + echo "############################################################" + echo "SA TEST" + echo "############################################################" + #SA test, attention : has a different config file from the rest of the test + CN_CONFIG="noS1" + CONF_FILE=gnb.band78.sa.fr1.106PRB.usrpb210.conf + S1_NOS1_CFG=0 + PRB=106 + FREQUENCY=3510 + + if [ ! -d $ARCHIVES_LOC ] + then + mkdir --parents $ARCHIVES_LOC + fi + + local try_cnt=0 + NR_STATUS=0 + + ######### start of SA TEST loop + while [ $try_cnt -lt 5 ] #5 because it hardly succeed within CI + do + + SYNC_STATUS=0 + SA_STATUS=0 + rm -f $ARCHIVES_LOC/tdd_${PRB}prb_${CN_CONFIG}*sa_test.log + + echo "############################################################" + echo "${CN_CONFIG} : Starting the gNB" + echo "############################################################" + CURRENT_GNB_LOG_FILE=tdd_${PRB}prb_${CN_CONFIG}_gnb_sa_test.log + #last argument = 2 is to enable --sa for SA test + start_rf_sim_gnb $GNB_VM_CMDS "$GNB_VM_IP_ADDR" $CURRENT_GNB_LOG_FILE $PRB $CONF_FILE $S1_NOS1_CFG 2 + + echo "############################################################" + echo "${CN_CONFIG} : Starting the NR-UE" + echo "############################################################" + CURRENT_NR_UE_LOG_FILE=tdd_${PRB}prb_${CN_CONFIG}_ue_sa_test.log + #last argument = 2 is to enable --sa for SA test + start_rf_sim_nr_ue $NR_UE_VM_CMDS $NR_UE_VM_IP_ADDR $GNB_VM_IP_ADDR $CURRENT_NR_UE_LOG_FILE $PRB $FREQUENCY $S1_NOS1_CFG 2 + if [ $NR_UE_SYNC -eq 0 ] + then + echo "Problem w/ gNB and NR-UE not syncing" + terminate_enb_ue_basic_sim $NR_UE_VM_CMDS $NR_UE_VM_IP_ADDR 2 + terminate_enb_ue_basic_sim $GNB_VM_CMDS $GNB_VM_IP_ADDR 1 + scp -o StrictHostKeyChecking=no ubuntu@$GNB_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_GNB_LOG_FILE $ARCHIVES_LOC + scp -o StrictHostKeyChecking=no ubuntu@$NR_UE_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_NR_UE_LOG_FILE $ARCHIVES_LOC + SYNC_STATUS=-1 + try_cnt=$((try_cnt+1)) + continue + fi + + echo "############################################################" + echo "${CN_CONFIG} : Terminate gNB/NR-UE simulators" + echo "############################################################" + sleep 20 + terminate_enb_ue_basic_sim $NR_UE_VM_CMDS $NR_UE_VM_IP_ADDR 2 + terminate_enb_ue_basic_sim $GNB_VM_CMDS $GNB_VM_IP_ADDR 1 + scp -o StrictHostKeyChecking=no ubuntu@$GNB_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_GNB_LOG_FILE $ARCHIVES_LOC + scp -o StrictHostKeyChecking=no ubuntu@$NR_UE_VM_IP_ADDR:/home/ubuntu/tmp/cmake_targets/log/$CURRENT_NR_UE_LOG_FILE $ARCHIVES_LOC + + #check SA markers in gNB and NR UE log files + echo "############################################################" + echo "${CN_CONFIG} : Checking SA on gNB / NR-UE" + echo "############################################################" + + # Proper check to be done when SA test is working! + check_sa_result $ARCHIVES_LOC/$CURRENT_GNB_LOG_FILE $ARCHIVES_LOC/$CURRENT_NR_UE_LOG_FILE + if [ $SA_STATUS -ne 0 ] + then + echo "SA test NOT OK" + echo "try_cnt = " $try_cnt + try_cnt=$((try_cnt+1)) + else + try_cnt=$((try_cnt+10)) + fi + done + ########### end SA test + + sleep 30 + + + echo "############################################################" echo "RA TEST FR2" echo "############################################################" @@ -2499,6 +2627,7 @@ function run_test_on_vm { echo "Checking run status" echo "############################################################" + if [ $SA_STATUS -ne 0 ]; then NR_STATUS=-1; fi if [ $RA_FR2_STATUS -ne 0 ]; then NR_STATUS=-1; fi if [ $RA_STATUS -ne 0 ]; then NR_STATUS=-1; fi if [ $SYNC_STATUS -ne 0 ]; then NR_STATUS=-1; fi diff --git a/ci-scripts/xml_files/fr1_nsa_quectel.xml b/ci-scripts/xml_files/fr1_nsa_quectel.xml index 2e8c07968d..7ec3a5f140 100644 --- a/ci-scripts/xml_files/fr1_nsa_quectel.xml +++ b/ci-scripts/xml_files/fr1_nsa_quectel.xml @@ -48,12 +48,13 @@ Initialize_UE Initialize Quectel idefix + yes - Detach_UE - Detach UE + Terminate_UE + Terminate Quectel idefix diff --git a/ci-scripts/xml_files/fr1_nsa_quectel_attach_detach.xml b/ci-scripts/xml_files/fr1_nsa_quectel_attach_detach.xml new file mode 100644 index 0000000000..2263f731c5 --- /dev/null +++ b/ci-scripts/xml_files/fr1_nsa_quectel_attach_detach.xml @@ -0,0 +1,168 @@ + + + TEST-NSA-FR1-TM1 + NSA Ping Attach Detach with QUECTEL + tasks + 1 + + 030000 + 040000 + 000002 + 010000 + 000001 + 050000 + 000001 + 010004 + 000001 + 010003 + 000001 + 050000 + 000001 + 010004 + 000001 + 010003 + 000001 + 050000 + 000001 + 010002 + 080001 + 080000 + + + + + Initialize_UE + Initialize Quectel + idefix + yes + + + + + Terminate_UE + Terminate Quectel + idefix + + + + Attach_UE + Attach Quectel + idefix + + + + Detach_UE + Detach Quectel + idefix + + + + + Initialize_eNB + Initialize eNB + -O ci-scripts/conf_files/enb.band7.tm1.fr1.25PRB.usrpb210.conf + 0 + 0 + lte + yes + + + + + Initialize_eNB + Initialize gNB + -O ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf -E -q + 1 + 1 + nr + + + + IdleSleep + Sleep + 5 + + + + IdleSleep + Sleep + 20 + + + + + Ping + Ping: 20pings in 20sec + idefix + -c 20 + 50 + + + + Ping + Ping: 100pings in 20sec + idefix + -c 100 -i 0.2 + 50 + + + + Iperf + iperf (DL/20Mbps/UDP)(60 sec)(single-ue profile) + -u -b 20M -t 60 + DL + idefix + 50 + single-ue + + + + Iperf + iperf (UL/3Mbps/UDP)(60 sec)(single-ue profile) + -u -b 3M -t 60 + UL + idefix + 50 + single-ue + + + + + Terminate_eNB + Terminate eNB + 0 + 0 + lte + + + + Terminate_eNB + Terminate gNB + 1 + 1 + nr + + + + diff --git a/ci-scripts/xml_files/fr1_nsa_quectel_long.xml b/ci-scripts/xml_files/fr1_nsa_quectel_long.xml new file mode 100644 index 0000000000..21ff7a63c5 --- /dev/null +++ b/ci-scripts/xml_files/fr1_nsa_quectel_long.xml @@ -0,0 +1,148 @@ + + + TEST-NSA-FR1-TM1 + NSA Ping DL UL with QUECTEL (Longer) + tasks + 1 + + 030000 + 040000 + 000002 + 010000 + 000001 + 050000 + 050001 + 000001 + 070000 + 000001 + 070001 + 000001 + 010002 + 080001 + 080000 + + + + + Initialize_UE + Initialize Quectel + idefix + + + + + Terminate_UE + Terminate Quectel + idefix + + + + + Initialize_eNB + Initialize eNB + -O ci-scripts/conf_files/enb.band7.tm1.fr1.25PRB.usrpb210.conf + 0 + 0 + lte + yes + + + + + Initialize_eNB + Initialize gNB + -O ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf -E -q + 1 + 1 + nr + + + + IdleSleep + Sleep + 5 + + + + IdleSleep + Sleep + 20 + + + + + Ping + Ping: 20pings in 20sec + idefix + -c 20 + 50 + + + + Ping + Ping: 100pings in 20sec + idefix + -c 100 -i 0.2 + 50 + + + + Iperf + iperf (DL/20Mbps/UDP)(600 sec)(single-ue profile) + -u -b 20M -t 600 + DL + idefix + 50 + single-ue + + + + Iperf + iperf (UL/3Mbps/UDP)(600 sec)(single-ue profile) + -u -b 3M -t 600 + UL + idefix + 50 + single-ue + + + + + Terminate_eNB + Terminate eNB + 0 + 0 + lte + + + + Terminate_eNB + Terminate gNB + 1 + 1 + nr + + + + diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 8d8e88455a..b8992573cd 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -320,7 +320,7 @@ if (CUDA_FOUND) set(CUDA_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS_PROCESSOR} -Wno-packed-bitfield-compat -fPIC -Wall -fno-strict-aliasing -rdynamic -std=c++11 -D CUDA_FLAG" ) - + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FCNTL_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_STRERROR=1 -DHAVE_SOCKET=1 -DHAVE_MEMSET=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_LIBSCTP -D${MKVER} -D CUDA_FLAG" ) @@ -338,8 +338,8 @@ else (CUDA_FOUND) endif () if (SANITIZE_ADDRESS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-common") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-common") endif () add_definitions("-DASN_DISABLE_OER_SUPPORT") @@ -349,7 +349,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath -Wl,${CMAKE_CU ######################### # set a flag for changes in the source code # these changes are related to hardcoded path to include .h files -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3 -O0 -DMALLOC_CHECK_=3") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3 -Og -DMALLOC_CHECK_=3") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -g3 -DMALLOC_CHECK_=3 -O2 -fno-delete-null-pointer-checks") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3") @@ -414,7 +414,6 @@ add_boolean_option(UE_AUTOTEST_TRACE False "Activate UE autotest specific logs add_boolean_option(UE_DEBUG_TRACE False "Activate UE debug trace") add_boolean_option(UE_TIMING_TRACE False "Activate UE timing trace") add_boolean_option(DEBUG_CONSOLE False "makes debugging easier, disables stdout/stderr buffering") -add_boolean_option(NEW_GTPU False "NEW_GTP") set (OCP_ITTI ${OPENAIR_DIR}/common/utils/ocp_itti) add_library(ITTI @@ -496,7 +495,7 @@ add_custom_target ( DEPENDS ${NR_RRC_GRAMMAR} ) -add_library(NR_RRC_LIB ${nr_rrc_h} ${nr_rrc_source} +add_library(NR_RRC_LIB ${nr_rrc_h} ${nr_rrc_source} ${OPENAIR2_DIR}/RRC/NR/MESSAGES/asn1_msg.c ) add_dependencies(NR_RRC_LIB nr_rrc_flag) @@ -530,21 +529,21 @@ endif (${RU} STREQUAL 0) add_custom_target ( s1ap_flag ALL ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "S1AP_" -fno-include-deps "${S1AP_C_DIR}" "${S1AP_ASN_DIR}/${S1AP_ASN_FILES}" - DEPENDS "${S1AP_ASN_DIR}/${S1AP_ASN_FILES}" + DEPENDS "${S1AP_ASN_DIR}/${S1AP_ASN_FILES}" ) - add_library(S1AP_LIB - ${S1AP_source} - ${S1AP_DIR}/s1ap_common.c - ) - add_dependencies(S1AP_LIB rrc_flag s1ap_flag) +add_library(S1AP_LIB + ${S1AP_source} + ${S1AP_DIR}/s1ap_common.c + ) +add_dependencies(S1AP_LIB rrc_flag s1ap_flag) - include_directories ("${S1AP_C_DIR}") - include_directories ("${S1AP_DIR}") +include_directories ("${S1AP_C_DIR}") +include_directories ("${S1AP_DIR}") add_library(S1AP_ENB - ${S1AP_DIR}/s1ap_eNB.c - ${S1AP_DIR}/s1ap_eNB_context_management_procedures.c + ${S1AP_DIR}/s1ap_eNB.c + ${S1AP_DIR}/s1ap_eNB_context_management_procedures.c ${S1AP_DIR}/s1ap_eNB_decoder.c ${S1AP_DIR}/s1ap_eNB_encoder.c ${S1AP_DIR}/s1ap_eNB_handlers.c @@ -575,7 +574,7 @@ set(NGAP_C_DIR ${asn1_generated_dir}/NGAP_${NGAP_RELEASE}) # Warning: if you modify ASN.1 source file to generate new C files, cmake should be re-run instead of make execute_process( - COMMAND ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "NGAP_" "-fno-include-deps -findirect-choice" "${NGAP_C_DIR}" "${NGAP_ASN_DIR}/${NGAP_ASN_FILES}" + COMMAND ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "NGAP_" "-fno-include-deps -findirect-choice" "${NGAP_C_DIR}" "${NGAP_ASN_DIR}/${NGAP_ASN_FILES}" RESULT_VARIABLE ret) if (NOT ${ret} STREQUAL 0) message(FATAL_ERROR "${ret}: error") @@ -585,7 +584,7 @@ file(GLOB NGAP_source ${NGAP_C_DIR}/*.c) add_custom_target ( ngap_flag ALL ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "NGAP_" "-fno-include-deps -findirect-choice" "${NGAP_C_DIR}" "${NGAP_ASN_DIR}/${NGAP_ASN_FILES}" - DEPENDS "${NGAP_ASN_DIR}/${NGAP_ASN_FILES}" + DEPENDS "${NGAP_ASN_DIR}/${NGAP_ASN_FILES}" ) add_library(NGAP_LIB @@ -643,7 +642,7 @@ set(M2AP_C_DIR ${asn1_generated_dir}/M2AP_${M2AP_RELEASE}) # Warning: if you modify ASN.1 source file to generate new C files, cmake should be re-run instead of make if (${RU} STREQUAL 0) execute_process( - COMMAND ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "M2AP_" "-fno-include-deps -DEMIT_ASN_DEBUG=1" "${M2AP_C_DIR}" "${M2AP_ASN_DIR}/${M2AP_ASN_FILES}" + COMMAND ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "M2AP_" "-fno-include-deps -DEMIT_ASN_DEBUG=1" "${M2AP_C_DIR}" "${M2AP_ASN_DIR}/${M2AP_ASN_FILES}" RESULT_VARIABLE ret) if (NOT ${ret} STREQUAL 0) message(FATAL_ERROR "${ret}: error") @@ -716,7 +715,7 @@ set(M3AP_C_DIR ${asn1_generated_dir}/M3AP_${M3AP_RELEASE}) # Warning: if you modify ASN.1 source file to generate new C files, cmake should be re-run instead of make if (${RU} STREQUAL 0) execute_process( - COMMAND ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "M3AP_" -fno-include-deps "${M3AP_C_DIR}" "${M3AP_ASN_DIR}/${M3AP_ASN_FILES}" + COMMAND ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "M3AP_" -fno-include-deps "${M3AP_C_DIR}" "${M3AP_ASN_DIR}/${M3AP_ASN_FILES}" RESULT_VARIABLE ret) if (NOT ${ret} STREQUAL 0) message(FATAL_ERROR "${ret}: error") @@ -813,6 +812,7 @@ add_dependencies(X2AP_ENB X2AP_LIB rrc_flag x2_flag) # F1AP ############## +set (F1AP_RELEASE R16) add_list1_option(F1AP_RELEASE R16 "F1AP ASN.1 grammar version" R16) set(F1AP_DIR ${OPENAIR2_DIR}/F1AP) if (${F1AP_RELEASE} STREQUAL "R16") @@ -841,7 +841,7 @@ if (${RU} STREQUAL 0) if (NOT ${ret} STREQUAL 0) message(FATAL_ERROR "asn1c: error") endif (NOT ${ret} STREQUAL 0) - + add_custom_target ( f1_flag ALL COMMAND ${OPENAIR_CMAKE}/tools/make_asn1c_includes.sh "F1AP_" "-findirect-choice -fno-include-deps" ${F1AP_ASN_GENERATED_C_DIR} ${F1AP_ASN_FILES} @@ -1021,6 +1021,7 @@ add_boolean_option(PHY_TX_THREAD False "enable UE_EXPANSION w add_boolean_option(PRE_SCD_THREAD False "enable UE_EXPANSION with max 256 UE") add_boolean_option(UESIM_EXPANSION False "enable UESIM_EXPANSION with max 256 UE") add_boolean_option(ITTI_SIM False "enable itti simulator") +add_boolean_option(RFSIM_NAS False "enable rfsim nas") ######################## # Include order @@ -1502,11 +1503,11 @@ add_dependencies(SCHED_UE_LIB rrc_flag) set(SCHED_SRC_NR_UE ${OPENAIR1_DIR}/SCHED_NR_UE/phy_procedures_nr_ue.c ${OPENAIR1_DIR}/SCHED_NR/phy_procedures_nr_common.c - ${OPENAIR1_DIR}/SCHED_NR_UE/fapi_nr_ue_l1.c + ${OPENAIR1_DIR}/SCHED_NR_UE/fapi_nr_ue_l1.c ${OPENAIR1_DIR}/SCHED_NR_UE/phy_frame_config_nr_ue.c ${OPENAIR1_DIR}/SCHED_NR_UE/harq_nr.c ${OPENAIR1_DIR}/SCHED_NR_UE/pucch_uci_ue_nr.c - ${OPENAIR1_DIR}/SCHED_NR_UE/pucch_power_control_ue_nr.c + ${OPENAIR1_DIR}/SCHED_NR_UE/pucch_power_control_ue_nr.c ) add_library(SCHED_NR_UE_LIB ${SCHED_SRC_NR_UE}) @@ -1816,7 +1817,7 @@ set(PHY_SRC_UE ${PHY_POLARSRC} ${PHY_SMALLBLOCKSRC} ${PHY_NR_CODINGIF} - ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/pucch_rx.c + ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/pucch_rx.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_uci_tools_common.c ) set(PHY_NR_UE_SRC @@ -1995,7 +1996,7 @@ set(NR_RLC_SRC ${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_sdu.c ${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_ue_manager.c ) - + set(NR_PDCP_SRC ${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_oai_api.c ${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c @@ -2022,12 +2023,13 @@ set(L2_SRC ${RRC_DIR}/rrc_eNB_S1AP.c ${RRC_DIR}/rrc_eNB_M2AP.c ${RRC_DIR}/rrc_eNB_UE_context.c + ${NR_RRC_DIR}/rrc_gNB_UE_context.c ${RRC_DIR}/rrc_common.c ${RRC_DIR}/L2_interface.c ${RRC_DIR}/L2_interface_common.c ${RRC_DIR}/L2_interface_ue.c ) - + set(L2_RRC_SRC ${OPENAIR2_DIR}/LAYER2/openair2_proc.c # ${RRC_DIR}/rrc_UE.c @@ -2073,14 +2075,14 @@ set(L2_SRC_UE ${RRC_DIR}/L2_interface_common.c ${RRC_DIR}/L2_interface_ue.c ) - + set(L2_RRC_SRC_UE ${RRC_DIR}/rrc_UE.c ${RRC_DIR}/rrc_common.c ${RRC_DIR}/L2_interface_common.c ${RRC_DIR}/L2_interface_ue.c - ) - + ) + set(NR_L2_SRC_UE ${NR_RLC_SRC} ${NR_PDCP_SRC} @@ -2138,7 +2140,7 @@ set (MAC_SRC_UE ${MAC_DIR}/rar_tools_ue.c ${MAC_DIR}/config_ue.c ) - + set (MAC_NR_SRC_UE ${NR_UE_PHY_INTERFACE_DIR}/NR_IF_Module.c ${NR_UE_MAC_DIR}/config_ue.c @@ -2210,6 +2212,7 @@ add_library(L2_NR ${MAC_NR_SRC} ${GNB_APP_SRC} ) +target_compile_definitions(L2_NR PUBLIC NEW_GTPU) add_library(L2_LTE_NR ${L2_RRC_SRC} @@ -2217,7 +2220,8 @@ add_library(L2_LTE_NR ${ENB_APP_SRC} ${MCE_APP_SRC} ) - +target_compile_definitions(L2_LTE_NR PUBLIC NEW_GTPU) + add_dependencies(L2_NR rrc_flag nr_rrc_flag s1ap_flag x2_flag) add_library(L2_UE @@ -2264,6 +2268,7 @@ add_library (GTPV1U_OCP ${RRC_DIR}/rrc_eNB_GTPV1U.c ${OPENAIR3_DIR}/ocp-gtpu/gtp_itf.cpp ) +target_compile_definitions(GTPV1U_OCP PUBLIC NEW_GTPU) add_dependencies(GTPV1U_OCP rrc_flag) include_directories(${OPENAIR3_DIR}/ocp-gtp) @@ -2282,12 +2287,6 @@ set (NR_GTPV1U_SRC add_library(NR_GTPV1U ${NR_GTPV1U_SRC}) add_dependencies(NR_GTPV1U rrc_flag) -if (${NEW_GTPU}) -set(GTPV1U GTPV1U_OCP) -else() -set(GTPV1U NR_GTPV1U) -endif() - set (MME_APP_SRC ${OPENAIR3_DIR}/MME_APP/mme_app.c ${OPENAIR3_DIR}/MME_APP/mme_config.c @@ -2566,7 +2565,7 @@ if(NAS_UE) endif() -if(ITTI_SIM) +if(ITTI_SIM OR RFSIM_NAS) set(libnas_ue_api_OBJS ${NAS_SRC}UE/API/USER/at_command.c ${NAS_SRC}UE/API/USER/at_error.c @@ -2759,7 +2758,7 @@ add_library(SIMU_COMMON # Simulation library ########################## -set (SIMUSRC +set (SIMUSRC ${OPENAIR1_DIR}/SIMULATION/TOOLS/taus.c ${OPENAIR1_DIR}/SIMULATION/TOOLS/multipath_channel.c ${OPENAIR1_DIR}/SIMULATION/TOOLS/multipath_tv_channel.c @@ -2822,7 +2821,7 @@ add_library(nrscope MODULE ${XFORMS_SOURCE_NR}) target_link_libraries(nrscope ${XFORMS_LIBRARIES}) -add_library(rfsimulator MODULE +add_library(rfsimulator MODULE ${OPENAIR_TARGETS}/ARCH/rfsimulator/simulator.c ${OPENAIR_TARGETS}/ARCH/rfsimulator/apply_channelmod.c ${OPENAIR_TARGETS}/ARCH/rfsimulator/new_channel_sim.c @@ -2830,7 +2829,7 @@ add_library(rfsimulator MODULE ) target_link_libraries(rfsimulator SIMU_COMMON ${ATLAS_LIBRARIES}) -add_library(oai_iqplayer MODULE +add_library(oai_iqplayer MODULE ${OPENAIR_TARGETS}/ARCH/iqplayer/iqplayer_lib.c ) set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}") @@ -2900,7 +2899,7 @@ add_executable(measurement_display ${OPENAIR_DIR}/common/utils/threadPool/measurement_display.c) target_link_libraries (measurement_display minimal_lib) -add_executable(test5Gnas +add_executable(test5Gnas ${OPENAIR_DIR}/openair3/TEST/test5Gnas.c ) target_link_libraries (test5Gnas LIB_5GNAS_GNB CONFIG_LIB minimal_lib ) @@ -2925,6 +2924,7 @@ add_executable(lte-softmodem ${OPENAIR_TARGETS}/ARCH/COMMON/record_player.c ${OPENAIR2_DIR}/RRC/NAS/nas_config.c ${OPENAIR2_DIR}/RRC/NAS/rb_config.c + ${OPENAIR2_DIR}/F1AP/dummy_enb.c ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/multicast_link.c ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/socket.c @@ -2943,11 +2943,11 @@ add_dependencies(lte-softmodem rrc_flag s1ap_flag x2_flag oai_iqplayer) target_link_libraries (lte-softmodem -Wl,--start-group - RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB F1AP_LIB F1AP M2AP_LIB M2AP_ENB X2AP_LIB X2AP_ENB M3AP_LIB M3AP_ENB ${GTPV1U} SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB SCHED_RU_LIB + RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB F1AP_LIB F1AP M2AP_LIB M2AP_ENB X2AP_LIB X2AP_ENB M3AP_LIB M3AP_ENB NR_GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB SCHED_RU_LIB PHY_COMMON PHY PHY_RU LFDS L2 L2_LTE NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} ${FSPT_MSG_LIB} ${PROTO_AGENT_LIB} -Wl,--end-group z dl) - + target_link_libraries (lte-softmodem ${LIBXML2_LIBRARIES}) target_link_libraries (lte-softmodem pthread m ${CONFIG_LIB} rt crypt ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} sctp ${PROTOBUF_LIB} ${CMAKE_DL_LIBS} ${LIBYAML_LIBRARIES}) target_link_libraries (lte-softmodem ${LIB_LMS_LIBRARIES}) @@ -2986,7 +2986,7 @@ add_dependencies(ocp-enb rrc_flag s1ap_flag x2_flag oai_iqplayer coding params_l target_link_libraries (ocp-enb -Wl,--start-group - RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB F1AP_LIB F1AP M2AP_LIB M2AP_ENB X2AP_LIB X2AP_ENB M3AP_LIB M3AP_ENB ${GTPV1U} SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB SCHED_RU_LIB + RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB F1AP_LIB F1AP M2AP_LIB M2AP_ENB X2AP_LIB X2AP_ENB M3AP_LIB M3AP_ENB NR_GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB SCHED_RU_LIB PHY_COMMON PHY PHY_RU LFDS L2 L2_LTE NFAPI_COMMON_LIB NFAPI_LIB MISC_NFAPI_LTE_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB LFDS7 SIMU_COMMON ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} ${FSPT_MSG_LIB} ${PROTO_AGENT_LIB} -Wl,--end-group z dl) @@ -3040,10 +3040,10 @@ add_executable(oairu ) target_link_libraries (oairu -Wl,--start-group - SCHED_RU_LIB - PHY_COMMON PHY_RU + SCHED_RU_LIB + PHY_COMMON PHY_RU -Wl,--end-group z dl) - + target_link_libraries (oairu pthread m ${CONFIG_LIB} rt ${CMAKE_DL_LIBS} ${T_LIB}) @@ -3083,7 +3083,7 @@ target_link_libraries (lte-uesoftmodem -Wl,--start-group RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB F1AP F1AP_LIB - ${GTPV1U} SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON + NR_GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_UE PHY_RU LFDS L2_UE L2_LTE LFDS7 SIMU_COMMON SIMU NFAPI_COMMON_LIB NFAPI_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${ATLAS_LIBRARIES} -Wl,--end-group z dl) @@ -3124,10 +3124,11 @@ add_executable(nr-softmodem ${OPENAIR2_DIR}/ENB_APP/flexran_agent_ran_api_to_fix.c ) +target_compile_definitions(nr-softmodem PUBLIC NEW_GTPU) target_link_libraries (nr-softmodem -Wl,--start-group - UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS ${GTPV1U} SECU_CN SECU_OSA - ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} RRC_LIB NR_RRC_LIB + UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS GTPV1U_OCP SECU_CN SECU_OSA + ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} RRC_LIB NR_RRC_LIB NGAP_LIB NGAP_GNB S1AP_LIB S1AP_ENB L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB X2AP_LIB X2AP_ENB F1AP_LIB F1AP M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB ${PROTO_AGENT_LIB} ${FSPT_MSG_LIB} -Wl,--end-group z dl) @@ -3165,8 +3166,8 @@ add_executable(ocp-gnb target_link_libraries (ocp-gnb -Wl,--start-group - UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS ${GTPV1U} SECU_CN SECU_OSA - ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} RRC_LIB NR_RRC_LIB + UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS GTPV1U_OCP SECU_CN SECU_OSA + ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} RRC_LIB NR_RRC_LIB NGAP_LIB NGAP_GNB S1AP_LIB S1AP_ENB L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB X2AP_LIB X2AP_ENB F1AP_LIB F1AP M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB SIMU_COMMON -Wl,--end-group z dl) @@ -3214,7 +3215,7 @@ target_link_libraries (nr-uesoftmodem NFAPI_USER_LIB MISC_NFAPI_NR_LIB S1AP_LIB S1AP_ENB ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES} NFAPI_USER_LIB S1AP_LIB S1AP_ENB - ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES} LIB_5GNAS_GNB ${NAS_SIM_LIB} + ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES} LIB_5GNAS_GNB LIB_NAS_SIMUE ${NAS_SIM_LIB} -Wl,--end-group z dl) target_link_libraries (nr-uesoftmodem ${LIBXML2_LIBRARIES}) @@ -3244,7 +3245,7 @@ target_link_libraries (dlsim_tm4 pthread m rt ${CONFIG_LIB} ${ATLAS_LIBRARIES} ${T_LIB} ) -add_executable(rftest +add_executable(rftest ${OPENAIR_DIR}/openair1/PHY/TOOLS/calibration_test.c ${OPENAIR_DIR}/openair1/PHY/TOOLS/calibration_scope.c ${OPENAIR_DIR}/common/utils/system.c @@ -3255,7 +3256,7 @@ add_executable(rftest ) target_link_libraries(rftest minimal_lib PHY_NR_COMMON pthread dl m forms ${T_LIB} ) -add_executable(polartest +add_executable(polartest ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/polartest.c ${OPENAIR_DIR}/common/utils/backtrace.c ${OPENAIR_DIR}/common/utils/nr/nr_common.c @@ -3268,7 +3269,7 @@ target_link_libraries(polartest m pthread ${ATLAS_LIBRARIES} dl ) -add_executable(smallblocktest +add_executable(smallblocktest ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/smallblocktest.c ${OPENAIR_DIR}/common/utils/backtrace.c ${OPENAIR_DIR}/common/utils/nr/nr_common.c @@ -3286,17 +3287,17 @@ target_link_libraries(smallblocktest # temp_C_flag = CMAKE_C_FLAGS #set(CMAKE_C_FLAGS " ") -set (TEMP_C_FLAG ${CMAKE_C_FLAGS}) +set (TEMP_C_FLAG ${CMAKE_C_FLAGS}) set (CMAKE_C_FLAGS ${CUDA_CMAKE_C_FLAGS}) -set (TEMP_CXX_FLAG ${CMAKE_CXX_FLAGS}) +set (TEMP_CXX_FLAG ${CMAKE_CXX_FLAGS}) set (CMAKE_CXX_FLAGS ${CUDA_CMAKE_CXX_FLAGS}) if (CUDA_FOUND) ################################################### -# For CUDA library +# For CUDA library ################################################### - - CUDA_ADD_LIBRARY(LDPC_CU + + CUDA_ADD_LIBRARY(LDPC_CU ${OPENAIR1_DIR}/PHY/CODING/nrLDPC_decoder_LYC/nrLDPC_decoder_LYC.cu ) CUDA_ADD_CUFFT_TO_TARGET(LDPC_CU) @@ -3306,14 +3307,14 @@ if (CUDA_FOUND) ${SHLIB_LOADER_SOURCES} ) target_link_libraries(ldpctest -ldl - -Wl,--start-group - LDPC_CU UTIL SIMU PHY_NR CONFIG_LIB + -Wl,--start-group + LDPC_CU UTIL SIMU PHY_NR CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} dl ) else (CUDA_FOUND) - add_executable(ldpctest + add_executable(ldpctest ${PHY_NR_CODINGIF} ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/ldpctest.c ${T_SOURCE} @@ -3325,21 +3326,21 @@ set (CMAKE_C_FLAGS ${TEMP_C_FLAG}) set (CMAKE_CXX_FLAGS ${TEMP_CXX_FLAG}) -# add_executable(ldpctest +# add_executable(ldpctest # ${PHY_NR_CODINGIF} # ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/ldpctest.c # ${T_SOURCE} # ${SHLIB_LOADER_SOURCES} # ) -add_dependencies( ldpctest ldpc_orig ldpc_optim ldpc_optim8seg ldpc ) +add_dependencies( ldpctest ldpc_orig ldpc_optim ldpc_optim8seg ldpc ) target_link_libraries(ldpctest -Wl,--start-group UTIL SIMU_COMMON SIMU PHY_NR PHY_COMMON PHY_NR_COMMON CONFIG_LIB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} dl ) -add_executable(nr_dlschsim - ${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlschsim.c +add_executable(nr_dlschsim + ${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlschsim.c ${OPENAIR_DIR}/common/utils/system.c ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR_DIR}/common/utils/utils.c @@ -3348,13 +3349,13 @@ add_executable(nr_dlschsim ${T_SOURCE} ${SHLIB_LOADER_SOURCES} ) -target_link_libraries(nr_dlschsim +target_link_libraries(nr_dlschsim -Wl,--start-group UTIL SIMU_COMMON SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} ${ITTI_LIB} dl ) -add_executable(nr_pbchsim - ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pbchsim.c +add_executable(nr_pbchsim + ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pbchsim.c ${OPENAIR_DIR}/common/utils/system.c ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR_DIR}/common/utils/utils.c @@ -3369,8 +3370,8 @@ target_link_libraries(nr_pbchsim ) #PUCCH ---> Prashanth -add_executable(nr_pucchsim - ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pucchsim.c +add_executable(nr_pucchsim + ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pucchsim.c ${OPENAIR_DIR}/common/utils/backtrace.c ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR_DIR}/common/utils/system.c @@ -3398,7 +3399,7 @@ add_executable(nr_dlsim ${UTIL_SRC} ${T_SOURCE} ${SHLIB_LOADER_SOURCES} - ) + ) target_link_libraries(nr_dlsim -Wl,--start-group UTIL SIMU_COMMON SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_NR MAC_UE_NR MAC_NR_COMMON RRC_LIB NR_RRC_LIB CONFIG_LIB L2_LTE_NR L2_NR HASHTABLE X2AP_ENB X2AP_LIB SECU_CN NGAP_GNB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} ${ITTI_LIB} ${OPENSSL_LIBRARIES} dl @@ -3406,7 +3407,7 @@ target_link_libraries(nr_dlsim target_compile_definitions(nr_dlsim PUBLIC -DPHYSICAL_SIMULATOR) add_executable(nr_prachsim - ${OPENAIR1_DIR}/SIMULATION/NR_PHY/prachsim.c + ${OPENAIR1_DIR}/SIMULATION/NR_PHY/prachsim.c ${OPENAIR_DIR}/common/utils/utils.c ${OPENAIR_DIR}/common/utils/system.c ${OPENAIR_DIR}/common/utils/nr/nr_common.c @@ -3418,7 +3419,7 @@ add_executable(nr_prachsim ${UTIL_SRC} ${T_SOURCE} ${SHLIB_LOADER_SOURCES}) -target_link_libraries(nr_prachsim +target_link_libraries(nr_prachsim -Wl,--start-group UTIL SIMU_COMMON SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_RU PHY_NR_UE MAC_NR_COMMON SCHED_NR_LIB SCHED_NR_UE_LIB MAC_NR MAC_UE_NR MAC_NR_COMMON RRC_LIB NR_RRC_LIB CONFIG_LIB L2_LTE_NR L2_NR HASHTABLE X2AP_ENB X2AP_LIB SECU_CN NGAP_GNB -Wl,--end-group m pthread ${ATLAS_LIBRARIES} ${T_LIB} ${ITTI_LIB} ${OPENSSL_LIBRARIES} dl) @@ -3458,7 +3459,7 @@ target_link_libraries(nr_ulsim target_compile_definitions(nr_ulsim PUBLIC -DPHYSICAL_SIMULATOR) foreach(myExe dlsim dlsim_tm7 ulsim pbchsim scansim mbmssim pdcchsim pucchsim prachsim syncsim) - + add_executable(${myExe} ${OPENAIR1_DIR}/SIMULATION/LTE_PHY/${myExe}.c ${OPENAIR_DIR}/common/utils/threadPool/thread-pool.c @@ -3478,7 +3479,7 @@ foreach(myExe dlsim dlsim_tm7 ulsim pbchsim scansim mbmssim pdcchsim pucchsim pr -Wl,--start-group SIMU_COMMON SIMU UTIL SCHED_LIB SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_NR_COMMON PHY PHY_UE PHY_RU LFDS ${ITTI_LIB} LFDS7 -Wl,--end-group pthread m rt ${CONFIG_LIB} ${ATLAS_LIBRARIES} ${XFORMS_LIBRARIES} ${T_LIB} dl ) - + endforeach(myExe) add_executable(test_epc_generate_scenario @@ -3490,7 +3491,7 @@ add_executable(test_epc_generate_scenario ${OPENAIR3_DIR}/S1AP/s1ap_eNB_defs.h ) target_link_libraries (test_epc_generate_scenario - -Wl,--start-group RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB F1AP_LIB F1AP ${GTPV1U} LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB PHY LFDS ${ITTI_LIB} ${MSC_LIB} L2 -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${CONFIG_LIB} + -Wl,--start-group RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB F1AP_LIB F1AP NR_GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB PHY LFDS ${ITTI_LIB} ${MSC_LIB} L2 -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${CONFIG_LIB} ) add_executable(test_epc_play_scenario @@ -3509,7 +3510,7 @@ add_executable(test_epc_play_scenario ) target_include_directories(test_epc_play_scenario PUBLIC /usr/local/share/asn1c) target_link_libraries (test_epc_play_scenario - -Wl,--start-group RRC_LIB S1AP_LIB X2AP_LIB X2AP_ENB M3AP_LIB M3AP_ENB F1AP_LIB F1AP ${GTPV1U} LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB PHY_NR_COMMON PHY_COMMON PHY PHY_UE LFDS ${ITTI_LIB} ${MSC_LIB} -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${CONFIG_LIB} + -Wl,--start-group RRC_LIB S1AP_LIB X2AP_LIB X2AP_ENB M3AP_LIB M3AP_ENB F1AP_LIB F1AP NR_GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT MME_APP UDP SCHED_LIB PHY_NR_COMMON PHY_COMMON PHY PHY_UE LFDS ${ITTI_LIB} ${MSC_LIB} -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${CONFIG_LIB} ) @@ -3541,13 +3542,13 @@ endforeach(myExe) if (${T_TRACER}) foreach(i #all "add_executable" definitions (except tests, rb_tool, updatefw) - lte-softmodem lte-uesoftmodem nr-softmodem + lte-softmodem lte-uesoftmodem nr-softmodem nr-uesoftmodem dlsim dlsim_tm4 dlsim_tm7 nr-ittisim ulsim pbchsim scansim mbmssim pdcchsim pucchsim prachsim syncsim nr_ulsim nr_dlsim nr_dlschsim nr_pbchsim nr_pucchsim nr_ulschsim ldpctest polartest smallblocktest cu_test du_test #all "add_library" definitions - ITTI RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB F1AP_LIB F1AP + ITTI RRC_LIB NR_RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB F1AP_LIB F1AP params_libconfig oai_exmimodevif oai_usrpdevif oai_bladerfdevif oai_lmssdrdevif oai_iqplayer oai_eth_transpro oai_mobipass tcp_bridge tcp_bridge_oai coding FLPT_MSG ASYNC_IF FLEXRAN_AGENT HASHTABLE MSC UTIL OMG_SUMO @@ -3555,7 +3556,7 @@ if (${T_TRACER}) NFAPI_COMMON_LIB NFAPI_LIB NFAPI_PNF_LIB NFAPI_VNF_LIB NFAPI_USER_LIB PHY_COMMON PHY PHY_UE PHY_NR PHY_NR_COMMON PHY_NR_UE PHY_RU PHY_MEX L2 L2_LTE L2_NR L2_LTE_NR L2_UE NR_L2_UE L2_UE_LTE_NR MAC_NR_COMMON MAC_NR MAC_UE_NR NGAP_GNB - CN_UTILS ${GTPV1U} SCTP_CLIENT MME_APP UDP LIB_NAS_UE NB_IoT LFDS LFDS7 SIMU_COMMON SIMU SIMU_ETH OPENAIR0_LIB + CN_UTILS NR_GTPV1U GTPV1U_OCP SCTP_CLIENT MME_APP UDP LIB_NAS_UE NB_IoT LFDS LFDS7 SIMU_COMMON SIMU SIMU_ETH OPENAIR0_LIB ldpc_orig ldpc_optim ldpc_optim8seg ldpc PROTO_AGENT dfts) if (TARGET ${i}) add_dependencies(${i} generate_T) @@ -3660,14 +3661,15 @@ add_executable(nr-ittisim ${CONFIG_SOURCES} ${SHLIB_LOADER_SOURCES} ) +target_compile_definitions(nr-ittisim PUBLIC NEW_GTPU) target_link_libraries (nr-ittisim -Wl,--start-group - UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS ${GTPV1U} SECU_CN SECU_OSA - ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_SIM_LIB} RRC_LIB NR_RRC_LIB + UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU LFDS GTPV1U_OCP SECU_CN SECU_OSA + ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} LFDS7 ${MSC_LIB} ${RAL_LIB} ${NAS_SIM_LIB} RRC_LIB NR_RRC_LIB NGAP_LIB NGAP_GNB S1AP_LIB S1AP_ENB L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB X2AP_LIB X2AP_ENB F1AP_LIB F1AP M2AP_LIB M2AP_ENB M3AP_LIB M3AP_ENB ${PROTO_AGENT_LIB} ${FSPT_MSG_LIB} - PHY_NR_UE SCHED_NR_UE_LIB NR_L2_UE + PHY_NR_UE SCHED_NR_UE_LIB NR_L2_UE -Wl,--end-group z dl) target_link_libraries (nr-ittisim ${LIBXML2_LIBRARIES}) diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 279b9489e0..d95b30e052 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -68,6 +68,7 @@ USRP_REC_PLAY="False" BUILD_ECLIPSE=0 NR="False" ITTI_SIM="False" +RFSIM_NAS="False" SANITIZE_ADDRESS="False" OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope msc" RU=0 @@ -106,7 +107,7 @@ Options --nrUE Makes the NR UE softmodem --RU - Makes the OAI RRU (without full stack) + Makes the OAI RRU (without full stack) --UE Makes the UE specific parts (ue_ip, usim, nvram) from the given configuration file --UE-conf-nvram [configuration file] @@ -259,7 +260,7 @@ function main() { echo_info "Will compile gNB" shift;; --RU) - RU=1 + RU=1 echo_info "Will compile RRU" shift;; -a | --agent) @@ -273,6 +274,8 @@ function main() { --nrUE) RU=0 nrUE=1 + rfsimNas=1 + RFSIM_NAS="True" NR="True" echo_info "Will compile NR UE" shift;; @@ -308,7 +311,7 @@ function main() { shift 2;; -P | --phy_simulators) SIMUS_PHY=1 - RU=0 + RU=0 echo_info "Will compile dlsim, ulsim, ..." shift;; -S | --core_simulators) @@ -604,7 +607,7 @@ function main() { config_libconfig_shlib=params_libconfig # first generate the CMakefile in the right directory - if [ "$eNB" = "1" -o "$eNBocp" = "1" -o "$UE" = "1" -o "$gNB" = "1" -o "$RU" = "1" -o "$nrUE" = "1" -o "$HW" = "EXMIMO" -o "$ittiSIM" = "1" ] ; then + if [ "$eNB" = "1" -o "$eNBocp" = "1" -o "$UE" = "1" -o "$gNB" = "1" -o "$RU" = "1" -o "$nrUE" = "1" -o "$HW" = "EXMIMO" -o "$ittiSIM" = "1" -o "$rfsimNas" = "1" ] ; then # softmodem compilation @@ -630,6 +633,7 @@ function main() { echo "set ( SKIP_SHARED_LIB_FLAG $SKIP_SHARED_LIB_FLAG )" >> $cmake_file echo "set ( RU $RU )" >> $cmake_file echo "set ( ITTI_SIM $ITTI_SIM )" >> $cmake_file + echo "set ( RFSIM_NAS $RFSIM_NAS )" >> $cmake_file echo "set ( SANITIZE_ADDRESS $SANITIZE_ADDRESS )" >> $cmake_file echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file cd $DIR/$build_dir/build diff --git a/common/config/config_cmdline.c b/common/config/config_cmdline.c index 1df1bc16f7..f5dfae06b6 100644 --- a/common/config/config_cmdline.c +++ b/common/config/config_cmdline.c @@ -211,7 +211,7 @@ int config_process_cmdline(paramdef_t *cfgoptions,int numoptions, char *prefix) while (c > 0 ) { char *oneargv = strdup(config_get_if()->argv[i]); /* we use strtok_r which modifies its string paramater, and we don't want argv to be modified */ - + if(!oneargv) abort(); /* first check help options, either --help, -h or --help_
*/ if (strncmp(oneargv, "-h",2) == 0 || strncmp(oneargv, "--help",6) == 0 ) { char *tokctx = NULL; diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c index 8029ba826b..d0a53a755b 100644 --- a/common/utils/LOG/log.c +++ b/common/utils/LOG/log.c @@ -460,7 +460,7 @@ int logInit (void) register_log_component("LOCALIZE","log",LOCALIZE); register_log_component("NAS","log",NAS); register_log_component("UDP","",UDP_); - register_log_component("GTPV1U","",GTPU); + register_log_component("GTPU","",GTPU); register_log_component("S1AP","",S1AP); register_log_component("F1AP","",F1AP); register_log_component("M2AP","",M2AP); diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 0671aed813..cc2860191a 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -111,8 +111,6 @@ const size_t nr_bandtable_size = sizeof(nr_bandtable) / sizeof(nr_bandentry_t); int NRRIV2BW(int locationAndBandwidth,int N_RB) { int tmp = locationAndBandwidth/N_RB; int tmp2 = locationAndBandwidth%N_RB; - - if (tmp <= ((N_RB>>1)+1) && (tmp+tmp2)>1)+1) && (tmp+tmp2)registration_request.fgsmobileidentity.suci.typeofidentity = FGS_MOBILE_IDENTITY_SUCI; + mm_msg->registration_request.fgsmobileidentity.suci.mncdigit1 = 9; + mm_msg->registration_request.fgsmobileidentity.suci.mncdigit2 = 9; + mm_msg->registration_request.fgsmobileidentity.suci.mncdigit3 = 0xf; + mm_msg->registration_request.fgsmobileidentity.suci.mccdigit1 = 2; + mm_msg->registration_request.fgsmobileidentity.suci.mccdigit2 = 0; + mm_msg->registration_request.fgsmobileidentity.suci.mccdigit3 = 8; + mm_msg->registration_request.fgsmobileidentity.suci.schemeoutput = 0x4778; +``` + - USIM_API_K and OPc keys are hardcoded at the beginning of the file: +```bash +// USIM_API_K: fe c8 6b a6 eb 70 7e d0 89 05 75 7b 1b b4 4b 8f +uint8_t k[16] = {0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f}; +// OPC: c4 24 49 36 3b ba d0 2b 66 d1 6b c9 75 d7 7c c1 +const uint8_t opc[16] = {0xc4, 0x24, 0x49, 0x36, 0x3b, 0xba, 0xd0, 0x2b, 0x66, 0xd1, 0x6b, 0xc9, 0x75, 0xd7, 0x7c, 0xc1}; +``` +- The NSSAI (*Network Slice Assistance Information*) and DNN (*Data Network Name*) are hardcoded in function *generatePduSessionEstablishRequest()* +```bash + uint8_t nssai[]={1,0,0,1}; //Corresponding to SST:1, SD:1 + uint8_t dnn[4]={0x4,0x6f,0x61,0x69}; //Corresponding to dnn:"oai" +``` +For interoperability with OAI or other CNs, it should be ensured that the configuration of the aforementioned parameters match the configuration of the corresponding subscribed user at the core network. +Hardcoding of the USIM information will soon be substituted with parsing those parameters from a configuration file. + +## 2.2 Execution of SA scenario + +The order of starting the different components should be the same as the one described in section 1.3. + + - To launch the gNB: + ```bash + sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --rfsim --sa + ``` +- To launch the OAI UE: + ```bash +sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --nokrnmod +``` +The IP address at the execution command of the OAI UE corresponds to the target IP of the gNB host that the RFSIMULATOR at the UE will connect to. In the above example, we assume that the gNB and UE are running on the same host so the specified address (127.0.0.1) is the one of the loopback interface. diff --git a/doc/testing_gnb_w_cots_ue_resources/enb.conf b/doc/testing_gnb_w_cots_ue_resources/enb.conf index 35249aac25..a6bbd98bda 100755 --- a/doc/testing_gnb_w_cots_ue_resources/enb.conf +++ b/doc/testing_gnb_w_cots_ue_resources/enb.conf @@ -238,7 +238,7 @@ MACRLCs = ( THREAD_STRUCT = ( { parallel_config = "PARALLEL_RU_L1_TRX_SPLITaaaaaa"; - worker_config = "ENABLE"; + worker_config = "WORKER_ENABLE"; } ); diff --git a/executables/main-ocp.c b/executables/main-ocp.c index b0f0b05315..c5f18419a3 100644 --- a/executables/main-ocp.c +++ b/executables/main-ocp.c @@ -86,6 +86,7 @@ char * split73_config; int split73; AGENT_RRC_xface *agent_rrc_xface[NUM_MAX_ENB]= {0}; AGENT_MAC_xface *agent_mac_xface[NUM_MAX_ENB]= {0}; +uint8_t proto_agent_flag = 0; void flexran_agent_slice_update(mid_t module_idP) { } int proto_agent_start(mod_id_t mod_id, const cudu_params_t *p){ diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 8b3d6fc64e..ea675eba9e 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -86,6 +86,8 @@ #include "T.h" #include "nfapi/oai_integration/vendor_ext.h" #include +#include +#include //#define DEBUG_THREADS 1 //#define USRP_DEBUG 1 @@ -347,6 +349,22 @@ static void *process_stats_thread(void *param) { return(NULL); } +void *nrL1_stats_thread(void *param) { + PHY_VARS_gNB *gNB = (PHY_VARS_gNB *)param; + wait_sync("L1_stats_thread"); + FILE *fd; + while (!oai_exit) { + sleep(1); + fd=fopen("nrL1_stats.log","w"); + AssertFatal(fd!=NULL,"Cannot open ngL1_stats.log\n"); + dump_nr_I0_stats(fd,gNB); + dump_pusch_stats(fd,gNB); + // nr_dump_uci_stats(fd,eNB,eNB->proc.L1_proc_tx.frame_tx); + fclose(fd); + } + return(NULL); +} + void init_gNB_Tpool(int inst) { PHY_VARS_gNB *gNB; gNB = RC.gNB[inst]; @@ -383,7 +401,9 @@ void init_gNB_Tpool(int inst) { initNotifiedFIFO(gNB->resp_RU_tx); // Stats measurement thread - if(opp_enabled == 1) threadCreate(&proc->L1_stats_thread, process_stats_thread,(void *)gNB, "time_meas", -1, OAI_PRIORITY_RT_LOW); + if(opp_enabled == 1) threadCreate(&proc->process_stats_thread, process_stats_thread,(void *)gNB, "time_meas", -1, OAI_PRIORITY_RT_LOW); + threadCreate(&proc->L1_stats_thread,nrL1_stats_thread,(void*)gNB,"L1_stats",-1,OAI_PRIORITY_RT_LOW); + } diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 0a042ba9b1..421ff4ad3e 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -24,14 +24,8 @@ #include #include #include -#include -#include -#include #include #include -#include -#include -#include #include #include @@ -39,7 +33,6 @@ #include "common/utils/assertions.h" #include "common/utils/system.h" -#include "msc.h" #include "../../ARCH/COMMON/common_lib.h" #include "../../ARCH/ETHERNET/USERSPACE/LIB/ethernet_lib.h" @@ -50,20 +43,15 @@ #include "PHY/types.h" #include "PHY/defs_nr_common.h" #include "PHY/phy_extern.h" -#include "PHY/LTE_TRANSPORT/transport_proto.h" #include "PHY/NR_TRANSPORT/nr_transport_proto.h" #include "PHY/INIT/phy_init.h" -#include "SCHED/sched_eNB.h" #include "SCHED_NR/sched_nr.h" #include "LAYER2/NR_MAC_COMMON/nr_mac_extern.h" -#include "RRC/LTE/rrc_extern.h" -#include "PHY_INTERFACE/phy_interface.h" #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" -#include "enb_config.h" #include #ifdef SMBV @@ -1948,39 +1936,41 @@ static void NRRCconfig_RU(void) if (config_isparamset(RUParamList.paramarray[j], RU_SDR_CLK_SRC)) { if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "internal") == 0) { RC.ru[j]->openair0_cfg.clock_source = internal; - LOG_D(PHY, "RU clock source set as internal\n"); + LOG_I(PHY, "RU clock source set as internal\n"); } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "external") == 0) { RC.ru[j]->openair0_cfg.clock_source = external; - LOG_D(PHY, "RU clock source set as external\n"); + LOG_I(PHY, "RU clock source set as external\n"); } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "gpsdo") == 0) { RC.ru[j]->openair0_cfg.clock_source = gpsdo; - LOG_D(PHY, "RU clock source set as gpsdo\n"); + LOG_I(PHY, "RU clock source set as gpsdo\n"); } else { LOG_E(PHY, "Erroneous RU clock source in the provided configuration file: '%s'\n", *(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr)); } } else { - RC.ru[j]->openair0_cfg.clock_source = unset; + LOG_I(PHY,"Setting clock source to internal\n"); + RC.ru[j]->openair0_cfg.clock_source = internal; } if (config_isparamset(RUParamList.paramarray[j], RU_SDR_TME_SRC)) { if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "internal") == 0) { RC.ru[j]->openair0_cfg.time_source = internal; - LOG_D(PHY, "RU time source set as internal\n"); + LOG_I(PHY, "RU time source set as internal\n"); } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "external") == 0) { RC.ru[j]->openair0_cfg.time_source = external; - LOG_D(PHY, "RU time source set as external\n"); + LOG_I(PHY, "RU time source set as external\n"); } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "gpsdo") == 0) { RC.ru[j]->openair0_cfg.time_source = gpsdo; - LOG_D(PHY, "RU time source set as gpsdo\n"); + LOG_I(PHY, "RU time source set as gpsdo\n"); } else { LOG_E(PHY, "Erroneous RU time source in the provided configuration file: '%s'\n", *(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr)); } } else { - RC.ru[j]->openair0_cfg.time_source = unset; + LOG_I(PHY,"Setting time source to internal\n"); + RC.ru[j]->openair0_cfg.time_source = internal; } - + if (strcmp(*(RUParamList.paramarray[j][RU_LOCAL_RF_IDX].strptr), "yes") == 0) { if ( !(config_isparamset(RUParamList.paramarray[j],RU_LOCAL_IF_NAME_IDX)) ) { RC.ru[j]->if_south = LOCAL_RF; @@ -2050,7 +2040,7 @@ static void NRRCconfig_RU(void) RC.ru[j]->if_south = REMOTE_IF4p5; RC.ru[j]->function = NGFI_RAU_IF4p5; RC.ru[j]->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; - } + } } /* strcmp(local_rf, "yes") != 0 */ RC.ru[j]->nb_tx = *(RUParamList.paramarray[j][RU_NB_TX_IDX].uptr); diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 353751a66a..4727d09b04 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -98,7 +98,6 @@ pthread_cond_t sync_cond; pthread_mutex_t sync_mutex; int sync_var=-1; //!< protected by mutex \ref sync_mutex. int config_sync_var=-1; -msc_interface_t msc_interface; volatile int start_gNB = 0; volatile int oai_exit = 0; @@ -165,6 +164,8 @@ uint32_t timing_advance = 0; uint64_t num_missed_slots=0; // counter for the number of missed slots #include +#include + int split73=0; void sendFs6Ul(PHY_VARS_eNB *eNB, int UE_id, int harq_pid, int segmentID, int16_t *data, int dataLen, int r_offset) { AssertFatal(false, "Must not be called in this context\n"); @@ -344,7 +345,7 @@ int create_gNB_tasks(uint32_t gnb_nb) { } - if (AMF_MODE_ENABLED && (get_softmodem_params()->phy_test==0 && get_softmodem_params()->do_ra==0 && get_softmodem_params()->sa==0)) { + if (AMF_MODE_ENABLED) { if (gnb_nb > 0) { /* if (itti_create_task (TASK_SCTP, sctp_eNB_task, NULL) < 0) { @@ -369,25 +370,31 @@ int create_gNB_tasks(uint32_t gnb_nb) { } } - if (itti_create_task (TASK_GTPV1_U, >pv1u_gNB_task, NULL) < 0) { + /*if (itti_create_task (TASK_GTPV1_U, &nr_gtpv1u_gNB_task, NULL) < 0) { LOG_E(GTPU, "Create task for GTPV1U failed\n"); return -1; - } + }*/ } } - if (gnb_nb > 0) { if (itti_create_task (TASK_GNB_APP, gNB_app_task, NULL) < 0) { LOG_E(GNB_APP, "Create task for gNB APP failed\n"); return -1; } - LOG_I(NR_RRC,"Creating NR RRC gNB Task\n"); + LOG_I(NR_RRC,"Creating NR RRC gNB Task\n"); if (itti_create_task (TASK_RRC_GNB, rrc_gnb_task, NULL) < 0) { LOG_E(NR_RRC, "Create task for NR RRC gNB failed\n"); return -1; } + //Use check on x2ap to consider the NSA scenario and check on AMF_MODE_ENABLED for the SA scenario + if(is_x2ap_enabled() || AMF_MODE_ENABLED){ + if (itti_create_task (TASK_GTPV1_U, &nr_gtpv1u_gNB_task, NULL) < 0) { + LOG_E(GTPU, "Create task for GTPV1U failed\n"); + return -1; + } + } } return 0; @@ -623,33 +630,36 @@ static void wait_nfapi_init(char *thread_name) { } void init_pdcp(void) { - //if (!NODE_IS_DU(RC.rrc[0]->node_type)) { - pdcp_layer_init(); - uint32_t pdcp_initmask = (IS_SOFTMODEM_NOS1) ? - (PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT) : LINK_ENB_PDCP_TO_GTPV1U_BIT; + if (!NODE_IS_DU(RC.nrrrc[0]->node_type)) { + // pdcp_layer_init(); + uint32_t pdcp_initmask = (IS_SOFTMODEM_NOS1) ? + (PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT) : LINK_ENB_PDCP_TO_GTPV1U_BIT; + if (IS_SOFTMODEM_NOS1) { + printf("IS_SOFTMODEM_NOS1 option enabled \n"); + pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_BIT | SOFTMODEM_NOKRNMOD_BIT; + } - if (IS_SOFTMODEM_NOS1) { - printf("IS_SOFTMODEM_NOS1 option enabled \n"); - pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_BIT | SOFTMODEM_NOKRNMOD_BIT ; - } + pdcp_module_init(pdcp_initmask); - pdcp_module_init(pdcp_initmask); - - /*if (NODE_IS_CU(RC.rrc[0]->node_type)) { - pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t)proto_agent_send_rlc_data_req); - } else {*/ - pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t) rlc_data_req); - pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) pdcp_data_ind); - //} - /*} else { + if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { + LOG_I(PDCP, "node is CU, pdcp send rlc_data_req by proto_agent \n"); + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t)proto_agent_send_rlc_data_req); + } else { + LOG_I(PDCP, "node is gNB \n"); + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t) rlc_data_req); + pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) pdcp_data_ind); + } + } else { + LOG_I(PDCP, "node is DU, rlc send pdcp_data_ind by proto_agent \n"); pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) proto_agent_send_pdcp_data_ind); - }*/ + } } int main( int argc, char **argv ) { int ru_id, CC_id = 0; + start_background_system(); ///static configuration for NR at the moment @@ -675,8 +685,8 @@ int main( int argc, char **argv ) } openair0_cfg[0].threequarter_fs = threequarter_fs; - AMF_MODE_ENABLED = !IS_SOFTMODEM_NOS1; //!get_softmodem_params()->phy_test; - NGAP_CONF_MODE = !IS_SOFTMODEM_NOS1; //!get_softmodem_params()->phy_test; + AMF_MODE_ENABLED = get_softmodem_params()->sa; + NGAP_CONF_MODE = get_softmodem_params()->sa; if (get_softmodem_params()->do_ra) AssertFatal(get_softmodem_params()->phy_test == 0,"RA and phy_test are mutually exclusive\n"); @@ -717,15 +727,11 @@ if(!IS_SOFTMODEM_NOS1) #endif LOG_I(HW, "Version: %s\n", PACKAGE_VERSION); - if(IS_SOFTMODEM_NOS1) - init_pdcp(); - if (RC.nb_nr_L1_inst > 0) RCconfig_NR_L1(); - if (RC.nb_nr_inst > 0) { + // don't create if node doesn't connect to RRC/S1/GTP AssertFatal(create_gNB_tasks(1) == 0,"cannot create ITTI tasks\n"); - } /* Start the agent. If it is turned off in the configuration, it won't start */ /* @@ -806,36 +812,38 @@ if(!IS_SOFTMODEM_NOS1) wait_nfapi_init("main?"); } - printf("wait RUs\n"); - wait_RUs(); - printf("ALL RUs READY!\n"); - printf("RC.nb_RU:%d\n", RC.nb_RU); - // once all RUs are ready initialize the rest of the gNBs ((dependence on final RU parameters after configuration) - printf("ALL RUs ready - init gNBs\n"); - if(IS_SOFTMODEM_DOSCOPE) { - sleep(1); - scopeParms_t p; - p.argc=&argc; - p.argv=argv; - p.gNB=RC.gNB[0]; - p.ru=RC.ru[0]; - load_softscope("nr",&p); - } + if (RC.nb_nr_L1_inst > 0) { + printf("wait RUs\n"); + wait_RUs(); + printf("ALL RUs READY!\n"); + printf("RC.nb_RU:%d\n", RC.nb_RU); + // once all RUs are ready initialize the rest of the gNBs ((dependence on final RU parameters after configuration) + printf("ALL RUs ready - init gNBs\n"); + if(IS_SOFTMODEM_DOSCOPE) { + sleep(1); + scopeParms_t p; + p.argc=&argc; + p.argv=argv; + p.gNB=RC.gNB[0]; + p.ru=RC.ru[0]; + load_softscope("nr",&p); + } - if (NFAPI_MODE != NFAPI_MODE_PNF && NFAPI_MODE != NFAPI_MODE_VNF) { - printf("Not NFAPI mode - call init_eNB_afterRU()\n"); - init_eNB_afterRU(); - } else { - printf("NFAPI mode - DO NOT call init_gNB_afterRU()\n"); - } + if (NFAPI_MODE != NFAPI_MODE_PNF && NFAPI_MODE != NFAPI_MODE_VNF) { + printf("Not NFAPI mode - call init_eNB_afterRU()\n"); + init_eNB_afterRU(); + } else { + printf("NFAPI mode - DO NOT call init_gNB_afterRU()\n"); + } - printf("ALL RUs ready - ALL gNBs ready\n"); - // connect the TX/RX buffers - printf("Sending sync to all threads\n"); - pthread_mutex_lock(&sync_mutex); - sync_var=0; - pthread_cond_broadcast(&sync_cond); - pthread_mutex_unlock(&sync_mutex); + printf("ALL RUs ready - ALL gNBs ready\n"); + // connect the TX/RX buffers + printf("Sending sync to all threads\n"); + pthread_mutex_lock(&sync_mutex); + sync_var=0; + pthread_cond_broadcast(&sync_cond); + pthread_mutex_unlock(&sync_mutex); + } printf("About to call end_configmodule() from %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); end_configmodule(); printf("Called end_configmodule() from %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); @@ -873,7 +881,10 @@ if(!IS_SOFTMODEM_NOS1) printf("stopping MODEM threads\n"); // cleanup stop_gNB(NB_gNB_INST); - stop_RU(NB_RU); + + if (RC.nb_nr_L1_inst > 0) { + stop_RU(NB_RU); + } /* release memory used by the RU/gNB threads (incomplete), after all * threads have been stopped (they partially use the same memory) */ diff --git a/executables/nr-ue.c b/executables/nr-ue.c index a35a11a033..53ec70330c 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -290,6 +290,7 @@ void processSlotTX(void *arg) { int tx_slot_type = nr_ue_slot_select(cfg, proc->frame_tx, proc->nr_slot_tx); uint8_t gNB_id = 0; + LOG_D(PHY,"%d.%d => slot type %d\n",proc->frame_tx,proc->nr_slot_tx,tx_slot_type); if (tx_slot_type == NR_UPLINK_SLOT || tx_slot_type == NR_MIXED_SLOT){ // trigger L2 to run ue_scheduler thru IF module @@ -343,7 +344,7 @@ void processSlotRX(void *arg) { LOG_D(PHY, "In %s: slot %d, time %lu\n", __FUNCTION__, proc->nr_slot_rx, (rdtsc()-a)/3500); #endif - if(IS_SOFTMODEM_NOS1){ + if(IS_SOFTMODEM_NOS1 || get_softmodem_params()->sa){ NR_UE_MAC_INST_t *mac = get_mac_inst(0); protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, UE->Mod_id, ENB_FLAG_NO, mac->crnti, proc->frame_rx, proc->nr_slot_rx, 0); @@ -607,7 +608,7 @@ void *UE_thread(void *arg) { thread_idx = absolute_slot % NR_RX_NB_TH; int slot_nr = absolute_slot % nb_slot_frame; notifiedFIFO_elt_t *msgToPush; - AssertFatal((msgToPush=pullTpool(&freeBlocks,&(get_nrUE_params()->Tpool))) != NULL,"chained list failure"); + AssertFatal((msgToPush=pullNotifiedFIFO_nothreadSafe(&freeBlocks)) != NULL,"chained list failure"); nr_rxtx_thread_data_t *curMsg=(nr_rxtx_thread_data_t *)NotifiedFifoData(msgToPush); curMsg->UE=UE; // update thread index for received subframe @@ -714,7 +715,13 @@ void *UE_thread(void *arg) { if (openair0_cfg[0].duplex_mode == duplex_mode_TDD) { uint8_t tdd_period = mac->phy_config.config_req.tdd_table.tdd_period_in_slots; - int nrofUplinkSlots = mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots; + + int nrofUplinkSlots = 0; + if (mac->scc_SIB) + nrofUplinkSlots = mac->scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots; + else if (mac->scc) + nrofUplinkSlots = mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots; + uint8_t num_UL_slots = nrofUplinkSlots + (nrofUplinkSlots != 0); uint8_t first_tx_slot = tdd_period - num_UL_slots; diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 83b3d433aa..cbc79912a2 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -86,8 +86,11 @@ unsigned short config_frames[4] = {2,9,11,13}; #include "executables/softmodem-common.h" #include "executables/thread-common.h" +#if defined(ITTI_SIM) || defined(RFSIM_NAS) +#include "nr_nas_msg_sim.h" +#endif + extern const char *duplex_mode[]; -msc_interface_t msc_interface; THREAD_STRUCT thread_struct; nrUE_params_t nrUE_params; @@ -150,8 +153,9 @@ int transmission_mode = 1; int numerology = 0; int oaisim_flag = 0; int emulate_rf = 0; - -char uecap_xer[1024],uecap_xer_in=0; +uint32_t N_RB_DL = 106; +char uecap_xer_in = 0; +char uecap_xer[1024]; /* see file openair2/LAYER2/MAC/main.c for why abstraction_flag is needed * this is very hackish - find a proper solution @@ -191,7 +195,12 @@ int create_tasks_nrue(uint32_t ue_nb) { LOG_E(NR_RRC, "Create task for RRC UE failed\n"); return -1; } - +#if defined(ITTI_SIM) || defined(RFSIM_NAS) + if (itti_create_task (TASK_NAS_NRUE, nas_nrue_task, NULL) < 0) { + LOG_E(NR_RRC, "Create task for NAS UE failed\n"); + return -1; + } +#endif } itti_wait_ready(0); @@ -327,8 +336,9 @@ void set_options(int CC_id, PHY_VARS_NR_UE *UE){ LOG_I(PHY, "Set UE frame_type %d\n", fp->frame_type); } - LOG_I(PHY, "Set UE N_RB_DL %d\n", fp->N_RB_DL); + fp->N_RB_DL = N_RB_DL; + LOG_I(PHY, "Set UE N_RB_DL %d\n", N_RB_DL); LOG_I(PHY, "Set UE nb_rx_antenna %d, nb_tx_antenna %d, threequarter_fs %d\n", fp->nb_antennas_rx, fp->nb_antennas_tx, fp->threequarter_fs); } @@ -390,10 +400,10 @@ void init_pdcp(void) { } pdcp_layer_init(); nr_DRB_preconfiguration();*/ + pdcp_layer_init(); pdcp_module_init(pdcp_initmask); pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t) rlc_data_req); pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) pdcp_data_ind); - LOG_I(PDCP, "Before getting out from init_pdcp() \n"); } // Stupid function addition because UE itti messages queues definition is common with eNB @@ -445,7 +455,7 @@ int main( int argc, char **argv ) { LOG_I(HW, "Version: %s\n", PACKAGE_VERSION); init_NR_UE(1,rrc_config_path); - if(IS_SOFTMODEM_NOS1) + if(IS_SOFTMODEM_NOS1 || get_softmodem_params()->sa) init_pdcp(); NB_UE_INST=1; @@ -473,8 +483,22 @@ int main( int argc, char **argv ) { mac->if_module->phy_config_request(&mac->phy_config); fapi_nr_config_request_t *nrUE_config = &UE[CC_id]->nrUE_config; + if (get_softmodem_params()->sa) { // set frame config to initial values from command line and assume that the SSB is centered on the grid + nrUE_config->ssb_config.scs_common = get_softmodem_params()->numerology; + nrUE_config->carrier_config.dl_grid_size[nrUE_config->ssb_config.scs_common] = UE[CC_id]->frame_parms.N_RB_DL; + nrUE_config->carrier_config.ul_grid_size[nrUE_config->ssb_config.scs_common] = UE[CC_id]->frame_parms.N_RB_DL; + nrUE_config->carrier_config.dl_frequency = (downlink_frequency[0][0] -(6*UE[CC_id]->frame_parms.N_RB_DL*(15000<ssb_config.scs_common)))/1000; + nrUE_config->carrier_config.uplink_frequency = (downlink_frequency[0][0] -(6*UE[CC_id]->frame_parms.N_RB_DL*(15000<ssb_config.scs_common)))/1000; + nrUE_config->ssb_table.ssb_offset_point_a = (UE[CC_id]->frame_parms.N_RB_DL - 20)>>1; - nr_init_frame_parms_ue(&UE[CC_id]->frame_parms, nrUE_config, *mac->scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]); + // Initialize values, will be updated upon SIB1 reception + nrUE_config->cell_config.frame_duplex_type = TDD; + nrUE_config->ssb_table.ssb_mask_list[0].ssb_mask = 0xFFFFFFFF; + nrUE_config->ssb_table.ssb_period = 1; + } + + nr_init_frame_parms_ue(&UE[CC_id]->frame_parms, nrUE_config, + mac->scc == NULL ? 78 : *mac->scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]); init_symbol_rotation(&UE[CC_id]->frame_parms); init_nr_ue_vars(UE[CC_id], 0, abstraction_flag); @@ -496,7 +520,7 @@ int main( int argc, char **argv ) { configure_linux(); mlockall(MCL_CURRENT | MCL_FUTURE); - if(IS_SOFTMODEM_DOSCOPE) { + if(IS_SOFTMODEM_DOSCOPE) { load_softscope("nr",PHY_vars_UE_g[0][0]); } diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index 5c082b7bbb..be3ca2d1a2 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -55,7 +55,7 @@ {"ue-scan-carrier", CONFIG_HLP_UESCAN, PARAMFLAG_BOOL, iptr:&(UE->UE_scan_carrier), defintval:0, TYPE_INT, 0}, \ {"ue-fo-compensation", CONFIG_HLP_UEFO, PARAMFLAG_BOOL, iptr:&(UE->UE_fo_compensation), defintval:0, TYPE_INT, 0}, \ {"ue-max-power", NULL, 0, iptr:&(tx_max_power[0]), defintval:90, TYPE_INT, 0}, \ - {"r" , CONFIG_HLP_PRB, 0, iptr:&(fp->N_RB_DL), defintval:25, TYPE_UINT, 0}, \ + {"r" , CONFIG_HLP_PRB, 0, uptr:&(N_RB_DL), defuintval:160, TYPE_UINT, 0}, \ {"A" , CONFIG_HLP_TADV, 0, iptr:&(UE->timing_advance), defintval:0, TYPE_INT, 0}, \ {"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, u8ptr:&(fp->threequarter_fs), defintval:0, TYPE_UINT8, 0}, \ {"T" , CONFIG_HLP_TDD, PARAMFLAG_BOOL, iptr:&tddflag, defintval:0, TYPE_INT, 0}, \ diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index ad349d4d01..b6a2b18215 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -85,6 +85,7 @@ extern "C" #define CONFIG_HLP_TNOFORK "to ease debugging with gdb\n" #define CONFIG_HLP_NUMEROLOGY "adding numerology for 5G\n" +#define CONFIG_HLP_BAND "band index\n" #define CONFIG_HLP_EMULATE_RF "Emulated RF enabled(disable by defult)\n" #define CONFIG_HLP_PARALLEL_CMD "three config for level of parallelism 'PARALLEL_SINGLE_THREAD', 'PARALLEL_RU_L1_SPLIT', or 'PARALLEL_RU_L1_TRX_SPLIT'\n" #define CONFIG_HLP_WORKER_CMD "two option for worker 'WORKER_DISABLE' or 'WORKER_ENABLE'\n" @@ -111,6 +112,7 @@ extern "C" #define SINGLE_THREAD_FLAG softmodem_params.single_thread_flag #define CHAIN_OFFSET softmodem_params.chain_offset #define NUMEROLOGY softmodem_params.numerology +#define BAND softmodem_params.band #define EMULATE_RF softmodem_params.emulate_rf #define CLOCK_SOURCE softmodem_params.clock_source #define TIMING_SOURCE softmodem_params.timing_source @@ -125,22 +127,23 @@ extern "C" extern int usrp_tx_thread; #define CMDLINE_PARAMS_DESC { \ {"rf-config-file", CONFIG_HLP_RFCFGF, 0, strptr:(char **)&RF_CONFIG_FILE, defstrval:NULL, TYPE_STRING, sizeof(RF_CONFIG_FILE)},\ - {"split73", CONFIG_HLP_SPLIT73, 0, strptr:(char **)&SPLIT73, defstrval:NULL, TYPE_STRING, sizeof(SPLIT73)},\ - {"thread-pool", CONFIG_HLP_TPOOL, 0, strptr:(char **)&TP_CONFIG, defstrval:"n", TYPE_STRING, sizeof(TP_CONFIG)}, \ + {"split73", CONFIG_HLP_SPLIT73, 0, strptr:(char **)&SPLIT73, defstrval:NULL, TYPE_STRING, sizeof(SPLIT73)}, \ + {"thread-pool", CONFIG_HLP_TPOOL, 0, strptr:(char **)&TP_CONFIG, defstrval:"n", TYPE_STRING, sizeof(TP_CONFIG)}, \ {"phy-test", CONFIG_HLP_PHYTST, PARAMFLAG_BOOL, iptr:&PHY_TEST, defintval:0, TYPE_INT, 0}, \ {"do-ra", CONFIG_HLP_DORA, PARAMFLAG_BOOL, iptr:&DO_RA, defintval:0, TYPE_INT, 0}, \ - {"sa", CONFIG_HLP_SA, PARAMFLAG_BOOL, iptr:&SA, defintval:0, TYPE_INT, 0}, \ + {"sa", CONFIG_HLP_SA, PARAMFLAG_BOOL, iptr:&SA, defintval:0, TYPE_INT, 0}, \ {"usim-test", CONFIG_HLP_USIM, PARAMFLAG_BOOL, u8ptr:&USIM_TEST, defintval:0, TYPE_UINT8, 0}, \ - {"clock-source", CONFIG_HLP_CLK, 0, uptr:&CLOCK_SOURCE, defintval:0, TYPE_UINT, 0}, \ - {"time-source", CONFIG_HLP_TME, 0, uptr:&TIMING_SOURCE, defintval:0, TYPE_UINT, 0}, \ + {"clock-source", CONFIG_HLP_CLK, 0, uptr:&CLOCK_SOURCE, defintval:0, TYPE_UINT, 0}, \ + {"time-source", CONFIG_HLP_TME, 0, uptr:&TIMING_SOURCE, defintval:0, TYPE_UINT, 0}, \ {"wait-for-sync", NULL, PARAMFLAG_BOOL, iptr:&WAIT_FOR_SYNC, defintval:0, TYPE_INT, 0}, \ {"single-thread-enable", CONFIG_HLP_NOSNGLT, PARAMFLAG_BOOL, iptr:&SINGLE_THREAD_FLAG, defintval:0, TYPE_INT, 0}, \ - {"C" , CONFIG_HLP_DLF, 0, u64ptr:&(downlink_frequency[0][0]), defuintval:0, TYPE_UINT64, 0}, \ - {"CO" , CONFIG_HLP_ULF, 0, iptr:&(uplink_frequency_offset[0][0]), defintval:0, TYPE_INT, 0}, \ + {"C" , CONFIG_HLP_DLF, 0, u64ptr:&(downlink_frequency[0][0]), defuintval:3619200000, TYPE_UINT64, 0}, \ + {"CO" , CONFIG_HLP_ULF, 0, iptr:&(uplink_frequency_offset[0][0]), defintval:0, TYPE_INT, 0}, \ {"a" , CONFIG_HLP_CHOFF, 0, iptr:&CHAIN_OFFSET, defintval:0, TYPE_INT, 0}, \ {"d" , CONFIG_HLP_SOFTS, PARAMFLAG_BOOL, uptr:(uint32_t *)&do_forms, defintval:0, TYPE_INT8, 0}, \ {"q" , CONFIG_HLP_STMON, PARAMFLAG_BOOL, iptr:&opp_enabled, defintval:0, TYPE_INT, 0}, \ - {"numerology" , CONFIG_HLP_NUMEROLOGY, PARAMFLAG_BOOL, iptr:&NUMEROLOGY, defintval:0, TYPE_INT, 0}, \ + {"numerology" , CONFIG_HLP_NUMEROLOGY, PARAMFLAG_BOOL, iptr:&NUMEROLOGY, defintval:1, TYPE_INT, 0}, \ + {"band" , CONFIG_HLP_BAND, PARAMFLAG_BOOL, iptr:&BAND, defintval:78, TYPE_INT, 0}, \ {"emulate-rf" , CONFIG_HLP_EMULATE_RF, PARAMFLAG_BOOL, iptr:&EMULATE_RF, defintval:0, TYPE_INT, 0}, \ {"parallel-config", CONFIG_HLP_PARALLEL_CMD, 0, strptr:(char **)¶llel_config, defstrval:NULL, TYPE_STRING, 0}, \ {"worker-config", CONFIG_HLP_WORKER_CMD, 0, strptr:(char **)&worker_config, defstrval:NULL, TYPE_STRING, 0}, \ @@ -150,9 +153,9 @@ extern int usrp_tx_thread; {"nokrnmod", CONFIG_HLP_NOKRNMOD, PARAMFLAG_BOOL, uptr:&nokrnmod, defintval:0, TYPE_INT, 0}, \ {"nbiot-disable", CONFIG_HLP_DISABLNBIOT, PARAMFLAG_BOOL, uptr:&nonbiot, defuintval:0, TYPE_INT, 0}, \ {"use-256qam-table", CONFIG_HLP_256QAM, PARAMFLAG_BOOL, iptr:&USE_256QAM_TABLE, defintval:0, TYPE_INT, 0}, \ - {"usrp-tx-thread-config", CONFIG_HLP_USRP_THREAD, 0, iptr:&usrp_tx_thread, defstrval:0, TYPE_INT, 0}, \ - {"nfapi", CONFIG_HLP_NFAPI, 0, u8ptr:&nfapi_mode, defintval:0, TYPE_UINT8, 0}, \ - {"non-stop", CONFIG_HLP_NONSTOP, PARAMFLAG_BOOL, iptr:&NON_STOP, defintval:0, TYPE_INT, 0}, \ + {"usrp-tx-thread-config", CONFIG_HLP_USRP_THREAD, 0, iptr:&usrp_tx_thread, defstrval:0, TYPE_INT, 0}, \ + {"nfapi", CONFIG_HLP_NFAPI, 0, u8ptr:&nfapi_mode, defintval:0, TYPE_UINT8, 0}, \ + {"non-stop", CONFIG_HLP_NONSTOP, PARAMFLAG_BOOL, iptr:&NON_STOP, defintval:0, TYPE_INT, 0}, \ } @@ -236,6 +239,7 @@ typedef struct { int single_thread_flag; //eNodeB only int chain_offset; int numerology; + int band; unsigned int start_msc; uint32_t clock_source; uint32_t timing_source; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h index 0a83c1a27f..8e6bf9a802 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h @@ -14,7 +14,7 @@ /// RX_IND -#define FAPI_NR_RX_PDU_TYPE_MIB 0x01 +#define FAPI_NR_RX_PDU_TYPE_SSB 0x01 #define FAPI_NR_RX_PDU_TYPE_SIB 0x02 #define FAPI_NR_RX_PDU_TYPE_DLSCH 0x03 #define FAPI_NR_DCI_IND 0x04 @@ -45,6 +45,8 @@ #define FAPI_NR_DL_CONFIG_TYPE_DLSCH 0x02 #define FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH 0x03 #define FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH 0x04 +#define FAPI_NR_DL_CONFIG_TYPE_P_DLSCH 0x05 +#define FAPI_NR_DL_CONFIG_TYPES 0x05 #define FAPI_NR_CCE_REG_MAPPING_TYPE_INTERLEAVED 0x01 #define FAPI_NR_CCE_REG_MAPPING_TYPE_NON_INTERLEAVED 0x02 @@ -59,6 +61,6 @@ #define FAPI_NR_UL_CONFIG_TYPE_PUCCH 0x02 #define FAPI_NR_UL_CONFIG_TYPE_PUSCH 0x03 #define FAPI_NR_UL_CONFIG_TYPE_SRS 0x04 - +#define FAPI_NR_UL_CONFIG_TYPES 0x04 #endif diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index 0436f92a8d..c0abf6acb0 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -111,8 +111,8 @@ typedef struct { uint8_t ssb_index; uint8_t ssb_length; uint16_t cell_id; - -} fapi_nr_mib_pdu_t; + uint16_t ssb_start_subcarrier; +} fapi_nr_ssb_pdu_t; typedef struct { uint32_t pdu_length; @@ -124,7 +124,7 @@ typedef struct { uint8_t pdu_type; union { fapi_nr_pdsch_pdu_t pdsch_pdu; - fapi_nr_mib_pdu_t mib_pdu; + fapi_nr_ssb_pdu_t ssb_pdu; fapi_nr_sib_pdu_t sib_pdu; }; } fapi_nr_rx_indication_body_t; diff --git a/openair1/PHY/CODING/nr_rate_matching.c b/openair1/PHY/CODING/nr_rate_matching.c index 1afe82497a..b1ce6ab0de 100644 --- a/openair1/PHY/CODING/nr_rate_matching.c +++ b/openair1/PHY/CODING/nr_rate_matching.c @@ -493,8 +493,8 @@ int nr_rate_matching_ldpc_rx(uint8_t Ilbrm, } ind = (index_k0[BG-1][rvidx]*Ncb/N)*Z; - AssertFatal(Foffset <= E,"Foffset %d > E %d\n",Foffset,E); - AssertFatal(Foffset <= Ncb,"Foffset %d > Ncb %d\n",Foffset,Ncb); + AssertFatal(Foffset <= E,"Foffset %d > E %d\n",Foffset,E); + AssertFatal(Foffset <= Ncb,"Foffset %d > Ncb %d\n",Foffset,Ncb); #ifdef RM_DEBUG printf("nr_rate_matching_ldpc_rx: Clear %d, E %d, k0 %d, Ncb %d, rvidx %d\n", clear, E, ind, Ncb, rvidx); diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 5ab46ccda2..8189a7ab34 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -121,7 +121,7 @@ int nr_phy_init_RU(RU_t *ru) { AssertFatal(ru->num_gNB <= NUMBER_OF_gNB_MAX,"gNB instances %d > %d\n", ru->num_gNB,NUMBER_OF_gNB_MAX); - LOG_E(PHY,"[INIT] %s() ru->num_gNB:%d \n", __FUNCTION__, ru->num_gNB); + LOG_I(PHY,"[INIT] %s() ru->num_gNB:%d \n", __FUNCTION__, ru->num_gNB); int beam_count = 0; if (ru->nb_tx>1) {//Enable beamforming when nb_tx > 1 diff --git a/openair1/PHY/INIT/nr_parms.c b/openair1/PHY/INIT/nr_parms.c index 34938501e3..3a9d8a6c18 100644 --- a/openair1/PHY/INIT/nr_parms.c +++ b/openair1/PHY/INIT/nr_parms.c @@ -264,6 +264,9 @@ int nr_init_frame_parms_ue(NR_DL_FRAME_PARMS *fp, uint64_t dl_bw_khz = (12*config->carrier_config.dl_grid_size[config->ssb_config.scs_common])*(15<ssb_config.scs_common); fp->dl_CarrierFreq = ((dl_bw_khz>>1) + config->carrier_config.dl_frequency)*1000 ; + LOG_D(PHY,"dl_bw_kHz %lu\n",dl_bw_khz); + LOG_D(PHY,"dl_CarrierFreq %lu\n",fp->dl_CarrierFreq); + uint64_t ul_bw_khz = (12*config->carrier_config.ul_grid_size[config->ssb_config.scs_common])*(15<ssb_config.scs_common); fp->ul_CarrierFreq = ((ul_bw_khz>>1) + config->carrier_config.uplink_frequency)*1000 ; @@ -277,7 +280,7 @@ int nr_init_frame_parms_ue(NR_DL_FRAME_PARMS *fp, LOG_I(PHY, "Initializing frame parms: DL frequency %lu Hz, UL frequency %lu Hz: band %d, uldl offset %d Hz\n", fp->dl_CarrierFreq, fp->ul_CarrierFreq, fp->nr_band, uplink_frequency_offset); - AssertFatal(fp->frame_type==config->cell_config.frame_duplex_type, "Invalid duplex type in config request file for band %d\n", fp->nr_band); + AssertFatal(fp->frame_type==config->cell_config.frame_duplex_type, "Invalid duplex type (frame_type %d,cell_config.frame_duplex_type %d) in config request file for band %d\n", fp->frame_type,config->cell_config.frame_duplex_type,fp->nr_band); AssertFatal(fp->ul_CarrierFreq == (fp->dl_CarrierFreq + uplink_frequency_offset), "Disagreement in uplink frequency for band %d: ul_CarrierFreq = %lu Hz vs expected %lu Hz\n", fp->nr_band, fp->ul_CarrierFreq, fp->dl_CarrierFreq + uplink_frequency_offset); diff --git a/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c b/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c index eab9b3f321..0ce716ef20 100644 --- a/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c +++ b/openair1/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c @@ -35,7 +35,7 @@ static int16_t ru_90c[2*128] = {32767, 0,32766, -402,32758, -804,32746, -1206,32 #define SCALE 0x3FFF static const short conjugate[8]__attribute__((aligned(16))) = {-1,1,-1,1,-1,1,-1,1}; -static const short conjugate2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1}; +//static const short conjugate2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1}; extern unsigned short dftsizes[34]; extern int16_t *ul_ref_sigs_rx[30][2][34]; diff --git a/openair1/PHY/LTE_TRANSPORT/dci_tools.c b/openair1/PHY/LTE_TRANSPORT/dci_tools.c index f11a39e249..61af617d6e 100644 --- a/openair1/PHY/LTE_TRANSPORT/dci_tools.c +++ b/openair1/PHY/LTE_TRANSPORT/dci_tools.c @@ -61,12 +61,17 @@ int find_dlsch(uint16_t rnti, PHY_VARS_eNB *eNB,find_type_t type) { if (eNB->dlsch[i][0] == NULL) continue; LOG_D(PHY,"searching for rnti %x : UE index %d=> harq_mask %x, rnti %x, first_free_index %d\n", rnti,i,eNB->dlsch[i][0]->harq_mask,eNB->dlsch[i][0]->rnti,first_free_index); - if ((eNB->dlsch[i][0]->harq_mask >0) && - (eNB->dlsch[i][0]->rnti==rnti)) return i; - else if ((eNB->dlsch[i][0]->harq_mask == 0) && (first_free_index==-1)) first_free_index=i; + if (type == SEARCH_EXIST_RA) { + if (eNB->dlsch[i][0]->rnti==rnti) return i; + } else { + if ((eNB->dlsch[i][0]->harq_mask >0) && + (eNB->dlsch[i][0]->rnti==rnti)) return i; + else if ((eNB->dlsch[i][0]->harq_mask == 0) && (first_free_index==-1)) first_free_index=i; + } + } - if (type == SEARCH_EXIST) + if (type == SEARCH_EXIST_RA || type == SEARCH_EXIST) return -1; if (first_free_index != -1) @@ -83,12 +88,16 @@ int find_ulsch(uint16_t rnti, PHY_VARS_eNB *eNB,find_type_t type) { for (int i=0; iulsch[i]==NULL) continue; - if ((eNB->ulsch[i]->harq_mask >0) && - (eNB->ulsch[i]->rnti==rnti)) return i; - else if ((eNB->ulsch[i]->harq_mask == 0) && (first_free_index==-1)) first_free_index=i; + if (type == SEARCH_EXIST_RA) { + if (eNB->ulsch[i]->rnti == rnti) return i; + } else { + if ((eNB->ulsch[i]->harq_mask >0) && + (eNB->ulsch[i]->rnti==rnti)) return i; + else if ((eNB->ulsch[i]->harq_mask == 0) && (first_free_index==-1)) first_free_index=i; + } } - if (type == SEARCH_EXIST) + if (type == SEARCH_EXIST_RA || type == SEARCH_EXIST) return -1; if (first_free_index != -1) diff --git a/openair1/PHY/LTE_TRANSPORT/pucch.c b/openair1/PHY/LTE_TRANSPORT/pucch.c index 585a9d92a5..3270066448 100644 --- a/openair1/PHY/LTE_TRANSPORT/pucch.c +++ b/openair1/PHY/LTE_TRANSPORT/pucch.c @@ -982,7 +982,14 @@ uint32_t rx_pucch(PHY_VARS_eNB *eNB, break; } else if (first_uci_stats == NULL && eNB->uci_stats[i].rnti == 0) first_uci_stats = &eNB->uci_stats[i]; - if (uci_stats == NULL) { uci_stats=first_uci_stats; uci_stats->rnti = eNB->uci_vars[UCI_id].rnti;} + if (uci_stats == NULL) { + if (first_uci_stats == NULL) { + LOG_E(PHY,"first_uci_stats is NULL\n"); + return -1; + } + uci_stats=first_uci_stats; + uci_stats->rnti = eNB->uci_vars[UCI_id].rnti; + } AssertFatal(uci_stats!=NULL,"No stat index found\n"); uci_stats->frame = frame; diff --git a/openair1/PHY/LTE_TRANSPORT/transport_common.h b/openair1/PHY/LTE_TRANSPORT/transport_common.h index 7c1065736d..93ca7195de 100644 --- a/openair1/PHY/LTE_TRANSPORT/transport_common.h +++ b/openair1/PHY/LTE_TRANSPORT/transport_common.h @@ -93,7 +93,8 @@ #define PMI_2A_R1_1j 2 typedef enum { SEARCH_EXIST=0, - SEARCH_EXIST_OR_FREE + SEARCH_EXIST_OR_FREE, + SEARCH_EXIST_RA } find_type_t; typedef enum { diff --git a/openair1/PHY/MODULATION/slot_fep_nr.c b/openair1/PHY/MODULATION/slot_fep_nr.c index 0be635ba0b..39c730bfac 100644 --- a/openair1/PHY/MODULATION/slot_fep_nr.c +++ b/openair1/PHY/MODULATION/slot_fep_nr.c @@ -211,16 +211,19 @@ int nr_slot_fep_init_sync(PHY_VARS_NR_UE *ue, memset(&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol],0,frame_parms->ofdm_symbol_size*sizeof(int32_t)); int16_t *rxdata_ptr; + rx_offset%=frame_length_samples*2; - if (frame_length_samples - rx_offset < frame_parms->ofdm_symbol_size) { + if (rx_offset+frame_parms->ofdm_symbol_size > frame_length_samples*2 ) { + // rxdata is 2 frames len + // we have to wrap on the end memcpy((void *)&tmp_dft_in[0], (void *)&common_vars->rxdata[aa][rx_offset], - (frame_length_samples - rx_offset) * sizeof(int32_t)); - memcpy((void *)&tmp_dft_in[frame_length_samples - rx_offset], + (frame_length_samples*2 - rx_offset) * sizeof(int32_t)); + memcpy((void *)&tmp_dft_in[frame_length_samples*2 - rx_offset], (void *)&common_vars->rxdata[aa][0], - (frame_parms->ofdm_symbol_size - (frame_length_samples - rx_offset)) * sizeof(int32_t)); - rxdata_ptr = (int16_t *)&tmp_dft_in[0]; + (frame_parms->ofdm_symbol_size - (frame_length_samples*2 - rx_offset)) * sizeof(int32_t)); + rxdata_ptr = (int16_t *)tmp_dft_in; } else if ((rx_offset & 7) != 0) { @@ -228,7 +231,7 @@ int nr_slot_fep_init_sync(PHY_VARS_NR_UE *ue, memcpy((void *)&tmp_dft_in[0], (void *)&common_vars->rxdata[aa][rx_offset], frame_parms->ofdm_symbol_size * sizeof(int32_t)); - rxdata_ptr = (int16_t *)&tmp_dft_in[0]; + rxdata_ptr = (int16_t *)tmp_dft_in; } else { @@ -308,7 +311,7 @@ int nr_slot_fep_ul(NR_DL_FRAME_PARMS *frame_parms, memcpy((void *)&tmp_dft_in[sample_offset - rxdata_offset], (void *)&rxdata[0], (frame_parms->ofdm_symbol_size - sample_offset + rxdata_offset) * sizeof(int32_t)); - rxdata_ptr = (int16_t *)&tmp_dft_in[0]; + rxdata_ptr = (int16_t *)tmp_dft_in; } else if (((rxdata_offset - sample_offset) & 7) != 0) { @@ -316,7 +319,7 @@ int nr_slot_fep_ul(NR_DL_FRAME_PARMS *frame_parms, memcpy((void *)&tmp_dft_in[0], (void *)&rxdata[rxdata_offset - sample_offset], (frame_parms->ofdm_symbol_size) * sizeof(int32_t)); - rxdata_ptr = (int16_t *)&tmp_dft_in[0]; + rxdata_ptr = (int16_t *)tmp_dft_in; } else { diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index d3e33a91a8..91b6a59248 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -70,51 +70,81 @@ int nr_est_timing_advance_pusch(PHY_VARS_gNB* gNB, int UE_id) } -void gNB_I0_measurements(PHY_VARS_gNB *gNB) { +void dump_nr_I0_stats(FILE *fd,PHY_VARS_gNB *gNB) { + + + int min_I0=1000,max_I0=0; + int amin=0,amax=0; + for (int i=0; iframe_parms.N_RB_UL; i++) { + if (i==(gNB->frame_parms.N_RB_UL>>1) - 1) i+=2; + + if (gNB->measurements.n0_subband_power_tot_dB[i]measurements.n0_subband_power_tot_dB[i]; amin=i;} + + if (gNB->measurements.n0_subband_power_tot_dB[i]>max_I0) {max_I0 = gNB->measurements.n0_subband_power_tot_dB[i]; amax=i;} + } + + for (int i=0; iframe_parms.N_RB_UL; i++) { + fprintf(fd,"%2d.",gNB->measurements.n0_subband_power_tot_dB[i]-gNB->measurements.n0_subband_power_avg_dB); + if (i%25 == 24) fprintf(fd,"\n"); + } + + fprintf(fd,"\nmax_I0 %d (rb %d), min_I0 %d (rb %d), avg I0 %d\n", max_I0, amax, min_I0, amin, gNB->measurements.n0_subband_power_avg_dB); + + fprintf(fd,"PRACH I0 = %d.%d dB\n",gNB->measurements.prach_I0/10,gNB->measurements.prach_I0%10); + + +} + + + +void gNB_I0_measurements(PHY_VARS_gNB *gNB,int first_symb,int num_symb) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; NR_gNB_COMMON *common_vars = &gNB->common_vars; PHY_MEASUREMENTS_gNB *measurements = &gNB->measurements; - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - double rx_gain = openair0_cfg[0].rx_gain[0]; - double rx_gain_offset = openair0_cfg[0].rx_gain_offset[0]; uint32_t *rb_mask = gNB->rb_mask_ul; - int symbol = gNB->ulmask_symb; - int rb, offset, nb_rb; - uint32_t n0_subband_power_temp = 0; + int rb, offset, offset0, nb_rb, len; int32_t *ul_ch; + int32_t n0_power_tot; + int64_t n0_power_tot2; - if (symbol>-1) { - measurements->n0_power_tot = 0; - for (int aarx=0; aarxnb_antennas_rx; aarx++) { - nb_rb = 0; - for (rb=0; rbN_RB_UL; rb++) { - if ((rb_mask[rb>>5]&(1<<(rb&31))) == 0) { // check that rb was not used in this subframe - nb_rb++; - offset = (frame_parms->first_carrier_offset + (rb*12))%frame_parms->ofdm_symbol_size; - offset += (symbol*frame_parms->ofdm_symbol_size); - ul_ch = &common_vars->rxdataF[aarx][offset]; - //TODO what about DC? - n0_subband_power_temp += signal_energy_nodc(ul_ch,12); - } - } - - if (nb_rb != 0) { - measurements->n0_power[aarx] = n0_subband_power_temp/nb_rb; - measurements->n0_power_dB[aarx] = dB_fixed(measurements->n0_power[aarx]); - measurements->n0_power_tot += measurements->n0_power[aarx]; - } + nb_rb = 0; + n0_power_tot2=0; + for (rb=0; rbN_RB_UL; rb++) { + n0_power_tot=0; + offset0 = (frame_parms->first_carrier_offset + (rb*12))%frame_parms->ofdm_symbol_size; + if ((rb_mask[rb>>5]&(1<<(rb&31))) == 0) { // check that rb was not used in this subframe + nb_rb++; + for (int aarx=0; aarxnb_antennas_rx; aarx++) { + measurements->n0_subband_power[aarx][rb]=0; + for (int s=first_symb;s<(first_symb+num_symb);s++) { + offset = offset0 + (s*frame_parms->ofdm_symbol_size); + ul_ch = &common_vars->rxdataF[aarx][offset]; + len = 12; + if (((frame_parms->N_RB_UL&1) == 1) && + (rb==(frame_parms->N_RB_UL>>1))) { + len=6; + } + AssertFatal(ul_ch, "RX signal buffer (freq) problem\n"); + measurements->n0_subband_power[aarx][rb] += signal_energy_nodc(ul_ch,len); + } // symbol + measurements->n0_subband_power[aarx][rb]/=num_symb; + measurements->n0_subband_power_dB[aarx][rb] = dB_fixed(measurements->n0_subband_power[aarx][rb]); + n0_power_tot += measurements->n0_subband_power[aarx][rb]; + } //antenna + n0_power_tot/=frame_parms->nb_antennas_rx; + n0_power_tot2 += n0_power_tot; + measurements->n0_subband_power_tot_dB[rb] = dB_fixed(n0_power_tot); + measurements->n0_subband_power_tot_dBm[rb] = measurements->n0_subband_power_tot_dB[rb] - gNB->rx_total_gain_dB - dB_fixed(frame_parms->N_RB_UL); } + } //rb - measurements->n0_power_tot_dB = dB_fixed(measurements->n0_power_tot); - measurements->n0_power_tot_dBm = measurements->n0_power_tot_dB + 30 - 10 * log10(pow(2, 30)) - (rx_gain - rx_gain_offset) - dB_fixed(fp->ofdm_symbol_size); + if (nb_rb>0) measurements->n0_subband_power_avg_dB = dB_fixed(n0_power_tot2/nb_rb); - LOG_D(PHY, "In %s: tot n0 power %d dBm for %d RBs (tot N0 power = %d)\n", __FUNCTION__, measurements->n0_power_tot_dBm, nb_rb, measurements->n0_power_tot); - - } } + // Scope: This function computes the UL SNR from the UL channel estimates // // Todo: diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 625519b953..5e1528c922 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -80,7 +80,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, uint16_t nb_rb_pusch = pusch_pdu->rb_size; #ifdef DEBUG_CH - LOG_D(PHY, "In %s: ch_offset %d, symbol_offset %d OFDM size %d, Ns = %d, k = %d symbol %d\n", + LOG_I(PHY, "In %s: ch_offset %d, symbol_offset %d OFDM size %d, Ns = %d, k = %d symbol %d\n", __FUNCTION__, ch_offset, symbol_offset, diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 7bd004a4d4..89bb5b2807 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -47,7 +47,9 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, unsigned short bwp_start_subcarrier, nfapi_nr_pusch_pdu_t *pusch_pdu); -void gNB_I0_measurements(PHY_VARS_gNB *gNB); +void dump_nr_I0_stats(FILE *fd,PHY_VARS_gNB *gNB); + +void gNB_I0_measurements(PHY_VARS_gNB *gNB,int first_symb,int num_symb); void nr_gnb_measurements(PHY_VARS_gNB *gNB, uint8_t ulsch_id, unsigned char harq_pid, unsigned char symbol); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c index d208167855..ea96756184 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c @@ -94,6 +94,7 @@ void nr_generate_dci(PHY_VARS_gNB *gNB, * in time: by its first slot and its first symbol*/ const nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[d]; + LOG_D(PHY,"DCI pdu %d, rnti %x, aggregation %d CCE %d Scrambling_Id %x ScramblingRNTI %x PayloadSizeBits %d\n",d,dci_pdu->RNTI,dci_pdu->AggregationLevel,dci_pdu->CceIndex,dci_pdu->ScramblingId,dci_pdu->ScramblingRNTI,dci_pdu->PayloadSizeBits); cset_start_symb = pdcch_pdu_rel15->StartSymbolIndex; cset_nsymb = pdcch_pdu_rel15->DurationSymbols; dci_idx = 0; diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c index 0884c11a9b..c562dfaa41 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c @@ -255,12 +255,7 @@ void nr_fill_dci(PHY_VARS_gNB *gNB, dlsch->harq_mask |= (1<rnti = pdcch_pdu_rel15->dci_pdu[i].RNTI; - // nr_fill_cce_list(gNB,0); - /* - LOG_D(PHY, "DCI PDU: [0]->0x%lx \t [1]->0x%lx \n",dci_pdu[0], dci_pdu[1]); - LOG_D(PHY, "DCI type %d payload (size %d) generated on candidate %d\n", dci_alloc->pdcch_params.dci_format, dci_alloc->size, cand_idx); - */ - + // nr_fill_cce_list(gNB,0); } } diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 96918792a0..b1dbc7c7d9 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -86,7 +86,7 @@ void nr_pdsch_codeword_scrambling_optim(uint8_t *in, for (int i=0; i<((size>>5)+((size&0x1f) > 0 ? 1 : 0)); i++) { in32=_mm256_movemask_epi8(_mm256_slli_epi16(((__m256i*)in)[i],7)); out[i]=(in32^s); - // printf("in[%d] %x => %x\n",i,in32,out[i]); + //printf("in[%d] %x => %x\n",i,in32,out[i]); s=lte_gold_generic(&x1, &x2, 0); } #elif defined(__SSE4__) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index b441635246..1e372eb9e0 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -44,6 +44,7 @@ #include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/LOG/log.h" #include +#include //#define DEBUG_DLSCH_CODING //#define DEBUG_DLSCH_FREE 1 @@ -288,7 +289,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, } G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs,mod_order,rel15->nrOfLayers); - LOG_D(PHY,"dlsch coding A %d G %d mod_order %d\n", A,G, mod_order); + LOG_D(NR_PHY,"dlsch coding A %d G %d (nb_rb %d, nb_symb_sch %d, nb_re_dmrs %d, length_dmrs %d, mod_order %d)\n", A,G, nb_rb,nb_symb_sch,nb_re_dmrs,length_dmrs,mod_order); if (A > 3824) { // Add 24-bit crc (polynomial A) to payload @@ -406,7 +407,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, E = nr_get_E(G, harq->C, mod_order, rel15->nrOfLayers, r); //#ifdef DEBUG_DLSCH_CODING - LOG_D(PHY,"Rate Matching, Code segment %d/%d (coded bits (G) %u, E %d, Filler bits %d, Filler offset %d mod_order %d, nb_rb %d)...\n", + LOG_D(NR_PHY,"Rate Matching, Code segment %d/%d (coded bits (G) %u, E %d, Filler bits %d, Filler offset %d mod_order %d, nb_rb %d)...\n", r, harq->C, G, diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 6b22ece2bd..994ff18bc8 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -355,6 +355,7 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, nfapi_nr_pucch_pdu_t* pucch_pdu); void nr_decode_pucch0(PHY_VARS_gNB *gNB, + int frame, int slot, nfapi_nr_uci_pucch_pdu_format_0_1_t* uci_pdu, nfapi_nr_pucch_pdu_t* pucch_pdu); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index e6e8fdb01b..53ff90404b 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -137,12 +137,27 @@ void nr_ulsch_unscrambling_optim(int16_t* llr, #endif } -void dump_pusch_stats(PHY_VARS_gNB *gNB) { +#define STATSTRLEN 16384 +void dump_pusch_stats(FILE *fd,PHY_VARS_gNB *gNB) { - for (int i=0;inumber_of_nr_ulsch_max;i++) - if (gNB->ulsch_stats[i].rnti>0) - LOG_I(PHY,"ULSCH RNTI %x: round_trials %d(%1.1e):%d(%1.1e):%d(%1.1e):%d, current_Qm %d, current_RI %d, total_bytes RX/SCHED %d/%d\n", - gNB->ulsch_stats[i].rnti, + char output[16384]; + int stroff=0; + + for (int i=0;inumber_of_nr_ulsch_max;i++) { + if (gNB->ulsch_stats[i].rnti>0) { + for (int aa=0;aaframe_parms.nb_antennas_rx;aa++) + if (aa==0) stroff+=sprintf(output+stroff,"ULSCH RNTI %4x: ulsch_power[%d] %d,%d ulsch_noise_power[%d] %d.%d\n", + gNB->ulsch_stats[i].rnti, + aa,gNB->ulsch_stats[i].power[aa]/10,gNB->ulsch_stats[i].power[aa]%10, + aa,gNB->ulsch_stats[i].noise_power[aa]/10,gNB->ulsch_stats[i].noise_power[aa]%10); + else stroff+=sprintf(output+stroff," ulsch_power[%d] %d.%d, ulsch_noise_power[%d] %d.%d\n", + aa,gNB->ulsch_stats[i].power[aa]/10,gNB->ulsch_stats[i].power[aa]%10, + aa,gNB->ulsch_stats[i].noise_power[aa]/10,gNB->ulsch_stats[i].noise_power[aa]%10); + + AssertFatal(stroff<(STATSTRLEN-1000),"Increase STATSTRLEN\n"); + + + stroff+=sprintf(output+stroff," round_trials %d(%1.1e):%d(%1.1e):%d(%1.1e):%d, DTX %d, current_Qm %d, current_RI %d, total_bytes RX/SCHED %d/%d\n", gNB->ulsch_stats[i].round_trials[0], (double)gNB->ulsch_stats[i].round_trials[1]/gNB->ulsch_stats[i].round_trials[0], gNB->ulsch_stats[i].round_trials[1], @@ -150,11 +165,14 @@ void dump_pusch_stats(PHY_VARS_gNB *gNB) { gNB->ulsch_stats[i].round_trials[2], (double)gNB->ulsch_stats[i].round_trials[3]/gNB->ulsch_stats[i].round_trials[0], gNB->ulsch_stats[i].round_trials[3], + gNB->ulsch_stats[i].DTX, gNB->ulsch_stats[i].current_Qm, gNB->ulsch_stats[i].current_RI, gNB->ulsch_stats[i].total_bytes_rx, gNB->ulsch_stats[i].total_bytes_tx); - + } + } + fprintf(fd,"%s",output); } void clear_pusch_stats(PHY_VARS_gNB *gNB) { @@ -162,3 +180,20 @@ void clear_pusch_stats(PHY_VARS_gNB *gNB) { for (int i=0;inumber_of_nr_ulsch_max;i++) memset((void*)&gNB->ulsch_stats[i],0,sizeof(gNB->ulsch_stats[i])); } + +NR_gNB_SCH_STATS_t *get_ulsch_stats(PHY_VARS_gNB *gNB,NR_gNB_ULSCH_t *ulsch) { + NR_gNB_SCH_STATS_t *stats=NULL; + int first_free=-1; + for (int i=0;inumber_of_nr_ulsch_max;i++) { + if (gNB->ulsch_stats[i].rnti == 0 && first_free == -1) { + first_free = i; + stats=&gNB->ulsch_stats[i]; + } + if (gNB->ulsch_stats[i].rnti == ulsch->rnti) { + stats=&gNB->ulsch_stats[i]; + break; + } + } + return(stats); +} + diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h index de9b5f48c3..2ca8e24af1 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h @@ -89,6 +89,8 @@ void nr_ulsch_procedures(PHY_VARS_gNB *gNB, uint8_t harq_pid); int16_t find_nr_ulsch(uint16_t rnti, PHY_VARS_gNB *gNB,find_type_t type); -void dump_pusch_stats(PHY_VARS_gNB *gNB); +void dump_pusch_stats(FILE *fd,PHY_VARS_gNB *gNB); void clear_pusch_stats(PHY_VARS_gNB *gNB); + +NR_gNB_SCH_STATS_t *get_ulsch_stats(PHY_VARS_gNB *gNB,NR_gNB_ULSCH_t *ulsch); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index b572a6e5bb..3aad4980bc 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -449,7 +449,7 @@ void nr_processULSegment(void* arg) { } uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, - uint8_t UE_id, + uint8_t ULSCH_id, short *ulsch_llr, NR_DL_FRAME_PARMS *frame_parms, nfapi_nr_pusch_pdu_t *pusch_pdu, @@ -471,7 +471,8 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, #endif - NR_gNB_ULSCH_t *ulsch = phy_vars_gNB->ulsch[UE_id][0]; + NR_gNB_ULSCH_t *ulsch = phy_vars_gNB->ulsch[ULSCH_id][0]; + NR_gNB_PUSCH *pusch = phy_vars_gNB->pusch_vars[ULSCH_id]; NR_UL_gNB_HARQ_t *harq_process = ulsch->harq_processes[harq_pid]; if (!harq_process) { @@ -557,9 +558,11 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, if (stats) { stats->rnti = ulsch->rnti; stats->round_trials[harq_process->round]++; - } - if (harq_process->round == 0) { - if (stats) { + for (int aarx=0;aarxnb_antennas_rx;aarx++) { + stats->power[aarx]=dB_fixed_x10(pusch->ulsch_power[aarx]); + stats->noise_power[aarx]=dB_fixed_x10(pusch->ulsch_noise_power[aarx]); + } + if (harq_process->round == 0) { stats->current_Qm = Qm; stats->current_RI = n_layers; stats->total_bytes_tx += harq_process->TBS; @@ -639,7 +642,7 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, rdata->Tbslbrm = Tbslbrm; rdata->offset = offset; rdata->ulsch = ulsch; - rdata->ulsch_id = UE_id; + rdata->ulsch_id = ULSCH_id; pushTpool(phy_vars_gNB->threadPool,req); phy_vars_gNB->nbDecode++; LOG_D(PHY,"Added a block to decode, in pipe: %d\n",phy_vars_gNB->nbDecode); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 72db5488c4..7a4555d254 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -1184,7 +1184,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, start_meas(&gNB->ulsch_channel_estimation_stats); for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; - LOG_D(PHY,"symbol %d, dmrs_symbol_flag :%d\n", symbol, dmrs_symbol_flag); + LOG_D(PHY, "symbol %d, dmrs_symbol_flag :%d\n", symbol, dmrs_symbol_flag); if (dmrs_symbol_flag == 1) { if (gNB->pusch_vars[ulsch_id]->dmrs_symbol == INVALID_VALUE) gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; @@ -1200,10 +1200,18 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, nr_gnb_measurements(gNB, ulsch_id, harq_pid, symbol); for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] = signal_energy_nodc(&gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aarx][symbol*frame_parms->ofdm_symbol_size], - rel15_ul->rb_size*12); - if (gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] == 1) - return 1; + if (symbol == rel15_ul->start_symbol_index) { + gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] = 0; + gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] = 0; + } + gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( + &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aarx][symbol * frame_parms->ofdm_symbol_size], + rel15_ul->rb_size * 12); + for (int rb = 0; rb < rel15_ul->rb_size; rb++) { + gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] += + gNB->measurements.n0_subband_power[aarx][rel15_ul->bwp_start + rel15_ul->rb_start + rb] / + rel15_ul->rb_size; + } } } } diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 8145cf893c..2b5343aa60 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -161,6 +161,7 @@ int16_t idft12_im[12][12] = { void nr_decode_pucch0(PHY_VARS_gNB *gNB, + int frame, int slot, nfapi_nr_uci_pucch_pdu_format_0_1_t* uci_pdu, nfapi_nr_pucch_pdu_t* pucch_pdu) { @@ -177,6 +178,19 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, "Either bit_len_harq (%d) or sr_flag (%d) must be > 0\n", pucch_pdu->bit_len_harq,pucch_pdu->sr_flag); + NR_gNB_UCI_STATS_t *uci_stats=NULL; + NR_gNB_UCI_STATS_t *first_uci_stats=NULL; + for (int i=0;iuci_stats[i].rnti == pucch_pdu->rnti) { + uci_stats = &gNB->uci_stats[i]; + break; + } else if (first_uci_stats == NULL && gNB->uci_stats[i].rnti == 0) first_uci_stats = &gNB->uci_stats[i]; + + if (uci_stats == NULL) { uci_stats=first_uci_stats; uci_stats->rnti = pucch_pdu->rnti;} + + AssertFatal(uci_stats!=NULL,"No stat index found\n"); + uci_stats->frame = frame; + if(pucch_pdu->bit_len_harq==0){ mcs=table1_mcs; nr_sequences=1; @@ -190,6 +204,8 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, nr_sequences=8>>(1-pucch_pdu->sr_flag); } + LOG_D(PHY,"pucch0: nr_symbols %d, start_symbol %d, prb_start %d, second_hop_prb %d, group_hop_flag %d, sequence_hop_flag %d, O_ACK %d, O_SR %d, mcs %d\n",pucch_pdu->nr_of_symbols,pucch_pdu->start_symbol_index,pucch_pdu->prb_start,pucch_pdu->second_hop_prb,pucch_pdu->group_hop_flag,pucch_pdu->sequence_hop_flag,pucch_pdu->bit_len_harq,pucch_pdu->sr_flag,mcs[0]); + int cs_ind = get_pucch0_cs_lut_index(gNB,pucch_pdu); /* * Implement TS 38.211 Subclause 6.3.2.3.1 Sequence generation @@ -216,178 +232,133 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, * x(l*12+n) = r_u_v_alpha_delta(n) */ // the value of u,v (delta always 0 for PUCCH) has to be calculated according to TS 38.211 Subclause 6.3.2.2.1 - uint8_t u=0,v=0;//,delta=0; - // if frequency hopping is disabled by the higher-layer parameter PUCCH-frequency-hopping - // n_hop = 0 - // if frequency hopping is enabled by the higher-layer parameter PUCCH-frequency-hopping - // n_hop = 0 for first hop - // n_hop = 1 for second hop - uint8_t n_hop = 0; // Frequnecy hopping not implemented FIXME!! + uint8_t u[2]={0,0},v[2]={0,0}; // x_n contains the sequence r_u_v_alpha_delta(n) int n,i,l; - nr_group_sequence_hopping(pucch_GroupHopping,pucch_pdu->hopping_id,n_hop,slot,&u,&v); // calculating u and v value + int prb_offset[2] = {pucch_pdu->bwp_start+pucch_pdu->prb_start, pucch_pdu->bwp_start+pucch_pdu->prb_start}; - uint32_t re_offset=0; + nr_group_sequence_hopping(pucch_GroupHopping,pucch_pdu->hopping_id,0,slot,&u[0],&v[0]); // calculating u and v value first hop + LOG_D(PHY,"pucch0: u %d, v %d\n",u[0],v[0]); + + + if (pucch_pdu->freq_hop_flag == 1) { + nr_group_sequence_hopping(pucch_GroupHopping,pucch_pdu->hopping_id,1,slot,&u[1],&v[1]); // calculating u and v value second hop + LOG_D(PHY,"pucch0 second hop: u %d, v %d\n",u[1],v[1]); + prb_offset[1] = pucch_pdu->bwp_start+pucch_pdu->second_hop_prb; + } + + AssertFatal(pucch_pdu->nr_of_symbols < 3,"nr_of_symbols %d not allowed\n",pucch_pdu->nr_of_symbols); + uint32_t re_offset[2]={0,0}; uint8_t l2; -#ifdef OLD_IMPL - int16_t x_n_re[nr_sequences][24],x_n_im[nr_sequences][24]; + const int16_t *x_re[2],*x_im[2]; + x_re[0] = table_5_2_2_2_2_Re[u[0]]; + x_im[0] = table_5_2_2_2_2_Im[u[0]]; + x_re[1] = table_5_2_2_2_2_Re[u[1]]; + x_im[1] = table_5_2_2_2_2_Im[u[1]]; - for(i=0;inr_of_symbols; l++){ - double alpha = nr_cyclic_shift_hopping(pucch_pdu->hopping_id,pucch_pdu->initial_cyclic_shift,mcs[i],l,pucch_pdu->start_symbol_index,slot); -#ifdef DEBUG_NR_PUCCH_RX - printf("\t [nr_generate_pucch0] sequence generation \tu=%d \tv=%d \talpha=%lf \t(for symbol l=%d/%d,mcs %d)\n",u,v,alpha,l,l+pucch_pdu->start_symbol_index,mcs[i]); - printf("lut output %d\n",gNB->pucch0_lut.lut[cs_ind][slot][l+pucch_pdu->start_symbol_index]); -#endif - alpha=0.0; - for (n=0; n<12; n++){ - x_n_re[i][(12*l)+n] = (int16_t)((int16_t)(((((int32_t)(round(32767*cos(alpha*n))) * table_5_2_2_2_2_Re[u][n])>>15) - - (((int32_t)(round(32767*sin(alpha*n))) * table_5_2_2_2_2_Im[u][n])>>15)))); // Re part of base sequence shifted by alpha - x_n_im[i][(12*l)+n] =(int16_t)((int16_t)(((((int32_t)(round(32767*cos(alpha*n))) * table_5_2_2_2_2_Im[u][n])>>15) - + (((int32_t)(round(32767*sin(alpha*n))) * table_5_2_2_2_2_Re[u][n])>>15)))); // Im part of base sequence shifted by alpha -#ifdef DEBUG_NR_PUCCH_RX - printf("\t [nr_generate_pucch0] sequence generation \tu=%d \tv=%d \talpha=%lf \tx_n(l=%d,n=%d)=(%d,%d) %d,%d\n", - u,v,alpha,l,n,x_n_re[i][(12*l)+n],x_n_im[i][(12*l)+n], - (int32_t)(round(32767*cos(alpha*n))), - (int32_t)(round(32767*sin(alpha*n)))); -#endif - } - } - } - /* - * Implementing TS 38.211 Subclause 6.3.2.3.2 Mapping to physical resources - */ - - int16_t r_re[24],r_im[24]; - - for (l=0; lnr_of_symbols; l++) { - - l2 = l+pucch_pdu->start_symbol_index; - re_offset = (12*pucch_pdu->prb_start) + (12*pucch_pdu->bwp_start) + frame_parms->first_carrier_offset; - if (re_offset>= frame_parms->ofdm_symbol_size) - re_offset-=frame_parms->ofdm_symbol_size; - - for (n=0; n<12; n++){ - - r_re[(12*l)+n]=((int16_t *)&rxdataF[0][(l2*frame_parms->ofdm_symbol_size)+re_offset])[0]; - r_im[(12*l)+n]=((int16_t *)&rxdataF[0][(l2*frame_parms->ofdm_symbol_size)+re_offset])[1]; -#ifdef DEBUG_NR_PUCCH_RX - printf("\t [nr_generate_pucch0] mapping to RE \tofdm_symbol_size=%d \tN_RB_DL=%d \tfirst_carrier_offset=%d \ttxptr(%d)=(x_n(l=%d,n=%d)=(%d,%d))\n", - frame_parms->ofdm_symbol_size,frame_parms->N_RB_DL,frame_parms->first_carrier_offset,(l2*frame_parms->ofdm_symbol_size)+re_offset, - l,n,((int16_t *)&rxdataF[0][(l2*frame_parms->ofdm_symbol_size)+re_offset])[0], - ((int16_t *)&rxdataF[0][(l2*frame_parms->ofdm_symbol_size)+re_offset])[1]); -#endif - re_offset++; - if (re_offset>= frame_parms->ofdm_symbol_size) - re_offset-=frame_parms->ofdm_symbol_size; - } - } - double corr[nr_sequences],corr_re[nr_sequences],corr_im[nr_sequences]; - memset(corr,0,nr_sequences*sizeof(double)); - memset(corr_re,0,nr_sequences*sizeof(double)); - memset(corr_im,0,nr_sequences*sizeof(double)); - for(i=0;inr_of_symbols;l++){ - for(n=0;n<12;n++){ - corr_re[i]+= (double)(r_re[12*l+n])/32767*(double)(x_n_re[i][12*l+n])/32767+(double)(r_im[12*l+n])/32767*(double)(x_n_im[i][12*l+n])/32767; - corr_im[i]+= (double)(r_re[12*l+n])/32767*(double)(x_n_im[i][12*l+n])/32767-(double)(r_im[12*l+n])/32767*(double)(x_n_re[i][12*l+n])/32767; - } - } - corr[i]=corr_re[i]*corr_re[i]+corr_im[i]*corr_im[i]; - } - float max_corr=corr[0]; - uint8_t index=0; - for(i=1;imax_corr){ - index= i; - max_corr=corr[i]; - } - } -#else - - const int16_t *x_re = table_5_2_2_2_2_Re[u],*x_im = table_5_2_2_2_2_Im[u]; - int16_t xr[24] __attribute__((aligned(32))); - //int16_t xrt[24] __attribute__((aligned(32))); - int32_t xrtmag=0; + int16_t xr[2][24] __attribute__((aligned(32))); + int64_t xrtmag=0; uint8_t maxpos=0; - int n2=0; uint8_t index=0; - memset((void*)xr,0,24*sizeof(int16_t)); + memset((void*)xr[0],0,24*sizeof(int16_t)); + memset((void*)xr[1],0,24*sizeof(int16_t)); + int n2; for (l=0; lnr_of_symbols; l++) { - l2 = l+pucch_pdu->start_symbol_index; - re_offset = (12*pucch_pdu->prb_start) + frame_parms->first_carrier_offset; - if (re_offset>= frame_parms->ofdm_symbol_size) - re_offset-=frame_parms->ofdm_symbol_size; + re_offset[l] = (12*prb_offset[l]) + frame_parms->first_carrier_offset; + if (re_offset[l]>= frame_parms->ofdm_symbol_size) + re_offset[l]-=frame_parms->ofdm_symbol_size; - AssertFatal(re_offset+12 < frame_parms->ofdm_symbol_size,"pucch straddles DC carrier, handle this!\n"); + AssertFatal(re_offset[l]+12 < frame_parms->ofdm_symbol_size,"pucch straddles DC carrier, handle this!\n"); - int16_t *r=(int16_t*)&rxdataF[0][(l2*frame_parms->ofdm_symbol_size+re_offset)]; + int16_t *r=(int16_t*)&rxdataF[0][(l2*frame_parms->ofdm_symbol_size)+re_offset[l]]; + n2=0; for (n=0;n<12;n++,n2+=2) { - xr[n2] =(int16_t)(((int32_t)x_re[n]*r[n2]+(int32_t)x_im[n]*r[n2+1])>>15); - xr[n2+1]=(int16_t)(((int32_t)x_re[n]*r[n2+1]-(int32_t)x_im[n]*r[n2])>>15); + xr[l][n2] =(int16_t)(((int32_t)x_re[l][n]*r[n2]+(int32_t)x_im[l][n]*r[n2+1])>>15); + xr[l][n2+1]=(int16_t)(((int32_t)x_re[l][n]*r[n2+1]-(int32_t)x_im[l][n]*r[n2])>>15); #ifdef DEBUG_NR_PUCCH_RX - printf("x (%d,%d), r (%d,%d), xr (%d,%d)\n", - x_re[n],x_im[n],r[n2],r[n2+1],xr[n2],xr[n2+1]); + printf("x (%d,%d), r%d.%d (%d,%d), xr (%d,%d)\n", + x_re[l][n],x_im[l][n],l2,re_offset[l],r[n2],r[n2+1],xr[l][n2],xr[l][n2+1]); #endif } } - int32_t corr_re,corr_im,temp,no_corr=0; - int32_t av_corr=0; + + int32_t corr_re[2]; + int32_t corr_im[2]; + //int32_t no_corr = 0; int seq_index; + int64_t temp; + int64_t av_corr=0; for(i=0;inr_of_symbols;l++) { - + corr_re[l]=0; + corr_im[l]=0; seq_index = (pucch_pdu->initial_cyclic_shift+ mcs[i]+ gNB->pucch0_lut.lut[cs_ind][slot][l+pucch_pdu->start_symbol_index])%12; +#ifdef DEBUG_NR_PUCCH_RX + printf("PUCCH symbol %d seq %d, seq_index %d, mcs %d\n",l,i,seq_index,mcs[i]); +#endif + n2=0; for (n=0;n<12;n++,n2+=2) { - corr_re+=(xr[n2]*idft12_re[seq_index][n]+xr[n2+1]*idft12_im[seq_index][n])>>15; - corr_im+=(xr[n2]*idft12_im[seq_index][n]-xr[n2+1]*idft12_re[seq_index][n])>>15; + corr_re[l]+=(xr[l][n2]*idft12_re[seq_index][n]+xr[l][n2+1]*idft12_im[seq_index][n])>>15; + corr_im[l]+=(xr[l][n2]*idft12_im[seq_index][n]-xr[l][n2+1]*idft12_re[seq_index][n])>>15; } } #ifdef DEBUG_NR_PUCCH_RX - printf("PUCCH IDFT[%d/%d] = (%d,%d)=>%f\n",mcs[i],seq_index,corr_re,corr_im,10*log10(corr_re*corr_re + corr_im*corr_im)); + LOG_I(PHY,"PUCCH IDFT = (%d,%d)=>%f\n",corr_re[0],corr_im[0],10*log10((double)corr_re[0]*corr_re[0] + (double)corr_im[0]*corr_im[0])); + if (l>1) LOG_I(PHY,"PUCCH 2nd symbol IDFT[%d/%d] = (%d,%d)=>%f\n",mcs[i],seq_index,corr_re[1],corr_im[1],10*log10((double)corr_re[1]*corr_re[1] + (double)corr_im[1]*corr_im[1])); #endif - temp=corr_re*corr_re + corr_im*corr_im; + if (pucch_pdu->freq_hop_flag == 0 && l==1) // non-coherent correlation + temp=(int64_t)corr_re[0]*corr_re[0] + (int64_t)corr_im[0]*corr_im[0]; + else if (pucch_pdu->freq_hop_flag == 0 && l==2) { + int64_t corr_re2 = (int64_t)corr_re[0]+corr_re[1]; + int64_t corr_im2 = (int64_t)corr_im[0]+corr_im[1]; + // coherent combining of 2 symbols and then complex modulus for single-frequency case + temp=corr_re2*corr_re2 + corr_im2*corr_im2; + } + else if (pucch_pdu->freq_hop_flag == 1) + // full non-coherent combining of 2 symbols for frequency-hopping case + temp = (int64_t)corr_re[0]*corr_re[0] + (int64_t)corr_im[0]*corr_im[0] + (int64_t)corr_re[1]*corr_re[1] + (int64_t)corr_im[1]*corr_im[1]; + else AssertFatal(1==0,"shouldn't happen\n"); + av_corr+=temp; if (temp>xrtmag) { xrtmag=temp; maxpos=i; + uci_stats->current_pucch0_stat0 = dB_fixed64((int64_t)corr_re[0]*corr_re[0] + (int64_t)corr_im[0]*corr_im[0]); + if (l==2) uci_stats->current_pucch0_stat1 = dB_fixed64((int64_t)corr_re[1]*corr_re[1] + (int64_t)corr_im[1]*corr_im[1]); } } - if(nr_sequences>1) - no_corr=(av_corr-xrtmag)/(nr_sequences-1); - av_corr/=nr_sequences; - uint8_t xrtmag_dB = dB_fixed(xrtmag); + av_corr/=nr_sequences/l; + + uint8_t xrtmag_dB = dB_fixed64(xrtmag); #ifdef DEBUG_NR_PUCCH_RX printf("PUCCH 0 : maxpos %d\n",maxpos); #endif index=maxpos; -#endif // estimate CQI for MAC (from antenna port 0 only) - int SNRtimes10 = dB_fixed_times10(signal_energy_nodc(&rxdataF[0][pucch_pdu->start_symbol_index*frame_parms->ofdm_symbol_size+re_offset],12)) - (10*gNB->measurements.n0_power_tot_dB); + int max_n0 = uci_stats->pucch0_n00>uci_stats->pucch0_n01 ? uci_stats->pucch0_n00:uci_stats->pucch0_n01; + int SNRtimes10 = dB_fixed_times10(signal_energy_nodc(&rxdataF[0][pucch_pdu->start_symbol_index*frame_parms->ofdm_symbol_size+re_offset[0]],12)) - (10*max_n0); int cqi; if (SNRtimes10 < -640) cqi=0; else if (SNRtimes10 > 635) cqi=255; else cqi=(640+SNRtimes10)/5; + uci_stats->pucch0_thres = gNB->pucch0_thres + (10*max_n0); bool no_conf=false; if (nr_sequences>1) { - if ((xrtmag_dB<(11+dB_fixed(no_corr))) || (dB_fixed(av_corr)<(13+gNB->measurements.n0_power_tot_dB))) //TODO these are temporary threshold based on measurments with the phone + if (10*xrtmag_dB < uci_stats->pucch0_thres) no_conf=true; } gNB->bad_pucch += no_conf; @@ -397,13 +368,18 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, uci_pdu->rnti = pucch_pdu->rnti; uci_pdu->ul_cqi = cqi; uci_pdu->timing_advance = 0xffff; // currently not valid - uci_pdu->rssi = 1280 - (10*dB_fixed(32767*32767)-dB_fixed_times10(signal_energy_nodc(&rxdataF[0][pucch_pdu->start_symbol_index*frame_parms->ofdm_symbol_size+re_offset],12))); + uci_pdu->rssi = 1280 - (10*dB_fixed(32767*32767)-dB_fixed_times10(signal_energy_nodc(&rxdataF[0][pucch_pdu->start_symbol_index*frame_parms->ofdm_symbol_size+re_offset[0]],12))); + + uci_stats->pucch0_n00 = gNB->measurements.n0_subband_power_tot_dB[prb_offset[0]]; + uci_stats->pucch0_n01 = gNB->measurements.n0_subband_power_tot_dB[prb_offset[1]]; if (pucch_pdu->bit_len_harq==0) { uci_pdu->harq = NULL; uci_pdu->sr = calloc(1,sizeof(*uci_pdu->sr)); - uci_pdu->sr->sr_confidence_level = (xrtmag_dB<(13+gNB->measurements.n0_power_tot_dB)) ? 1 : 0; + uci_pdu->sr->sr_confidence_level = no_conf ? 1 : 0; + uci_stats->pucch0_sr_trials++; if (xrtmag_dB>(gNB->measurements.n0_power_tot_dB)) { uci_pdu->sr->sr_indication = 1; + uci_stats->pucch0_positive_SR++; } else { uci_pdu->sr->sr_indication = 0; } @@ -411,16 +387,18 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, else if (pucch_pdu->bit_len_harq==1) { uci_pdu->harq = calloc(1,sizeof(*uci_pdu->harq)); uci_pdu->harq->num_harq = 1; - uci_pdu->harq->harq_confidence_level = (no_conf) ? 1 : 0; + uci_pdu->harq->harq_confidence_level = no_conf ? 1 : 0; uci_pdu->harq->harq_list = (nfapi_nr_harq_t*)malloc(1); uci_pdu->harq->harq_list[0].harq_value = index&0x01; - LOG_D(PHY, "Slot %d HARQ value %d with confidence level (0 is good, 1 is bad) %d\n", - slot,uci_pdu->harq->harq_list[0].harq_value,uci_pdu->harq->harq_confidence_level); + LOG_D(PHY, "Slot %d HARQ value %d with confidence level (0 is good, 1 is bad) %d xrt_mag %d n0 %d pucch0_thres %d\n", + slot,uci_pdu->harq->harq_list[0].harq_value,uci_pdu->harq->harq_confidence_level,xrtmag_dB,max_n0,uci_stats->pucch0_thres); if (pucch_pdu->sr_flag == 1) { uci_pdu->sr = calloc(1,sizeof(*uci_pdu->sr)); uci_pdu->sr->sr_indication = (index>1) ? 1 : 0; - uci_pdu->sr->sr_confidence_level = (no_conf) ? 1 : 0; + uci_pdu->sr->sr_confidence_level = no_conf ? 1 : 0; + uci_stats->pucch0_positive_SR++; } + uci_stats->pucch01_trials++; } else { uci_pdu->harq = calloc(1,sizeof(*uci_pdu->harq)); @@ -429,8 +407,8 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, uci_pdu->harq->harq_list = (nfapi_nr_harq_t*)malloc(2); uci_pdu->harq->harq_list[1].harq_value = index&0x01; uci_pdu->harq->harq_list[0].harq_value = (index>>1)&0x01; - LOG_D(PHY, "Slot %d HARQ values %d and %d with confidence level (0 is good, 1 is bad) %d\n", - slot,uci_pdu->harq->harq_list[1].harq_value,uci_pdu->harq->harq_list[0].harq_value,uci_pdu->harq->harq_confidence_level); + LOG_D(PHY, "Slot %d HARQ values %d and %d with confidence level (0 is good, 1 is bad) %d, xrt_mag %d\n", + slot,uci_pdu->harq->harq_list[1].harq_value,uci_pdu->harq->harq_list[0].harq_value,uci_pdu->harq->harq_confidence_level,xrtmag_dB); if (pucch_pdu->sr_flag == 1) { uci_pdu->sr = calloc(1,sizeof(*uci_pdu->sr)); uci_pdu->sr->sr_indication = (index>3) ? 1 : 0; @@ -1663,4 +1641,33 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, uci_pdu->pduBitmap|=8; } } - + +void nr_dump_uci_stats(FILE *fd,PHY_VARS_gNB *gNB,int frame) { + + int strpos=0; + char output[16384]; + + for (int i=0;iuci_stats[i].rnti>0) { + NR_gNB_UCI_STATS_t *uci_stats = &gNB->uci_stats[i]; + if (uci_stats->pucch0_sr_trials > 0) + strpos+=sprintf(output+strpos,"UCI %d RNTI %x: pucch0_sr_trials %d, pucch0_n00 %d dB, pucch0_n01 %d dB, pucch0_sr_thres %d dB, current pucch1_stat0 %d dB, current pucch1_stat1 %d dB, positive SR count %d\n", + i,uci_stats->rnti,uci_stats->pucch0_sr_trials,uci_stats->pucch0_n00,uci_stats->pucch0_n01,uci_stats->pucch0_sr_thres,dB_fixed(uci_stats->current_pucch0_sr_stat0),dB_fixed(uci_stats->current_pucch0_sr_stat1),uci_stats->pucch0_positive_SR); + if (uci_stats->pucch01_trials > 0) + strpos+=sprintf(output+strpos,"UCI %d RNTI %x: pucch01_trials %d, pucch0_n00 %d dB, pucch0_n01 %d dB, pucch0_thres %d dB, current pucch0_stat0 %d dB, current pucch1_stat1 %d dB, pucch01_DTX %d\n", + i,uci_stats->rnti,uci_stats->pucch01_trials,uci_stats->pucch0_n01,uci_stats->pucch0_n01,uci_stats->pucch0_thres,dB_fixed(uci_stats->current_pucch0_stat0),dB_fixed(uci_stats->current_pucch0_stat1),uci_stats->pucch01_DTX); + + if (uci_stats->pucch02_trials > 0) + strpos+=sprintf(output+strpos,"UCI %d RNTI %x: pucch01_trials %d, pucch0_n00 %d dB, pucch0_n01 %d dB, pucch0_thres %d dB, current pucch0_stat0 %d dB, current pucch0_stat1 %d dB, pucch01_DTX %d\n", + i,uci_stats->rnti,uci_stats->pucch02_trials,uci_stats->pucch0_n00,uci_stats->pucch0_n01,uci_stats->pucch0_thres,dB_fixed(uci_stats->current_pucch0_stat0),dB_fixed(uci_stats->current_pucch0_stat1),uci_stats->pucch02_DTX); + + if (uci_stats->pucch2_trials > 0) + strpos+=sprintf(output+strpos,"UCI %d RNTI %x: pucch2_trials %d, pucch2_DTX %d\n", + i,uci_stats->rnti, + uci_stats->pucch2_trials, + uci_stats->pucch2_DTX); + } + } + if (fd) fprintf(fd,"%s",output); + else printf("%s",output); +} diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c index 0318ed6478..a2f824e468 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c @@ -105,7 +105,7 @@ void nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, { first_time = 0; ue->time_sync_cell = 1; - if (get_softmodem_params()->do_ra) { + if (get_softmodem_params()->do_ra || get_softmodem_params()->sa) { LOG_I(PHY,"[UE%d] Sending synch status to higher layers\n",ue->Mod_id); //mac_resynch(); //dl_phy_sync_success(ue->Mod_id,frame,0,1);//ue->common_vars.eNb_id); diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c index 3f52b29281..44f1844ae9 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c @@ -180,11 +180,11 @@ void nr_ue_measurements(PHY_VARS_NR_UE *ue, ue->measurements.wideband_cqi_avg[gNB_id] = dB_fixed2(ue->measurements.rx_power_avg[gNB_id], ue->measurements.n0_power_avg); ue->measurements.rx_rssi_dBm[gNB_id] = ue->measurements.rx_power_avg_dB[gNB_id] + 30 - 10*log10(pow(2, 30)) - ((int)openair0_cfg[0].rx_gain[0] - (int)openair0_cfg[0].rx_gain_offset[0]) - dB_fixed(ue->frame_parms.ofdm_symbol_size); - LOG_I(PHY, "[gNB %d] Slot %d, RSSI %d dBm/RE, RSSI (digital) %d dB, WBandCQI %d dB, rxPwrAvg %d, n0PwrAvg %d \n", + LOG_D(PHY, "[gNB %d] Slot %d, RSSI %d dB (%d dBm/RE), WBandCQI %d dB, rxPwrAvg %d, n0PwrAvg %d\n", gNB_id, slot, - ue->measurements.rx_rssi_dBm[gNB_id], ue->measurements.rx_power_avg_dB[gNB_id], + ue->measurements.rx_rssi_dBm[gNB_id], ue->measurements.wideband_cqi_avg[gNB_id], ue->measurements.rx_power_avg[gNB_id], ue->measurements.n0_power_tot); diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 15303bb0c8..5d4d9df47e 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -50,6 +50,16 @@ #include "assertions.h" #include "T.h" +char nr_dci_format_string[8][30] = { + "NR_DL_DCI_FORMAT_1_0", + "NR_DL_DCI_FORMAT_1_1", + "NR_DL_DCI_FORMAT_2_0", + "NR_DL_DCI_FORMAT_2_1", + "NR_DL_DCI_FORMAT_2_2", + "NR_DL_DCI_FORMAT_2_3", + "NR_UL_DCI_FORMAT_0_0", + "NR_UL_DCI_FORMAT_0_1"}; + //#define DEBUG_DCI_DECODING 1 //#define NR_LTE_PDCCH_DCI_SWITCH @@ -722,6 +732,10 @@ int32_t nr_rx_pdcch(PHY_VARS_NR_UE *ue, rel15 = &pdcch_vars->pdcch_config[i]; int n_rb,rb_offset; get_coreset_rballoc(rel15->coreset.frequency_domain_resource,&n_rb,&rb_offset); + + LOG_D(PHY,"pdcch coreset: freq %x, n_rb %d, rb_offset %d\n", + rel15->coreset.frequency_domain_resource[0],n_rb,rb_offset); + for (int s=rel15->coreset.StartSymbolIndex; s<(rel15->coreset.StartSymbolIndex+rel15->coreset.duration); s++) { LOG_D(PHY,"in nr_pdcch_extract_rbs_single(rxdataF -> rxdataF_ext || dl_ch_estimates -> dl_ch_estimates_ext)\n"); @@ -890,7 +904,7 @@ uint16_t nr_dci_false_detection(uint64_t *dci, int rnti) { uint32_t encoder_output[NR_MAX_DCI_SIZE_DWORD]; - polar_encoder_fast(dci, (void*)encoder_output, rnti, 1, polar_param); + polar_encoder_fast(dci, (void*)encoder_output, rnti, 1, (t_nrPolar_params *)polar_param); uint8_t *enout_p = (uint8_t*)encoder_output; uint16_t x = 0; @@ -949,10 +963,10 @@ uint8_t nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue, currentPtrDCI); n_rnti = rel15->rnti; - + LOG_D(PHY, "(%i.%i) dci indication (rnti %x,dci format %s,n_CCE %d,payloadSize %d)\n", proc->frame_rx, proc->nr_slot_rx,n_rnti,nr_dci_format_string[rel15->dci_format_options[k]],CCEind,dci_length); if (crc == n_rnti) { - LOG_D(PHY, "(%i.%i) Received dci indication (rnti %x,dci format %d,n_CCE %d,payloadSize %d,payload %llx)\n", - proc->frame_rx, proc->nr_slot_rx,n_rnti,rel15->dci_format_options[k],CCEind,dci_length,*(unsigned long long*)dci_estimation); + LOG_D(PHY, "(%i.%i) Received dci indication (rnti %x,dci format %s,n_CCE %d,payloadSize %d,payload %llx)\n", + proc->frame_rx, proc->nr_slot_rx,n_rnti,nr_dci_format_string[rel15->dci_format_options[k]],CCEind,dci_length,*(unsigned long long*)dci_estimation); uint16_t mb = nr_dci_false_detection(dci_estimation,tmp_e,currentPtrDCI,L*108,n_rnti); ue->dci_thres = (ue->dci_thres + mb) / 2; if (mb > (ue->dci_thres+20)) { diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c index d5c0245fdc..4f6871ca4f 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c @@ -588,7 +588,6 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, } else { LOG_D(PHY,"CRC NOT OK\n\033[0m"); - ret = 1 + dlsch->max_ldpc_iterations; } @@ -691,8 +690,8 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, harq_process->F>>3, (void *)(uint64_t)(harq_process->c[r]) ); if (frame%100 == 0){ - LOG_D (PHY, "Printing 10 first payload bytes at frame: %d ", frame); - for (int i = 0; i <10 ; i++){ //Kr_bytes + LOG_D (PHY, "Printing 60 first payload bytes at frame: %d ", frame); + for (int i = 0; i <60 ; i++){ //Kr_bytes LOG_D(PHY, "[%d] : %x ", i, harq_process->b[i]); } } @@ -1122,7 +1121,6 @@ uint32_t nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue, ret = 2; } else { - LOG_D(PHY,"CRC NOK\n"); ret = 1+dlsch->max_ldpc_iterations; } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index 35baef1792..2046c13eaa 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -3367,99 +3367,6 @@ static int nr_dlsch_llr(NR_UE_PDSCH **pdsch_vars, } //============================================================================================== -#ifdef USER_MODE - - -void dump_dlsch2(PHY_VARS_UE *ue,uint8_t gNB_id,uint8_t nr_slot_rx,unsigned int *coded_bits_per_codeword,int round, unsigned char harq_pid) -{ - unsigned int nsymb = (ue->frame_parms.Ncp == 0) ? 14 : 12; - char fname[32],vname[32]; - int N_RB_DL=ue->frame_parms.N_RB_DL; - - snprintf(fname, 32, "dlsch%d_rxF_r%d_ext0.m", gNB_id, round); - snprintf(vname, 32, "dl%d_rxF_r%d_ext0", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->rxdataF_ext[0],12*N_RB_DL*nsymb,1,1); - - if (ue->frame_parms.nb_antennas_rx >1) { - snprintf(fname, 32, "dlsch%d_rxF_r%d_ext1.m", gNB_id, round); - snprintf(vname, 32, "dl%d_rxF_r%d_ext1", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->rxdataF_ext[1],12*N_RB_DL*nsymb,1,1); - } - - snprintf(fname, 32, "dlsch%d_ch_r%d_ext00.m", gNB_id, round); - snprintf(vname, 32, "dl%d_ch_r%d_ext00", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_estimates_ext[0],12*N_RB_DL*nsymb,1,1); - - if (ue->transmission_mode[gNB_id]==7){ - snprintf(fname, 32, "dlsch%d_bf_ch_r%d.m", gNB_id, round); - snprintf(vname, 32, "dl%d_bf_ch_r%d", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_bf_ch_estimates[0],512*nsymb,1,1); - //write_output(fname,vname,phy_vars_ue->lte_ue_pdsch_vars[gNB_id]->dl_bf_ch_estimates[0],512,1,1); - - snprintf(fname, 32, "dlsch%d_bf_ch_r%d_ext00.m", gNB_id, round); - snprintf(vname, 32, "dl%d_bf_ch_r%d_ext00", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_bf_ch_estimates_ext[0],12*N_RB_DL*nsymb,1,1); - } - - if (ue->frame_parms.nb_antennas_rx == 2) { - snprintf(fname, 32, "dlsch%d_ch_r%d_ext01.m", gNB_id, round); - snprintf(vname, 32, "dl%d_ch_r%d_ext01", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_estimates_ext[1],12*N_RB_DL*nsymb,1,1); - } - - if (ue->frame_parms.nb_antenna_ports_gNB == 2) { - snprintf(fname, 32, "dlsch%d_ch_r%d_ext10.m", gNB_id, round); - snprintf(vname, 32, "dl%d_ch_r%d_ext10", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_estimates_ext[2],12*N_RB_DL*nsymb,1,1); - - if (ue->frame_parms.nb_antennas_rx == 2) { - snprintf(fname, 32, "dlsch%d_ch_r%d_ext11.m",gNB_id,round); - snprintf(vname, 32, "dl%d_ch_r%d_ext11",gNB_id,round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_estimates_ext[3],12*N_RB_DL*nsymb,1,1); - } - } - - snprintf(fname, 32, "dlsch%d_rxF_r%d_uespec0.m", gNB_id, round); - snprintf(vname, 32, "dl%d_rxF_r%d_uespec0", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->rxdataF_uespec_pilots[0],12*N_RB_DL,1,1); - - /* - write_output("dlsch%d_ch_ext01.m","dl01_ch0_ext",pdsch_vars[gNB_id]->dl_ch_estimates_ext[1],12*N_RB_DL*nsymb,1,1); - write_output("dlsch%d_ch_ext10.m","dl10_ch0_ext",pdsch_vars[gNB_id]->dl_ch_estimates_ext[2],12*N_RB_DL*nsymb,1,1); - write_output("dlsch%d_ch_ext11.m","dl11_ch0_ext",pdsch_vars[gNB_id]->dl_ch_estimates_ext[3],12*N_RB_DL*nsymb,1,1); - */ - snprintf(fname, 32, "dlsch%d_r%d_rho.m", gNB_id, round); - snprintf(vname, 32, "dl_rho_r%d_%d", gNB_id, round); - - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_rho_ext[harq_pid][round][0],12*N_RB_DL*nsymb,1,1); - - snprintf(fname, 32, "dlsch%d_r%d_rho2.m", gNB_id, round); - snprintf(vname, 32, "dl_rho2_r%d_%d", gNB_id, round); - - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_rho2_ext[0],12*N_RB_DL*nsymb,1,1); - - snprintf(fname, 32, "dlsch%d_rxF_r%d_comp0.m", gNB_id, round); - snprintf(vname, 32, "dl%d_rxF_r%d_comp0", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->rxdataF_comp0[0],12*N_RB_DL*nsymb,1,1); - if (ue->frame_parms.nb_antenna_ports_gNB == 2) { - snprintf(fname, 32, "dlsch%d_rxF_r%d_comp1.m", gNB_id, round); - snprintf(vname, 32, "dl%d_rxF_r%d_comp1", gNB_id, round); - write_output(fname,vname,ue->pdsch_vars[proc->thread_id][gNB_id]->rxdataF_comp1[harq_pid][round][0],12*N_RB_DL*nsymb,1,1); - } - - snprintf(fname, 32, "dlsch%d_rxF_r%d_llr.m", gNB_id, round); - snprintf(vname, 32, "dl%d_r%d_llr", gNB_id, round); - write_output(fname, vname, ue->pdsch_vars[proc->thread_id][gNB_id]->llr[0], coded_bits_per_codeword[0], 1, 0); - snprintf(fname, 32, "dlsch%d_r%d_mag1.m", gNB_id, round); - snprintf(vname, 32, "dl%d_r%d_mag1", gNB_id, round); - write_output(fname, vname, ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_mag0[0], 12*N_RB_DL*nsymb, 1, 1); - snprintf(fname, 32, "dlsch%d_r%d_mag2.m", gNB_id, round); - snprintf(vname, 32, "dl%d_r%d_mag2", gNB_id, round); - write_output(fname, vname, ue->pdsch_vars[proc->thread_id][gNB_id]->dl_ch_magb0[0], 12*N_RB_DL*nsymb, 1, 1); - - // printf("log2_maxh = %d\n",ue->pdsch_vars[gNB_id]->log2_maxh); -} -#endif #ifdef DEBUG_DLSCH_DEMOD /* diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c index 66c3de97ea..f0e6e9984b 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c @@ -613,7 +613,7 @@ int nr_rx_pbch( PHY_VARS_NR_UE *ue, uint16_t number_pdus = 1; nr_fill_dl_indication(&dl_indication, NULL, &rx_ind, proc, ue, gNB_id); - nr_fill_rx_indication(&rx_ind, FAPI_NR_RX_PDU_TYPE_MIB, gNB_id, ue, NULL, number_pdus); + nr_fill_rx_indication(&rx_ind, FAPI_NR_RX_PDU_TYPE_SSB, gNB_id, ue, NULL, number_pdus); if (ue->if_inst && ue->if_inst->dl_indication) ue->if_inst->dl_indication(&dl_indication, NULL); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c index 2c4c0b9e14..0d372deedf 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c @@ -66,7 +66,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, uint8_t slot){ uint8_t Mod_id, fd_occasion, preamble_index, restricted_set, not_found; uint16_t rootSequenceIndex, prach_fmt_id, NCS, *prach_root_sequence_map, preamble_offset = 0; - uint16_t preamble_shift = 0, preamble_index0, n_shift_ra, n_shift_ra_bar, d_start, numshift, N_ZC, u, offset, offset2, first_nonzero_root_idx; + uint16_t preamble_shift = 0, preamble_index0, n_shift_ra, n_shift_ra_bar, d_start=0, numshift, N_ZC, u, offset, offset2, first_nonzero_root_idx; int16_t prach_tmp[98304*2*4] __attribute__((aligned(32))); int16_t Ncp = 0, amp, *prach, *prach2, *prachF, *Xu; @@ -74,6 +74,10 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, uint8_t slot){ int prach_start, prach_sequence_length, i, prach_len, dftlen, mu, kbar, K, n_ra_prb, k, prachStartSymbol, sample_offset_slot; //int restricted_Type; + fd_occasion = 0; + prach_len = 0; + dftlen = 0; + first_nonzero_root_idx = 0; prach = prach_tmp; prachF = ue->prach_vars[gNB_id]->prachF; amp = ue->prach_vars[gNB_id]->amp; @@ -83,14 +87,10 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, uint8_t slot){ mu = nrUE_config->prach_config.prach_sub_c_spacing; restricted_set = prach_pdu->restricted_set; rootSequenceIndex = prach_pdu->root_seq_id; - n_ra_prb = prach_pdu->freq_msg1; + n_ra_prb = nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].k1,//prach_pdu->freq_msg1; NCS = prach_pdu->num_cs; prach_fmt_id = prach_pdu->prach_format; preamble_index = prach_resources->ra_PreambleIndex; - fd_occasion = 0; - prach_len = 0; - dftlen = 0; - first_nonzero_root_idx = 0; kbar = 1; K = 24; k = 12*n_ra_prb - 6*fp->N_RB_UL; @@ -217,10 +217,11 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, uint8_t slot){ k += kbar; k *= 2; - LOG_I(PHY, "PRACH [UE %d] in slot %d, placing PRACH in position %d, msg1 frequency start %d, preamble_offset %d, first_nonzero_root_idx %d\n", Mod_id, + LOG_I(PHY, "PRACH [UE %d] in slot %d, placing PRACH in position %d, msg1 frequency start %d (k1 %d), preamble_offset %d, first_nonzero_root_idx %d\n", Mod_id, slot, k, n_ra_prb, + nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].k1, preamble_offset, first_nonzero_root_idx); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c index 76fc5f298f..6cf782d9d8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c @@ -31,13 +31,15 @@ #include "PHY/defs_nr_UE.h" #include "PHY/phy_extern_nr_ue.h" #include "nr_transport_proto_ue.h" +#include "executables/softmodem-common.h" void nr_get_carrier_frequencies(NR_DL_FRAME_PARMS *fp, uint64_t *dl_carrier, uint64_t *ul_carrier){ - if (downlink_frequency[0][0]) - *dl_carrier = downlink_frequency[0][0]; - else + if (get_softmodem_params()->phy_test==1 || get_softmodem_params()->do_ra==1 || !downlink_frequency[0][0]) { *dl_carrier = fp->dl_CarrierFreq; + } else { + *dl_carrier = downlink_frequency[0][0]; + } if (uplink_frequency_offset[0][0]) *ul_carrier = *dl_carrier + uplink_frequency_offset[0][0]; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index 1a39def26b..8f3d1987bc 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -264,21 +264,24 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, LOG_D(PHY,"ulsch coding nb_rb %d, Nl = %d\n", nb_rb, harq_process->pusch_pdu.nrOfLayers); LOG_D(PHY,"ulsch coding A %d G %d mod_order %d\n", A,G, mod_order); + LOG_D(PHY,"harq_pid %d harq_process->ndi %d, pusch_data.new_data_indicator %d\n", + harq_pid,harq_process->ndi,harq_process->pusch_pdu.pusch_data.new_data_indicator); - if (harq_process->ndi != harq_process->pusch_pdu.pusch_data.new_data_indicator) { // this is a new packet + if (harq_process->first_tx == 1 || + harq_process->ndi != harq_process->pusch_pdu.pusch_data.new_data_indicator) { // this is a new packet #ifdef DEBUG_ULSCH_CODING printf("encoding thinks this is a new packet \n"); #endif - + harq_process->first_tx = 0; ///////////////////////// a---->| add CRC |---->b ///////////////////////// /////////// - /* + /* int i; printf("ulsch (tx): \n"); for (i=0;i<(A>>3);i++) - printf("%02x.",a[i]); + printf("%02x.",harq_process->a[i]); printf("\n"); - */ + */ if (A > 3824) { // Add 24-bit crc (polynomial A) to payload @@ -412,6 +415,7 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, /////////// /////////////////////////////////////////////////////////////////////////////// + LOG_D(PHY,"setting ndi to %d from pusch_data\n", harq_process->pusch_pdu.pusch_data.new_data_indicator); harq_process->ndi = harq_process->pusch_pdu.pusch_data.new_data_indicator; } F = harq_process->F; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 60b87fae08..fae6b5b2e7 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -146,6 +146,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, get_num_re_dmrs(pusch_pdu, &nb_dmrs_re_per_rb, &number_dmrs_symbols); + LOG_D(PHY,"ulsch %x : start_rb %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", + rnti,start_rb,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); + // TbD num_of_mod_symbols is set but never used N_RE_prime = NR_NB_SC_PER_RB*number_of_symbols - nb_dmrs_re_per_rb*number_dmrs_symbols - N_PRB_oh; harq_process_ul_ue->num_of_mod_symbols = N_RE_prime*nb_rb*num_of_codewords; @@ -361,6 +364,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part // Perform this on gold sequence, not required when SC FDMA operation is done, + LOG_D(PHY,"DMRS in symbol %d\n",l); nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated } else { diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c index 3560b23550..c7bf459f6f 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c @@ -59,10 +59,11 @@ void nr_generate_pucch0(PHY_VARS_NR_UE *ue, int16_t amp, int nr_slot_tx, uint8_t m0, - uint8_t mcs, + uint8_t mcs, uint8_t nrofSymbols, uint8_t startingSymbolIndex, - uint16_t startingPRB) { + uint16_t startingPRB, + uint16_t secondHopPRB) { #ifdef DEBUG_NR_PUCCH_TX printf("\t [nr_generate_pucch0] start function at slot(nr_slot_tx)=%d\n",nr_slot_tx); #endif @@ -94,38 +95,35 @@ void nr_generate_pucch0(PHY_VARS_NR_UE *ue, * x(l*12+n) = r_u_v_alpha_delta(n) */ // the value of u,v (delta always 0 for PUCCH) has to be calculated according to TS 38.211 Subclause 6.3.2.2.1 - uint8_t u=0,v=0;//,delta=0; - // if frequency hopping is disabled by the higher-layer parameter PUCCH-frequency-hopping - // n_hop = 0 - // if frequency hopping is enabled by the higher-layer parameter PUCCH-frequency-hopping - // n_hop = 0 for first hop - // n_hop = 1 for second hop - uint8_t n_hop = 0; - //uint8_t PUCCH_Frequency_Hopping; // from higher layers FIXME!! + uint8_t u[2]={0,0},v[2]={0,0}; + #ifdef DEBUG_NR_PUCCH_TX printf("\t [nr_generate_pucch0] sequence generation: variable initialization for test\n"); #endif // x_n contains the sequence r_u_v_alpha_delta(n) - int16_t x_n_re[24],x_n_im[24]; + int16_t x_n_re[2][24],x_n_im[2][24]; // we proceed to calculate alpha according to TS 38.211 Subclause 6.3.2.2.2 + int prb_offset[2]={startingPRB,startingPRB}; + nr_group_sequence_hopping(pucch_GroupHopping,hoppingId,0,nr_slot_tx,&u[0],&v[0]); // calculating u and v value + if (startingPRB!=secondHopPRB) { + nr_group_sequence_hopping(pucch_GroupHopping,hoppingId,1,nr_slot_tx,&u[1],&v[1]); // calculating u and v value + prb_offset[1] = secondHopPRB; + } for (int l=0; l>15) - - (((int32_t)(round(32767*sin(alpha*n))) * table_5_2_2_2_2_Im[u][n])>>15))); // Re part of base sequence shifted by alpha - x_n_im[(12*l)+n] = (int16_t)(((((int32_t)(round(32767*cos(alpha*n))) * table_5_2_2_2_2_Im[u][n])>>15) - + (((int32_t)(round(32767*sin(alpha*n))) * table_5_2_2_2_2_Re[u][n])>>15))); // Im part of base sequence shifted by alpha + x_n_re[l][n] = (int16_t)(((((int32_t)(round(32767*cos(alpha*n))) * table_5_2_2_2_2_Re[u[l]][n])>>15) + - (((int32_t)(round(32767*sin(alpha*n))) * table_5_2_2_2_2_Im[u[l]][n])>>15))); // Re part of base sequence shifted by alpha + x_n_im[l][n] = (int16_t)(((((int32_t)(round(32767*cos(alpha*n))) * table_5_2_2_2_2_Im[u[l]][n])>>15) + + (((int32_t)(round(32767*sin(alpha*n))) * table_5_2_2_2_2_Re[u[l]][n])>>15))); // Im part of base sequence shifted by alpha #ifdef DEBUG_NR_PUCCH_TX printf("\t [nr_generate_pucch0] sequence generation \tu=%d \tv=%d \talpha=%lf \tx_n(l=%d,n=%d)=(%d,%d)\n", - u,v,alpha,l,n,x_n_re[(12*l)+n],x_n_im[(12*l)+n]); + u[l],v[l],alpha,l,n,x_n_re[l][n],x_n_im[l][n]); #endif } } @@ -139,15 +137,18 @@ void nr_generate_pucch0(PHY_VARS_NR_UE *ue, for (int l=0; lfirst_carrier_offset; + re_offset = (12*prb_offset[l]) + frame_parms->first_carrier_offset; if (re_offset>= frame_parms->ofdm_symbol_size) re_offset-=frame_parms->ofdm_symbol_size; //txptr = &txdataF[0][re_offset]; +#ifdef DEBUG_NR_PUCCH_TX + printf("\t [nr_generate_pucch0] symbol %d PRB %d (%d)\n",l,prb_offset[l],re_offset); +#endif for (int n=0; n<12; n++) { - ((int16_t *)&txdataF[0][(l2*frame_parms->ofdm_symbol_size) + re_offset])[0] = (int16_t)(((int32_t)(amp) * x_n_re[(12*l)+n])>>15); - ((int16_t *)&txdataF[0][(l2*frame_parms->ofdm_symbol_size) + re_offset])[1] = (int16_t)(((int32_t)(amp) * x_n_im[(12*l)+n])>>15); + ((int16_t *)&txdataF[0][(l2*frame_parms->ofdm_symbol_size) + re_offset])[0] = (int16_t)(((int32_t)(amp) * x_n_re[l][n])>>15); + ((int16_t *)&txdataF[0][(l2*frame_parms->ofdm_symbol_size) + re_offset])[1] = (int16_t)(((int32_t)(amp) * x_n_im[l][n])>>15); //((int16_t *)txptr[0][re_offset])[0] = (int16_t)((int32_t)amp * x_n_re[(12*l)+n])>>15; //((int16_t *)txptr[0][re_offset])[1] = (int16_t)((int32_t)amp * x_n_im[(12*l)+n])>>15; //txptr[re_offset] = (x_n_re[(12*l)+n]<<16) + x_n_im[(12*l)+n]; diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h index c7f318f588..665dec2443 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h +++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h @@ -54,10 +54,12 @@ void nr_generate_pucch0(PHY_VARS_NR_UE *ue, int16_t amp, int nr_slot_tx, uint8_t m0, - uint8_t mcs, + uint8_t mcs, uint8_t nrofSymbols, uint8_t startingSymbolIndex, - uint16_t startingPRB); + uint16_t startingPRB, + uint16_t secondHopPRB); + void nr_generate_pucch1(PHY_VARS_NR_UE *ue, int32_t **txdataF, NR_DL_FRAME_PARMS *frame_parms, @@ -72,10 +74,11 @@ void nr_generate_pucch1(PHY_VARS_NR_UE *ue, uint16_t startingPRB_intraSlotHopping, uint8_t timeDomainOCC, uint8_t nr_bit); + void nr_generate_pucch2(PHY_VARS_NR_UE *ue, uint16_t crnti, - uint32_t dmrs_scrambling_id, - uint32_t data_scrambling_id, + uint32_t dmrs_scrambling_id, + uint32_t data_scrambling_id, int32_t **txdataF, NR_DL_FRAME_PARMS *frame_parms, PUCCH_CONFIG_DEDICATED *pucch_config_dedicated, @@ -87,6 +90,7 @@ void nr_generate_pucch2(PHY_VARS_NR_UE *ue, uint8_t nrofPRB, uint16_t startingPRB, uint8_t nr_bit); + void nr_generate_pucch3_4(PHY_VARS_NR_UE *ue, uint16_t crnti, int32_t **txdataF, diff --git a/openair1/PHY/defs_common.h b/openair1/PHY/defs_common.h index b9c776f1c8..a371b2334b 100644 --- a/openair1/PHY/defs_common.h +++ b/openair1/PHY/defs_common.h @@ -869,7 +869,8 @@ typedef enum { RA_RESPONSE=2, RA_WAIT_CR=3, PUSCH=4, - RESYNCH=5 + RESYNCH=5, + NUM_UE_MODE=6 } UE_MODE_t; #define FOREACH_PARALLEL(GEN) \ diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index d815c75567..364e3a118a 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -133,8 +133,32 @@ typedef struct { int total_bytes_rx; int current_Qm; int current_RI; + int power[NB_ANTENNAS_RX]; + int noise_power[NB_ANTENNAS_RX]; + int DTX; } NR_gNB_SCH_STATS_t; +typedef struct { + int frame; + uint16_t rnti; + int pucch0_sr_trials; + int pucch0_sr_thres; + int current_pucch0_sr_stat0; + int current_pucch0_sr_stat1; + int pucch0_positive_SR; + int pucch01_trials; + int pucch0_n00; + int pucch0_n01; + int pucch0_thres; + int current_pucch0_stat0; + int current_pucch0_stat1; + int pucch01_DTX; + int pucch02_trials; + int pucch02_DTX; + int pucch2_trials; + int pucch2_DTX; +} NR_gNB_UCI_STATS_t; + typedef struct { /// Pointers to variables related to DLSCH harq process NR_DL_gNB_HARQ_t harq_process; @@ -465,7 +489,13 @@ typedef struct { /// - second index: ? [0..168*N_RB_UL[ int32_t **ul_ch_magb1[8][8]; /// measured RX power based on DRS - int ulsch_power[2]; + int ulsch_power[8]; + /// total signal over antennas + int ulsch_power_tot; + /// measured RX noise power + int ulsch_noise_power[8]; + /// total noise over antennas + int ulsch_noise_power_tot; /// \brief llr values. /// - first index: ? [0..1179743] (hard coded) int16_t *llr; @@ -486,6 +516,8 @@ typedef struct { int16_t *ul_valid_re_per_slot; /// flag to verify if channel level computation is done uint8_t cl_done; + /// flag to indicate DTX on reception + int DTX; } NR_gNB_PUSCH; /// Context data structure for RX/TX portion of slot processing @@ -562,8 +594,10 @@ typedef struct gNB_L1_proc_t_s { pthread_t pthread_single; /// pthread structure for asychronous RX/TX processing thread pthread_t pthread_asynch_rxtx; - /// pthread structure for printing time meas + /// pthread structure for dumping L1 stats pthread_t L1_stats_thread; + /// pthread structure for printing time meas + pthread_t process_stats_thread; /// flag to indicate first RX acquisition int first_rx; /// flag to indicate first TX transmission @@ -629,6 +663,8 @@ typedef struct { unsigned short n0_subband_power[MAX_NUM_RU_PER_gNB][275]; //! estimated avg noise power per RB per RX ant (dB) unsigned short n0_subband_power_dB[MAX_NUM_RU_PER_gNB][275]; + //! estimated avg subband noise power (dB) + unsigned short n0_subband_power_avg_dB; //! estimated avg noise power per RB (dB) short n0_subband_power_tot_dB[275]; //! estimated avg noise power per RB (dBm) @@ -733,7 +769,7 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_SCH_STATS_t dlsch_stats[NUMBER_OF_NR_SCH_STATS_MAX]; /// statistics for ULSCH measurement collection NR_gNB_SCH_STATS_t ulsch_stats[NUMBER_OF_NR_SCH_STATS_MAX]; - + NR_gNB_UCI_STATS_t uci_stats[NUMBER_OF_NR_UCI_STATS_MAX]; t_nrPolar_params *uci_polarParams; uint8_t pbch_configured; @@ -800,6 +836,8 @@ typedef struct PHY_VARS_gNB_s { int prach_energy_counter; int pucch0_thres; + int pusch_thres; + int prach_thres; uint64_t bad_pucch; /* time_stats_t phy_proc; diff --git a/openair1/SCHED/phy_procedures_lte_eNb.c b/openair1/SCHED/phy_procedures_lte_eNb.c index f40f760f45..973c921d23 100644 --- a/openair1/SCHED/phy_procedures_lte_eNb.c +++ b/openair1/SCHED/phy_procedures_lte_eNb.c @@ -55,7 +55,6 @@ #include "common/ran_context.h" extern RAN_CONTEXT_t RC; -nfapi_ue_release_request_body_t release_rntis; int16_t get_hundred_times_delta_IF_eNB(PHY_VARS_eNB *eNB,uint16_t ULSCH_id,uint8_t harq_pid, uint8_t bw_factor) { uint32_t Nre,sumKr,MPR_x100,Kr,r; diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index 5a98b17cea..c95d3dc937 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -121,8 +121,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB,int frame,int slot) { max_preamble_delay[0], gNB->prach_energy_counter); - if ((gNB->prach_energy_counter == 100) && - (max_preamble_energy[0] > gNB->measurements.prach_I0+100)) { + if ((gNB->prach_energy_counter == 100) && (max_preamble_energy[0] > gNB->measurements.prach_I0+gNB->prach_thres)) { LOG_I(PHY,"[gNB %d][RAPROC] Frame %d, slot %d Initiating RA procedure with preamble %d, energy %d.%d dB, delay %d start symbol %u freq index %u\n", gNB->Mod_id, diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 83483f3a7f..717487c101 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -355,7 +355,7 @@ void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH } -void nr_fill_indication(PHY_VARS_gNB *gNB, int frame, int slot_rx, int ULSCH_id, uint8_t harq_pid, uint8_t crc_flag) { + void nr_fill_indication(PHY_VARS_gNB *gNB, int frame, int slot_rx, int ULSCH_id, uint8_t harq_pid, uint8_t crc_flag) { pthread_mutex_lock(&gNB->UL_INFO_mutex); @@ -381,10 +381,12 @@ void nr_fill_indication(PHY_VARS_gNB *gNB, int frame, int slot_rx, int ULSCH_id, LOG_D(PHY, "Estimated timing advance PUSCH is = %d, timing_advance_update is %d \n", sync_pos,timing_advance_update); - // estimate UL_CQI for MAC (from antenna port 0 only) - int SNRtimes10 = dB_fixed_times10(gNB->pusch_vars[ULSCH_id]->ulsch_power[0]) - (10*gNB->measurements.n0_power_dB[0]); + // estimate UL_CQI for MAC - LOG_D(PHY, "Estimated SNR for PUSCH is = %d dB\n", SNRtimes10/10); + int SNRtimes10 = dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot) - + dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot); + + LOG_D(PHY, "Estimated SNR for PUSCH is = %f dB (ulsch_power %f, noise %f)\n", SNRtimes10/10.0,dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot)/10.0,dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot)/10.0); if (SNRtimes10 < -640) cqi=0; else if (SNRtimes10 > 635) cqi=255; @@ -445,14 +447,14 @@ void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { NR_gNB_PUCCH_t *pucch = gNB->pucch[i]; if (pucch) { if ((pucch->active == 1) && - (pucch->frame == frame_rx) && - (pucch->slot == slot_rx) ) { + (pucch->frame == frame_rx) && + (pucch->slot == slot_rx) ) { gNB->ulmask_symb = symbol; nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; if ((symbol>=pucch_pdu->start_symbol_index) && (symbol<(pucch_pdu->start_symbol_index + pucch_pdu->nr_of_symbols))){ for (rb=0; rbprb_size; rb++) { - rb2 = rb+pucch_pdu->prb_start; + rb2 = rb+pucch_pdu->prb_start+pucch_pdu->bwp_start; gNB->rb_mask_ul[rb2>>5] |= (1<<(rb2&31)); } nb_rb+=pucch_pdu->prb_size; @@ -480,7 +482,7 @@ void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { if ((symbol>=symbol_start) && (symbolulsch_pdu.rb_size; rb++) { - rb2 = rb+ulsch_harq->ulsch_pdu.rb_start; + rb2 = rb+ulsch_harq->ulsch_pdu.rb_start+ulsch_harq->ulsch_pdu.bwp_start; gNB->rb_mask_ul[rb2>>5] |= (1<<(rb2&31)); } nb_rb+=ulsch_harq->ulsch_pdu.rb_size; @@ -534,11 +536,19 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { if (gNB->frame_parms.frame_type == TDD) fill_ul_rb_mask(gNB, frame_rx, slot_rx); - gNB_I0_measurements(gNB); + int first_symb=0,num_symb=0; + if (gNB->frame_parms.frame_type == TDD) + for(int symbol_count=0; symbol_countgNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list[symbol_count].slot_config.value==1) { + if (num_symb==0) first_symb=symbol_count; + num_symb++; + } + } + else num_symb=NR_NUMBER_OF_SYMBOLS_PER_SLOT; + gNB_I0_measurements(gNB,first_symb,num_symb); - // measure enegry in SS=10 L=4, nb_rb = 18, first_rb = 0 (corresponds to msg3) int offset = 10*gNB->frame_parms.ofdm_symbol_size + gNB->frame_parms.first_carrier_offset; - int power_rxF = signal_energy_nodc(&gNB->common_vars.rxdataF[0][offset],12*18); + int power_rxF = signal_energy_nodc(&gNB->common_vars.rxdataF[0][offset+(47*12)],12*18); LOG_D(PHY,"frame %d, slot %d: UL signal energy %d\n",frame_rx,slot_rx,power_rxF); start_meas(&gNB->phy_proc_rx); @@ -547,8 +557,8 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { NR_gNB_PUCCH_t *pucch = gNB->pucch[i]; if (pucch) { if ((pucch->active == 1) && - (pucch->frame == frame_rx) && - (pucch->slot == slot_rx) ) { + (pucch->frame == frame_rx) && + (pucch->slot == slot_rx) ) { pucch_decode_done = 1; @@ -569,13 +579,14 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { LOG_D(PHY,"frame %d, slot %d: PUCCH signal energy %d\n",frame_rx,slot_rx,power_rxF); nr_decode_pucch0(gNB, - slot_rx, + frame_rx, + slot_rx, uci_pdu_format0, pucch_pdu); gNB->UL_INFO.uci_ind.num_ucis += 1; pucch->active = 0; - break; + break; case 2: num_ucis = gNB->UL_INFO.uci_ind.num_ucis; gNB->UL_INFO.uci_ind.uci_list = &gNB->uci_pdu_list[0]; @@ -594,7 +605,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { pucch->active = 0; break; default: - AssertFatal(1==0,"Only PUCCH formats 0 and 2 are currently supported\n"); + AssertFatal(1==0,"Only PUCCH formats 0 and 2 are currently supported\n"); } } } @@ -610,15 +621,18 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { (ulsch->rnti > 0)) { // for for an active HARQ process for (harq_pid=0;harq_pidharq_processes[harq_pid]; - AssertFatal(ulsch_harq!=NULL,"harq_pid %d is not allocated\n",harq_pid); - if ((ulsch_harq->status == NR_ACTIVE) && - (ulsch_harq->frame == frame_rx) && - (ulsch_harq->slot == slot_rx) && - (ulsch_harq->handled == 0)){ + ulsch_harq = ulsch->harq_processes[harq_pid]; + AssertFatal(ulsch_harq!=NULL,"harq_pid %d is not allocated\n",harq_pid); + if ((ulsch_harq->status == NR_ACTIVE) && + (ulsch_harq->frame == frame_rx) && + (ulsch_harq->slot == slot_rx) && + (ulsch_harq->handled == 0)){ LOG_D(PHY, "PUSCH detection started in frame %d slot %d\n", frame_rx,slot_rx); + int num_dmrs=0; + for (int s=0;sulsch_pdu.ul_dmrs_symb_pos>>s)&1; #ifdef DEBUG_RXDATA NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; @@ -647,13 +661,35 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { pusch_decode_done = 1; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH,1); - start_meas(&gNB->rx_pusch_stats); + start_meas(&gNB->rx_pusch_stats); no_sig = nr_rx_pusch(gNB, ULSCH_id, frame_rx, slot_rx, harq_pid); if (no_sig) { - LOG_I(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); + LOG_D(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, harq_pid, 1); return 1; } + gNB->pusch_vars[ULSCH_id]->ulsch_power_tot=0; + gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot=0; + for (int aarx=0;aarxframe_parms.nb_antennas_rx;aarx++) { + gNB->pusch_vars[ULSCH_id]->ulsch_power[aarx]/=num_dmrs; + gNB->pusch_vars[ULSCH_id]->ulsch_power_tot += gNB->pusch_vars[ULSCH_id]->ulsch_power[aarx]; + gNB->pusch_vars[ULSCH_id]->ulsch_noise_power[aarx]/=num_dmrs; + gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot += gNB->pusch_vars[ULSCH_id]->ulsch_noise_power[aarx]; + } + if (dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot) < + dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot) + gNB->pusch_thres) { + NR_gNB_SCH_STATS_t *stats=get_ulsch_stats(gNB,ulsch); + + LOG_D(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, + dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot), + dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot),gNB->pusch_thres); + gNB->pusch_vars[ULSCH_id]->ulsch_power_tot = gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot; + nr_fill_indication(gNB,frame_rx, slot_rx, ULSCH_id, harq_pid, 1); + gNB->pusch_vars[ULSCH_id]->DTX=1; + stats->DTX++; + return 1; + } else gNB->pusch_vars[ULSCH_id]->DTX=0; + stop_meas(&gNB->rx_pusch_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH,0); //LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1); @@ -669,7 +705,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { stop_meas(&gNB->phy_proc_rx); // figure out a better way to choose slot_rx, 19 is ok for a particular TDD configuration with 30kHz SCS if ((frame_rx&127) == 0 && slot_rx==19) { - dump_pusch_stats(gNB); LOG_I(PHY, "Number of bad PUCCH received: %lu\n", gNB->bad_pucch); } diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index c8ac871bbf..c276de847d 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -42,7 +42,7 @@ extern PHY_VARS_NR_UE ***PHY_vars_UE_g; -const char *dl_pdu_type[]={"DCI", "DLSCH", "RA_DLSCH"}; +const char *dl_pdu_type[]={"DCI", "DLSCH", "RA_DLSCH", "SI_DLSCH", "P_DLSCH"}; const char *ul_pdu_type[]={"PRACH", "PUCCH", "PUSCH", "SRS"}; int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ @@ -67,7 +67,8 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ pdcch_vars->nb_search_space = 0; for (i = 0; i < dl_config->number_pdus; ++i){ - + AssertFatal(dl_config->number_pdus < FAPI_NR_DL_CONFIG_LIST_NUM,"dl_config->number_pdus %d out of bounds\n",dl_config->number_pdus); + AssertFatal(dl_config->dl_config_list[i].pdu_type<=FAPI_NR_DL_CONFIG_TYPES,"pdu_type %d > 2\n",dl_config->dl_config_list[i].pdu_type); LOG_D(PHY, "In %s: received 1 DL %s PDU of %d total DL PDUs:\n", __FUNCTION__, dl_pdu_type[dl_config->dl_config_list[i].pdu_type - 1], dl_config->number_pdus); if (dl_config->dl_config_list[i].pdu_type == FAPI_NR_DL_CONFIG_TYPE_DCI) { @@ -151,6 +152,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ for (i = 0; i < ul_config->number_pdus; ++i){ + AssertFatal(ul_config->ul_config_list[i].pdu_type <= FAPI_NR_UL_CONFIG_TYPES,"pdu_type %d out of bounds\n",ul_config->ul_config_list[i].pdu_type); LOG_D(PHY, "In %s: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); uint8_t pdu_type = ul_config->ul_config_list[i].pdu_type, pucch_resource_id, current_harq_pid, format, gNB_id = 0; @@ -183,7 +185,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ if (scheduled_response->tx_request){ fapi_nr_tx_request_body_t *tx_req_body = &scheduled_response->tx_request->tx_request_body[i]; - + LOG_D(PHY,"%d.%d Copying %d bytes to harq_process_ul_ue->a (harq_pid %d)\n",scheduled_response->frame,slot,tx_req_body->pdu_length,current_harq_pid); memcpy(harq_process_ul_ue->a, tx_req_body->pdu, tx_req_body->pdu_length); harq_process_ul_ue->status = ACTIVE; @@ -269,9 +271,11 @@ int8_t nr_ue_phy_config_request(nr_phy_config_t *phy_config){ fapi_nr_config_request_t *nrUE_config = &PHY_vars_UE_g[phy_config->Mod_id][phy_config->CC_id]->nrUE_config; - if(phy_config != NULL) + if(phy_config != NULL) { memcpy(nrUE_config,&phy_config->config_req,sizeof(fapi_nr_config_request_t)); - + if (PHY_vars_UE_g[phy_config->Mod_id][phy_config->CC_id]->UE_mode[0] == NOT_SYNCHED) + PHY_vars_UE_g[phy_config->Mod_id][phy_config->CC_id]->UE_mode[0] = PRACH; + } return 0; } diff --git a/openair1/SCHED_NR_UE/phy_frame_config_nr_ue.c b/openair1/SCHED_NR_UE/phy_frame_config_nr_ue.c index 6254f6a2d2..696828a970 100644 --- a/openair1/SCHED_NR_UE/phy_frame_config_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_frame_config_nr_ue.c @@ -46,6 +46,8 @@ int nr_ue_slot_select(fapi_nr_config_request_t *cfg, int nr_frame, int nr_slot) if (cfg->cell_config.frame_duplex_type == FDD) { return (NR_UPLINK_SLOT | NR_DOWNLINK_SLOT); } + if (cfg->tdd_table.max_tdd_periodicity_list == NULL) // this happens before receiving TDD configuration + return (NR_DOWNLINK_SLOT); if (nr_frame%2 == 0) { for(int symbol_count=0; symbol_countrx_indication_body[n_pdus - 1].pdsch_pdu.pdu = dlsch0->harq_processes[harq_pid]->b; rx_ind->rx_indication_body[n_pdus - 1].pdsch_pdu.pdu_length = dlsch0->harq_processes[harq_pid]->TBS / 8; break; - case FAPI_NR_RX_PDU_TYPE_MIB: - rx_ind->rx_indication_body[n_pdus - 1].mib_pdu.pdu = ue->pbch_vars[gNB_id]->decoded_output; - rx_ind->rx_indication_body[n_pdus - 1].mib_pdu.additional_bits = ue->pbch_vars[gNB_id]->xtra_byte; - rx_ind->rx_indication_body[n_pdus - 1].mib_pdu.ssb_index = (frame_parms->ssb_index)&0x7; - rx_ind->rx_indication_body[n_pdus - 1].mib_pdu.ssb_length = frame_parms->Lmax; - rx_ind->rx_indication_body[n_pdus - 1].mib_pdu.cell_id = frame_parms->Nid_cell; + case FAPI_NR_RX_PDU_TYPE_SSB: + rx_ind->rx_indication_body[n_pdus - 1].ssb_pdu.pdu = ue->pbch_vars[gNB_id]->decoded_output; + rx_ind->rx_indication_body[n_pdus - 1].ssb_pdu.additional_bits = ue->pbch_vars[gNB_id]->xtra_byte; + rx_ind->rx_indication_body[n_pdus - 1].ssb_pdu.ssb_index = (frame_parms->ssb_index)&0x7; + rx_ind->rx_indication_body[n_pdus - 1].ssb_pdu.ssb_length = frame_parms->Lmax; + rx_ind->rx_indication_body[n_pdus - 1].ssb_pdu.cell_id = frame_parms->Nid_cell; + rx_ind->rx_indication_body[n_pdus - 1].ssb_pdu.ssb_start_subcarrier = frame_parms->ssb_start_subcarrier; break; default: break; @@ -1760,7 +1761,7 @@ int phy_procedures_nrUE_RX(PHY_VARS_NR_UE *ue, } #endif //NR_PDCCH_SCHED - + // Start PUSCH processing here. It runs in parallel with PDSCH processing notifiedFIFO_elt_t *newElt = newNotifiedFIFO_elt(sizeof(nr_rxtx_thread_data_t), proc->nr_slot_tx,txFifo,processSlotTX); nr_rxtx_thread_data_t *curMsg=(nr_rxtx_thread_data_t *)NotifiedFifoData(newElt); @@ -1899,7 +1900,7 @@ int phy_procedures_nrUE_RX(PHY_VARS_NR_UE *ue, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDSCH_PROC, VCD_FUNCTION_OUT); } - + #if UE_TIMING_TRACE start_meas(&ue->generic_stat); #endif @@ -2061,9 +2062,8 @@ void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, uint8_t } else { - LOG_D(PHY, "In %s:[%d.%d] getting PRACH resources\n", __FUNCTION__, frame_tx, nr_slot_tx); nr_prach = nr_ue_get_rach(prach_resources, &ue->prach_vars[0]->prach_pdu, mod_id, ue->CC_id, frame_tx, gNB_id, nr_slot_tx); - + LOG_D(PHY, "In %s:[%d.%d] getting PRACH resources : %d\n", __FUNCTION__, frame_tx, nr_slot_tx,nr_prach); } if (nr_prach == GENERATE_PREAMBLE) { @@ -2073,7 +2073,7 @@ void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, uint8_t int16_t ra_preamble_rx_power = (int16_t)(prach_resources->ra_PREAMBLE_RECEIVED_TARGET_POWER - pathloss + 30); ue->tx_power_dBm[nr_slot_tx] = min(nr_get_Pcmax(mod_id), ra_preamble_rx_power); - LOG_I(PHY,"DEBUG [UE %d][RAPROC][%d.%d]: Generating PRACH Msg1 (preamble %d, PL %d dB, P0_PRACH %d, TARGET_RECEIVED_POWER %d dBm, RA-RNTI %x)\n", + LOG_D(PHY,"DEBUG [UE %d][RAPROC][%d.%d]: Generating PRACH Msg1 (preamble %d, PL %d dB, P0_PRACH %d, TARGET_RECEIVED_POWER %d dBm, RA-RNTI %x)\n", mod_id, frame_tx, nr_slot_tx, diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index 07c03f4238..9af50d28e0 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -41,6 +41,7 @@ #include "openair2/LAYER2/NR_MAC_UE/mac_proto.h" #include "openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h" #include +#include #ifndef NO_RAT_NR @@ -118,7 +119,7 @@ static const int sequence_cyclic_shift_1_harq_ack_bit_positive_sr[2] /* Sequence cyclic shift */ = { 3, 9 } ; -static float RSRP_meas_mapping_nr[98] +static float RSRP_meas_mapping_nr[98] = { -140, -139, @@ -433,7 +434,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ pucch_resource_indicator = ue->dlsch[proc->thread_id][gNB_id][0]->harq_processes[dl_harq_pid]->harq_ack.pucch_resource_indicator; } - LOG_D(PHY, "PUCCH: %d.%d dl_harq_pid = %d, pucch_resource_indicator = %d\n", frame_tx, nr_slot_tx, dl_harq_pid, pucch_resource_indicator); + LOG_D(PHY, "PUCCH: %d.%d bwp_id %ld dl_harq_pid = %d, pucch_resource_indicator = %d\n", frame_tx, nr_slot_tx, bwp_id,dl_harq_pid, pucch_resource_indicator); /* Part - I * Collect feedback that should be transmitted at this nr_slot_tx : @@ -468,22 +469,27 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ ri_status = ((ue->cqi_report_config[gNB_id].CQI_ReportPeriodic.ri_ConfigIndex>0) && (nr_is_ri_TXOp(ue,proc,gNB_id) == 1)); - - NR_CSI_MeasConfig_t *csi_MeasConfig = mac->scg->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; - - uint16_t report_slot_csi =csi_MeasConfig->csi_ReportConfigToAddModList->list.array[0]->reportConfigType.choice.periodic->reportSlotConfig.choice.slots320; - //if (mac->csirc->reportQuantity.choice.ssb_Index_RSRP){ - if (report_slot_csi == proc->nr_slot_tx) - csi_status = get_csi_nr(mac, ue, gNB_id, &csi_payload); - else - csi_status = 0; - //} + if (mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig&& + mac->cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup) { + NR_CSI_MeasConfig_t *csi_MeasConfig = mac->cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; - O_CSI = cqi_status + ri_status + csi_status; + uint16_t report_slot_csi =csi_MeasConfig->csi_ReportConfigToAddModList->list.array[0]->reportConfigType.choice.periodic->reportSlotConfig.choice.slots320; - /* Part - II */ - /* if payload is empty or only negative SR -> no pucch transmission */ + //if (mac->csirc->reportQuantity.choice.ssb_Index_RSRP){ + if (report_slot_csi == proc->nr_slot_tx) + csi_status = get_csi_nr(mac, ue, gNB_id, &csi_payload); + else + csi_status = 0; + //} + + O_CSI = cqi_status + ri_status + csi_status; + + /* Part - II */ + /* if payload is empty or only negative SR -> no pucch transmission */ if(O_ACK == 0) { N_UCI = O_SR + O_CSI; @@ -506,24 +512,25 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ csi_payload = 0; } - if (O_CSI == 0) { - /* only SR has to be send */ - /* in this case there is no DCI related to PUCCH parameters so pucch resource should be get from sr configuration */ - /* TS 38.213 9.2.4 UE procedure for reporting SR */ - pucch_resource_set = 0; /* force it to a valid value */ - if (ue->scheduling_request_config_nr[gNB_id].sr_ResourceConfig[ue->scheduling_request_config_nr[gNB_id].active_sr_id] != NULL) { - pucch_resource_id = ue->scheduling_request_config_nr[gNB_id].sr_ResourceConfig[ue->scheduling_request_config_nr[gNB_id].active_sr_id]->resource; - } - else { - LOG_E(PHY,"PUCCH No scheduling request configuration : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); - return(FALSE); - } + if (O_CSI == 0) { + /* only SR has to be send */ + /* in this case there is no DCI related to PUCCH parameters so pucch resource should be get from sr configuration */ + /* TS 38.213 9.2.4 UE procedure for reporting SR */ + pucch_resource_set = 0; /* force it to a valid value */ + if (ue->scheduling_request_config_nr[gNB_id].sr_ResourceConfig[ue->scheduling_request_config_nr[gNB_id].active_sr_id] != NULL) { + pucch_resource_id = ue->scheduling_request_config_nr[gNB_id].sr_ResourceConfig[ue->scheduling_request_config_nr[gNB_id].active_sr_id]->resource; + } + else { + LOG_E(PHY,"PUCCH No scheduling request configuration : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); + return(FALSE); + } + } } } } - else { - N_UCI = O_SR + O_ACK + O_CSI; - } + + N_UCI = O_SR + O_ACK + O_CSI; + if (N_UCI ==0) return(TRUE); /* Part - III */ /* Choice PUCCH format and its related parameters */ @@ -531,8 +538,8 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ uint8_t starting_symbol_index=0; uint8_t nb_symbols_total = 0; uint8_t nb_symbols = 0; - uint16_t starting_prb = 0;; /* it can be considered as first hop on case of pucch hopping */ - uint16_t second_hop = 0; /* second part for pucch for hopping */ + uint16_t startingPRB = 0;; /* it can be considered as first hop on case of pucch hopping */ + uint16_t secondHopPRB = 0; /* second part for pucch for hopping */ uint8_t nb_of_prbs = 0; int m_0 = 0; /* format 0 only */ int m_CS = 0; /* for all format except for format 0 */ @@ -541,6 +548,8 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ int time_domain_occ = 0; int occ_length = 0; int occ_Index = 0; + int BWPsize = 0; + int BWPstart = 0; NR_UE_HARQ_STATUS_t *harq_status = &ue->dlsch[proc->thread_id][gNB_id][0]->harq_processes[dl_harq_pid]->harq_ack; @@ -549,29 +558,32 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ /* use of initial pucch configuration provided by system information 1 */ /***********************************************************************/ if (initial_pucch_id != NB_INITIAL_PUCCH_RESOURCE) { + LOG_D(MAC,"Selecting INITIAL PUCCH Resource\n"); format = initial_pucch_resource[initial_pucch_id].format; starting_symbol_index = initial_pucch_resource[initial_pucch_id].startingSymbolIndex; nb_symbols_total = initial_pucch_resource[initial_pucch_id].nrofSymbols; int N_CS = initial_pucch_resource[initial_pucch_id].nb_CS_indexes; /* see TS 38213 Table 9.2.1-1: PUCCH resource sets before dedicated PUCCH resource configuration */ + BWPsize = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + BWPstart = NRRIV2PRBOFFSET(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); int RB_BWP_offset; if (initial_pucch_id == 15) { - RB_BWP_offset = ue->systemInformationBlockType1_nr.N_BWP_SIZE/4; + RB_BWP_offset =BWPsize/4; } else { RB_BWP_offset = initial_pucch_resource[initial_pucch_id].PRB_offset; } if (initial_pucch_id/8 == 0) { - starting_prb = RB_BWP_offset + (initial_pucch_id/N_CS); - second_hop = ue->systemInformationBlockType1_nr.N_BWP_SIZE - 1 - RB_BWP_offset - (initial_pucch_id/N_CS); + startingPRB = RB_BWP_offset + (initial_pucch_id/N_CS); + secondHopPRB = BWPsize - 1 - RB_BWP_offset - (initial_pucch_id/N_CS); m_0 = initial_pucch_resource[initial_pucch_id].initial_CS_indexes[initial_pucch_id%N_CS]; } else if (initial_pucch_id/8 == 1) { - starting_prb = RB_BWP_offset + (initial_pucch_id/N_CS); - second_hop = ue->systemInformationBlockType1_nr.N_BWP_SIZE - 1 - RB_BWP_offset - ((initial_pucch_id - 8)/N_CS); + startingPRB = RB_BWP_offset + (initial_pucch_id/N_CS); + secondHopPRB = BWPsize - 1 - RB_BWP_offset - ((initial_pucch_id - 8)/N_CS); m_0 = initial_pucch_resource[initial_pucch_id].initial_CS_indexes[(initial_pucch_id - 8)%N_CS]; } if ((ue->UE_mode[gNB_id] != PUSCH) && (O_ACK > 1)) { @@ -580,26 +592,55 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ LOG_W(PHY,"PUCCH ue is not expected to generate more than one HARQ-ACK at AbsSubframe %d.%d \n", frame_tx%1024, nr_slot_tx); } NR_TST_PHY_PRINTF("PUCCH common configuration with index %d \n", initial_pucch_id); + startingPRB += BWPstart; + secondHopPRB += BWPstart; } /* use dedicated pucch resource configuration */ /**********************************************/ else if ((pucch_resource_set != MAX_NB_OF_PUCCH_RESOURCE_SETS) && (pucch_resource_id != MAX_NB_OF_PUCCH_RESOURCES)) { /* check that current configuration is supported */ - if ((mac->scg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH != NULL) - || (mac->scg->physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook != 1)) { + if (mac->cg && + mac->cg->physicalCellGroupConfig && + (mac->cg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH != NULL || mac->cg->physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook != 1)) { LOG_E(PHY,"PUCCH Unsupported cell group configuration : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); return(FALSE); } - else if (mac->scg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { + else if (mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup && + mac->cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { LOG_E(PHY,"PUCCH Unsupported code block group for serving cell config : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); return(FALSE); } - pucch_resource = select_resource_by_id(pucch_resource_id, mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup); + NR_PUCCH_Config_t *pucch_Config; + if (bwp_id>0 && + mac->ULbwp[bwp_id-1] && + mac->ULbwp[bwp_id-1]->bwp_Dedicated && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup) + pucch_Config = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup; + else if (bwp_id==0 && + mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup) { + pucch_Config = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup; + BWPsize = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + BWPstart = NRRIV2PRBOFFSET(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + } + + else AssertFatal(1==0,"no pucch_Config\n"); + pucch_resource = select_resource_by_id(pucch_resource_id, pucch_Config); format = pucch_resource->format.present; nb_symbols_total = get_nb_symbols_pucch(pucch_resource, format); starting_symbol_index = get_starting_symb_idx(pucch_resource, format); - starting_prb = pucch_resource->startingPRB; - second_hop = starting_prb; + startingPRB = BWPstart + pucch_resource->startingPRB; + secondHopPRB = pucch_resource->intraSlotFrequencyHopping ? (BWPstart+*pucch_resource->secondHopPRB) : startingPRB; if (format==pucch_format1_nr) time_domain_occ = pucch_resource->format.choice.format1->timeDomainOCC; if (format==pucch_format4_nr) { @@ -787,8 +828,8 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ NR_TST_PHY_PRINTF("PUCCH ( format : %d ) ( modulation : %s ) ( nb prb : %d ) ( nb symbols total: %d ) ( nb symbols : %d ) ( max code rate*100 : %d ) ( starting_symbol_index : %d ) \n", format, (Q_m == BITS_PER_SYMBOL_QPSK ? " QPSK " : " BPSK "), nb_of_prbs, nb_symbols_total, nb_symbols, max_code_rate, starting_symbol_index); - NR_TST_PHY_PRINTF("PUCCH ( starting_prb : %d ) ( second_hop : %d ) ( m_0 : %d ) ( m_CS : %d ) ( time_domain_occ %d ) (occ_length : %d ) ( occ_Index : %d ) \n", - starting_prb, second_hop, m_0, m_CS, time_domain_occ, occ_length, occ_Index); + NR_TST_PHY_PRINTF("PUCCH ( startingPRB : %d ) ( secondHopPRB : %d ) ( m_0 : %d ) ( m_CS : %d ) ( time_domain_occ %d ) (occ_length : %d ) ( occ_Index : %d ) \n", + startingPRB (absolute), secondHopPRB (absolute), m_0, m_CS, time_domain_occ, occ_length, occ_Index); /* Part - IV */ /* Generate PUCCH signal according to its format and parameters */ @@ -817,17 +858,27 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ switch(format) { case pucch_format0_nr: { + int pucch_GroupHopping = mac->ULbwp[bwp_id-1] ? + mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping: + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->pucch_GroupHopping; + int hoppingId = mac->ULbwp[bwp_id-1] ? + mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId[0]: + (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->hoppingId? + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->hoppingId[0]: + mac->physCellId); nr_generate_pucch0(ue,ue->common_vars.txdataF, &ue->frame_parms, - mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping, - mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId[0], + pucch_GroupHopping, + hoppingId, tx_amp, nr_slot_tx, (uint8_t)m_0, (uint8_t)m_CS, nb_symbols_total, starting_symbol_index, - starting_prb); + startingPRB, + secondHopPRB + ); break; } case pucch_format1_nr: @@ -841,8 +892,8 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ (uint8_t)m_0, nb_symbols_total, starting_symbol_index, - starting_prb, - second_hop, + startingPRB, + secondHopPRB, (uint8_t)time_domain_occ, (uint8_t)N_UCI); break; @@ -862,7 +913,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ nb_symbols_total, starting_symbol_index, nb_of_prbs, - starting_prb, + startingPRB, (uint8_t)N_UCI); break; } @@ -881,8 +932,8 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ nb_symbols_total, starting_symbol_index, nb_of_prbs, - starting_prb, - second_hop, + startingPRB, + secondHopPRB, (uint8_t)N_UCI, (uint8_t)occ_length, (uint8_t)occ_Index); @@ -940,7 +991,11 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; if (mac->DLbwp[dl_bwp_id-1] == NULL) return 0; - if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { two_transport_blocks = TRUE; number_of_code_word = 2; } @@ -1017,7 +1072,9 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t U_DAI_c = number_harq_feedback/number_of_code_word; N_m_c_rx = number_harq_feedback; int N_SPS_c = 0; /* FFS TODO_NR multicells and SPS are not supported at the moment */ - if (mac->scg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH == NULL) { + if (mac->cg != NULL && + mac->cg->physicalCellGroupConfig != NULL && + mac->cg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH != NULL) { int N_TB_max_DL = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; *n_HARQ_ACK = (((V_DAI_m_DL - U_DAI_c)%4) * N_TB_max_DL) + N_m_c_rx + N_SPS_c; NR_TST_PHY_PRINTF("PUCCH power n(%d) = ( V(%d) - U(%d) )mod4 * N_TB(%d) + N(%d) \n", *n_HARQ_ACK, V_DAI_m_DL, U_DAI_c, N_TB_max_DL, N_m_c_rx); @@ -1130,12 +1187,30 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 //*resource_set_id = MAX_NB_OF_PUCCH_RESOURCE_SETS; //*resource_id = MAX_NB_OF_PUCCH_RESOURCES; - if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) { + if ((bwp_id ==0 && + mac->cg == NULL) || + (bwp_id == 0 && + mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) || + (mac->ULbwp[bwp_id-1] && + mac->ULbwp[bwp_id-1]->bwp_Dedicated && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) + ){ /* No resource set has been already configured so pucch_configCommon from Sib1 should be used in this case */ if (ue->UE_mode[gNB_id] != PUSCH) { - *initial_pucch_id = mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_ResourceCommon[0]; + *initial_pucch_id = *mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->pucch_ResourceCommon; if (*initial_pucch_id >= NB_INITIAL_PUCCH_RESOURCE) { LOG_E(PHY,"PUCCH Invalid initial resource index : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); *initial_pucch_id = NB_INITIAL_PUCCH_RESOURCE; @@ -1152,8 +1227,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 int n_CCE_0 = harq_status->n_CCE; int N_CCE_0 = harq_status->N_CCE; if (N_CCE_0 == 0) { - LOG_E(PHY,"PUCCH No compatible pucch format found : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); - return (FALSE); + AssertFatal(1==0,"PUCCH No compatible pucch format found : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); } int r_PUCCH = ((2 * n_CCE_0)/N_CCE_0) + (2 * delta_PRI); *initial_pucch_id = r_PUCCH; @@ -1196,18 +1270,29 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 if (pucch_resource_indicator < MAX_PUCCH_RESOURCE_INDICATOR) { // Verify that the value of pucch_resource_indicator is valid - if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.count <= pucch_resource_indicator) + struct NR_PUCCH_Config__resourceSetToAddModList *resourceSetToAddModList = NULL; + struct NR_PUCCH_Config__resourceToAddModList *resourceToAddModList = NULL; + if (bwp_id > 0 && mac->ULbwp[bwp_id-1]) { + AssertFatal(mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList!=NULL,"mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList is null\n"); + resourceSetToAddModList = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList; + resourceToAddModList = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList; + } else if (bwp_id == 0 && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList!=NULL) { + resourceSetToAddModList = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList; + resourceToAddModList = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceToAddModList; + } + if (resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.count <= pucch_resource_indicator) { LOG_E(PHY, "Value of pucch_resource_indicator is out of bounds! Possibly due to a false DCI. \n"); return (FALSE); } /* check if resource indexing by pucch_resource_indicator of this set is compatible */ - if ((ready_pucch_resource_id == TRUE) || (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.array[pucch_resource_indicator][0] != MAX_NB_OF_PUCCH_RESOURCES)) { + if ((ready_pucch_resource_id == TRUE) || (resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.array[pucch_resource_indicator][0] != MAX_NB_OF_PUCCH_RESOURCES)) { + if (ready_pucch_resource_id == TRUE) { current_resource_id = *resource_id; } else { - int R_PUCCH = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.count; + int R_PUCCH = resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.count; /* is it the first resource and its size exceeds 8 */ if ((pucch_resource_set_id == 0) && (R_PUCCH > MAX_NB_OF_PUCCH_RESOURCES_PER_SET_NOT_0)) { @@ -1229,17 +1314,14 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 current_resource_id = r_PUCCH; } else { - if (pucch_resource_set_id !=0 ) - current_resource_id = 3; //TBC mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.array[pucch_resource_indicator][0]; - else - current_resource_id = 1; + current_resource_id = resourceSetToAddModList->list.array[pucch_resource_set_id]->resourceList.list.array[pucch_resource_indicator][0]; } } - uint8_t pucch_resource_count = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.count; + uint8_t pucch_resource_count = resourceToAddModList->list.count; for (uint8_t i=0; iULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.array[i]->pucch_ResourceId == current_resource_id) - pucch_resource = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList->list.array[i]; + if (resourceToAddModList->list.array[i]->pucch_ResourceId == current_resource_id) + pucch_resource = resourceToAddModList->list.array[i]; } if (pucch_resource != NULL) { format_pucch = pucch_resource->format.present; @@ -1306,10 +1388,24 @@ int find_pucch_resource_set(NR_UE_MAC_INST_t *mac, uint8_t gNB_id, int uci_size) */ /* look for the first resource set which supports uci_size number of bits for payload */ while (pucch_resource_set_id < MAX_NB_OF_PUCCH_RESOURCE_SETS) { - if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id] != NULL) { - //pucch_max_pl_bits = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id]->maxPayloadMinus1; - if (uci_size <= 2) { //TBC rrc (((pucch_max_pl_bits != NULL) ? *pucch_max_pl_bits : 1706) + 1)) { - //NR_TST_PHY_PRINTF("PUCCH found resource set %d max bits %d\n", pucch_resource_set_id, pucch_max_pl_bits); + if ((bwp_id>0 && + mac->ULbwp[bwp_id-1] && + mac->ULbwp[bwp_id-1]->bwp_Dedicated && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id] != NULL) || + (bwp_id==0 && + mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id] != NULL)) { + if (uci_size <= 2) { pucch_resource_set_id = 0; return (pucch_resource_set_id); break; @@ -1506,7 +1602,7 @@ uint16_t get_nr_csi_bitlen(NR_UE_MAC_INST_t *mac) { uint16_t nb_ssbri_cri = 0; uint16_t cri_ssbri_bitlen = 0; - NR_CSI_MeasConfig_t *csi_MeasConfig = mac->scg->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; + NR_CSI_MeasConfig_t *csi_MeasConfig = mac->cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; struct NR_CSI_ResourceConfig__csi_RS_ResourceSetList__nzp_CSI_RS_SSB * nzp_CSI_RS_SSB = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[0]->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB; uint16_t nb_csi_ssb_report = nzp_CSI_RS_SSB->csi_SSB_ResourceSetList!=NULL ? nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.count:0; diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index aed4cb68ed..848b6d4e75 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -88,7 +88,7 @@ uint64_t downlink_frequency[MAX_NUM_CCs][4]; THREAD_STRUCT thread_struct; nfapi_ue_release_request_body_t release_rntis; msc_interface_t msc_interface; - +uint32_t N_RB_DL = 106; // dummy functions int dummy_nr_ue_ul_indication(nr_uplink_indication_t *ul_info) { return(0); } @@ -131,6 +131,12 @@ rrc_data_ind( { } +int ocp_gtpv1u_create_s1u_tunnel(instance_t instance, + const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, + gtpv1u_enb_create_tunnel_resp_t *create_tunnel_resp) { + return 0; +} + int gtpv1u_create_s1u_tunnel( const instance_t instanceP, @@ -166,6 +172,10 @@ gtpv1u_update_ngu_tunnel( return 0; } +int ocp_gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { + return 0; +} + int nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( const protocol_ctxt_t *const ctxt_pP, @@ -204,6 +214,17 @@ int is_x2ap_enabled(void) return 0; } +int DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(module_id_t module_idP, + int CC_idP, + int UE_id, + rnti_t rntiP, + const uint8_t *sduP, + sdu_size_t sdu_lenP, + const uint8_t *sdu2P, + sdu_size_t sdu2_lenP) { + return 0; +} + void processSlotTX(void *arg) {} //nFAPI P7 dummy functions @@ -217,7 +238,7 @@ int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } openair0_config_t openair0_cfg[MAX_CARDS]; void update_ptrs_config(NR_CellGroupConfig_t *secondaryCellGroup, uint16_t *rbSize, uint8_t *mcsIndex,int8_t *ptrs_arg); void update_dmrs_config(NR_CellGroupConfig_t *scg,PHY_VARS_NR_UE *ue, int8_t* dmrs_arg); -extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration +extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration /* specific dlsim DL preprocessor: uses rbStart/rbSize/mcs from command line of dlsim, does not search for CCE/PUCCH occasion but simply sets to 0 */ @@ -228,22 +249,22 @@ void nr_dlsim_preprocessor(module_id_t module_id, NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; AssertFatal(UE_info->num_UEs == 1, "can have only a single UE\n"); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[0]; + NR_ServingCellConfigCommon_t *scc = RC.nrmac[0]->common_channels[0].ServingCellConfigCommon; /* manually set free CCE to 0 */ const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; - sched_ctrl->search_space = get_searchspace(sched_ctrl->active_bwp, target_ss); + sched_ctrl->search_space = get_searchspace(scc, sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Dedicated : NULL, target_ss); uint8_t nr_of_candidates; find_aggregation_candidates(&sched_ctrl->aggregation_level, &nr_of_candidates, sched_ctrl->search_space); - sched_ctrl->coreset = get_coreset( - sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */); + sched_ctrl->coreset = get_coreset(scc, sched_ctrl->active_bwp, sched_ctrl->search_space, target_ss); sched_ctrl->cce_index = 0; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; - const NR_ServingCellConfigCommon_t *scc = RC.nrmac[0]->common_channels[0].ServingCellConfigCommon; + nr_set_pdsch_semi_static(scc, - UE_info->secondaryCellGroup[0], + UE_info->CellGroup[0], sched_ctrl->active_bwp, /* tda = */ 2, /* num_dmrs_cdm_grps_no_data = */ 1, @@ -293,7 +314,7 @@ void nr_dlsim_preprocessor(module_id_t module_id, typedef struct { uint64_t optmask; //mask to store boolean config options uint8_t nr_dlsch_parallel; // number of threads for dlsch decoding, 0 means no parallelization - tpool_t Tpool; // thread pool + tpool_t Tpool; // thread pool } nrUE_params_t; nrUE_params_t nrUE_params; @@ -367,8 +388,6 @@ int main(int argc, char **argv) NR_UE_MAC_INST_t *UE_mac; int cyclic_prefix_type = NFAPI_CP_NORMAL; int run_initial_sync=0; - int pusch_tgt_snrx10 = 200; - int pucch_tgt_snrx10 = 200; int loglvl=OAILOG_INFO; //float target_error_rate = 0.01; @@ -754,9 +773,9 @@ int main(int argc, char **argv) AssertFatal((gNB->if_inst = NR_IF_Module_init(0))!=NULL,"Cannot register interface"); gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0,n_tx,1,pusch_tgt_snrx10,pucch_tgt_snrx10,scc,0,0,NULL); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, scc, 0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0,n_tx,1,pusch_tgt_snrx10,pucch_tgt_snrx10,NULL,1,secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, scc, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); // reset preprocessor to the one of DLSIM after it has been set during // rrc_mac_config_req_gNB gNB_mac->pre_processor_dl = nr_dlsim_preprocessor; @@ -915,8 +934,7 @@ int main(int argc, char **argv) //Configure UE rrc.carrier.MIB = (uint8_t*) malloc(4); rrc.carrier.sizeof_MIB = do_MIB_NR(&rrc,0); - - nr_rrc_mac_config_req_ue(0,0,0,rrc.carrier.mib.message.choice.mib,secondaryCellGroup); + nr_rrc_mac_config_req_ue(0,0,0,rrc.carrier.mib.message.choice.mib, NULL, NULL, secondaryCellGroup); nr_dcireq_t dcireq; diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index bba0a6996f..93bed5514f 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -66,8 +66,10 @@ double cpuf; extern uint16_t prach_root_sequence_map0_3[838]; openair0_config_t openair0_cfg[MAX_CARDS]; //uint8_t nfapi_mode=0; +uint64_t downlink_frequency[MAX_NUM_CCs][4]; uint16_t sl_ahead = 0; msc_interface_t msc_interface; +uint32_t N_RB_DL = 106; //void dump_nr_prach_config(NR_DL_FRAME_PARMS *frame_parms,uint8_t subframe); @@ -90,6 +92,12 @@ rrc_data_ind( { } +int ocp_gtpv1u_create_s1u_tunnel(instance_t instance, + const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, + gtpv1u_enb_create_tunnel_resp_t *create_tunnel_resp) { + return 0; +} + int gtpv1u_create_s1u_tunnel( const instance_t instanceP, @@ -125,6 +133,10 @@ gtpv1u_update_ngu_tunnel( return 0; } +int ocp_gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { + return 0; +} + int nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( const protocol_ctxt_t *const ctxt_pP, @@ -174,10 +186,21 @@ int nr_derive_key(int alg_type, uint8_t alg_id, return 0; } +int DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(module_id_t module_idP, + int CC_idP, + int UE_id, + rnti_t rntiP, + const uint8_t *sduP, + sdu_size_t sdu_lenP, + const uint8_t *sdu2P, + sdu_size_t sdu2_lenP) { + return 0; +} + typedef struct { uint64_t optmask; //mask to store boolean config options uint8_t nr_dlsch_parallel; // number of threads for dlsch decoding, 0 means no parallelization - tpool_t Tpool; // thread pool + tpool_t Tpool; // thread pool } nrUE_params_t; nrUE_params_t nrUE_params; diff --git a/openair1/SIMULATION/NR_PHY/pucchsim.c b/openair1/SIMULATION/NR_PHY/pucchsim.c index 758afc6b3c..9c5fa5b8c9 100644 --- a/openair1/SIMULATION/NR_PHY/pucchsim.c +++ b/openair1/SIMULATION/NR_PHY/pucchsim.c @@ -92,6 +92,7 @@ int main(int argc, char **argv) //uint8_t nacktoack_flag=0; int16_t amp=0x7FFF; int nr_slot_tx=0; + int nr_frame_tx=0; uint64_t actual_payload=0,payload_received; int nr_bit=1; // maximum value possible is 2 uint8_t m0=0;// higher layer paramater initial cyclic shift @@ -502,13 +503,13 @@ int main(int argc, char **argv) for (trial=0; trialofdm_symbol_size*sizeof(int)); if(format==0){ - nr_generate_pucch0(UE,txdataF,frame_parms,PUCCH_GroupHopping,hopping_id,amp,nr_slot_tx,m0,mcs,nrofSymbols,startingSymbolIndex,startingPRB); + nr_generate_pucch0(UE,txdataF,frame_parms,PUCCH_GroupHopping,hopping_id,amp,nr_slot_tx,m0,mcs,nrofSymbols,startingSymbolIndex,startingPRB, 0); } else if (format == 1){ nr_generate_pucch1(UE,txdataF,frame_parms,UE->pucch_config_dedicated,actual_payload,amp,nr_slot_tx,m0,nrofSymbols,startingSymbolIndex,startingPRB,startingPRB_intraSlotHopping,0,nr_bit); } else { - nr_generate_pucch2(UE,0x1234,dmrs_scrambling_id,data_scrambling_id,txdataF,frame_parms,UE->pucch_config_dedicated,actual_payload,amp,nr_slot_tx,nrofSymbols,startingSymbolIndex,nrofPRB,startingPRB,nr_bit); + nr_generate_pucch2(UE,0x1234,dmrs_scrambling_id,data_scrambling_id,txdataF,frame_parms,UE->pucch_config_dedicated,actual_payload,amp,nr_slot_tx,nrofSymbols,startingSymbolIndex,nrofPRB,startingPRB,nr_bit); } int txlev = signal_energy(&txdataF[aa][startingSymbolIndex*frame_parms->ofdm_symbol_size], @@ -533,7 +534,7 @@ int main(int argc, char **argv) int rb2 = rb+startingPRB; gNB->rb_mask_ul[rb2>>5] |= (1<<(rb2&31)); } - gNB_I0_measurements(gNB); + gNB_I0_measurements(gNB, startingSymbolIndex, nrofSymbols); if (n_trials==1) printf("rxlev %d (%d dB), sigma2 %f dB, SNR %f, TX %f\n",rxlev,dB_fixed(rxlev),sigma2_dB,SNR,10*log10((double)txlev*UE->frame_parms.ofdm_symbol_size/12)); if(format==0){ @@ -551,7 +552,9 @@ int main(int argc, char **argv) pucch_pdu.initial_cyclic_shift = 0; pucch_pdu.start_symbol_index = startingSymbolIndex; pucch_pdu.prb_start = startingPRB; - nr_decode_pucch0(gNB,nr_slot_tx,&uci_pdu,&pucch_pdu); + pucch_pdu.bwp_start = 0; + pucch_pdu.freq_hop_flag = 0; + nr_decode_pucch0(gNB, nr_frame_tx, nr_slot_tx,&uci_pdu,&pucch_pdu); if(sr_flag==1){ if (uci_pdu.sr->sr_indication == 0 || uci_pdu.sr->sr_confidence_level == 1) sr_errors+=1; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 5dd05848c0..62f4420e75 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -84,6 +84,7 @@ uint64_t downlink_frequency[MAX_NUM_CCs][4]; THREAD_STRUCT thread_struct; nfapi_ue_release_request_body_t release_rntis; msc_interface_t msc_interface; +uint32_t N_RB_DL = 106; extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration @@ -120,6 +121,12 @@ rrc_data_ind( { } +int ocp_gtpv1u_create_s1u_tunnel(instance_t instance, + const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, + gtpv1u_enb_create_tunnel_resp_t *create_tunnel_resp) { + return 0; +} + int gtpv1u_create_s1u_tunnel( const instance_t instanceP, @@ -129,6 +136,10 @@ gtpv1u_create_s1u_tunnel( return 0; } +int ocp_gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { + return 0; +} + int rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( const protocol_ctxt_t *const ctxt_pP, @@ -185,6 +196,17 @@ int8_t nr_mac_rrc_data_req_ue(const module_id_t Mod_idP, return 0; } +int DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(module_id_t module_idP, + int CC_idP, + int UE_id, + rnti_t rntiP, + const uint8_t *sduP, + sdu_size_t sdu_lenP, + const uint8_t *sdu2P, + sdu_size_t sdu2_lenP) { + return 0; +} + //nFAPI P7 dummy functions int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { return(0); } @@ -296,8 +318,6 @@ int main(int argc, char **argv) int file_offset = 0; double DS_TDL = .03; - int pusch_tgt_snrx10 = 200; - int pucch_tgt_snrx10 = 200; int ibwps=24; int ibwp_rboffset=41; int params_from_file = 0; @@ -688,9 +708,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0,n_tx,n_rx,pusch_tgt_snrx10,pucch_tgt_snrx10,scc,0,0,NULL); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, scc, 0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0,1,1,pusch_tgt_snrx10,pucch_tgt_snrx10,NULL,1,secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, scc, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); phy_init_nr_gNB(gNB,0,1); N_RB_DL = gNB->frame_parms.N_RB_DL; @@ -748,7 +768,7 @@ int main(int argc, char **argv) rrc.carrier.MIB = (uint8_t*) malloc(4); rrc.carrier.sizeof_MIB = do_MIB_NR(&rrc,0); - nr_rrc_mac_config_req_ue(0,0,0,rrc.carrier.mib.message.choice.mib,secondaryCellGroup); + nr_rrc_mac_config_req_ue(0,0,0,rrc.carrier.mib.message.choice.mib, NULL, NULL, secondaryCellGroup); nr_ue_phy_config_request(&UE_mac->phy_config); @@ -1306,7 +1326,8 @@ int main(int argc, char **argv) (double)errors_scrambling[3]/available_bits/round_trials[0], roundStats[snrRun],effRate,effRate/TBS*100,TBS); - dump_pusch_stats(gNB); + FILE *fd=fopen("nr_ulsim.log","w"); + dump_pusch_stats(fd,gNB); printf("*****************************************\n"); printf("\n"); diff --git a/openair2/COMMON/f1ap_messages_def.h b/openair2/COMMON/f1ap_messages_def.h index 9e341f9bcb..6b1c6d9495 100644 --- a/openair2/COMMON/f1ap_messages_def.h +++ b/openair2/COMMON/f1ap_messages_def.h @@ -24,10 +24,13 @@ MESSAGE_DEF(F1AP_CU_SCTP_REQ , MESSAGE_PRIORITY_MED, f1ap_cu_setup_req_t /* eNB_DU application layer -> F1AP messages or CU F1AP -> RRC*/ MESSAGE_DEF(F1AP_SETUP_REQ , MESSAGE_PRIORITY_MED, f1ap_setup_req_t , f1ap_setup_req) +MESSAGE_DEF(F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE , MESSAGE_PRIORITY_MED, f1ap_gnb_cu_configuration_update_acknowledge_t , f1ap_gnb_cu_configuration_update_acknowledge) +MESSAGE_DEF(F1AP_GNB_CU_CONFIGURATION_UPDATE_FAILURE , MESSAGE_PRIORITY_MED, f1ap_gnb_cu_configuration_update_failure_t , f1ap_gnb_cu_configuration_update_failure) /* F1AP -> eNB_DU or eNB_CU_RRC -> F1AP application layer messages */ MESSAGE_DEF(F1AP_SETUP_RESP , MESSAGE_PRIORITY_MED, f1ap_setup_resp_t , f1ap_setup_resp) MESSAGE_DEF(F1AP_SETUP_FAILURE , MESSAGE_PRIORITY_MED, f1ap_setup_failure_t , f1ap_setup_failure) +MESSAGE_DEF(F1AP_GNB_CU_CONFIGURATION_UPDATE , MESSAGE_PRIORITY_MED, f1ap_gnb_cu_configuration_update_t , f1ap_gnb_cu_configuration_update) /* MAC -> F1AP messages */ MESSAGE_DEF(F1AP_INITIAL_UL_RRC_MESSAGE , MESSAGE_PRIORITY_MED, f1ap_initial_ul_rrc_message_t , f1ap_initial_ul_rrc_message) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index d83f9cb71d..c2fb4a9cf7 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -31,6 +31,9 @@ #define F1AP_SETUP_REQ(mSGpTR) (mSGpTR)->ittiMsg.f1ap_setup_req #define F1AP_SETUP_RESP(mSGpTR) (mSGpTR)->ittiMsg.f1ap_setup_resp +#define F1AP_GNB_CU_CONFIGURATION_UPDATE(mSGpTR) (mSGpTR)->ittiMsg.f1ap_gnb_cu_configuration_update +#define F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(mSGpTR) (mSGpTR)->ittiMsg.f1ap_gnb_cu_configuration_update_acknowledge +#define F1AP_GNB_CU_CONFIGURATION_UPDATE_FAILURE(mSGpTR) (mSGpTR)->ittiMsg.f1ap_gnb_cu_configuration_update_failure #define F1AP_SETUP_FAILURE(mSGpTR) (mSGpTR)->ittiMsg.f1ap_setup_failure #define F1AP_INITIAL_UL_RRC_MESSAGE(mSGpTR) (mSGpTR)->ittiMsg.f1ap_initial_ul_rrc_message @@ -55,6 +58,8 @@ // Note this should be 512 from maxval in 38.473 #define F1AP_MAX_NB_CELLS 2 +#define F1AP_MAX_NO_OF_TNL_ASSOCIATIONS 32 +#define F1AP_MAX_NO_UE_ID 1024 typedef struct f1ap_net_ip_address_s { unsigned ipv4:1; unsigned ipv6:1; @@ -98,7 +103,7 @@ typedef struct f1ap_setup_req_s { // Served Cell Information /* Tracking area code */ - uint16_t tac[F1AP_MAX_NB_CELLS]; + uint32_t tac[F1AP_MAX_NB_CELLS]; /* Mobile Country Codes * Mobile Network Codes @@ -174,6 +179,24 @@ typedef struct f1ap_setup_req_s { } f1ap_setup_req_t; +typedef struct served_cells_to_activate_s { + /// mcc of DU cells + uint16_t mcc; + /// mnc of DU cells + uint16_t mnc; + /// mnc digit length of DU cells + uint8_t mnc_digit_length; + // NR Global Cell Id + uint64_t nr_cellid; + /// NRPCI + uint16_t nrpci; + /// num SI messages per DU cell + uint8_t num_SI; + /// SI message containers (up to 21 messages per cell) + uint8_t *SI_container[21]; + int SI_container_length[21]; +} served_cells_to_activate_t; + typedef struct f1ap_setup_resp_s { /* Connexion id used between SCTP/F1AP */ uint16_t cnx_id; @@ -189,29 +212,62 @@ typedef struct f1ap_setup_resp_s { char *gNB_CU_name; /// number of DU cells to activate uint16_t num_cells_to_activate; //0< num_cells_to_activate <= 512; - /// mcc of DU cells - uint16_t mcc[F1AP_MAX_NB_CELLS]; - /// mnc of DU cells - uint16_t mnc[F1AP_MAX_NB_CELLS]; - /// mnc digit length of DU cells - uint8_t mnc_digit_length[F1AP_MAX_NB_CELLS]; - // NR Global Cell Id - uint64_t nr_cellid[F1AP_MAX_NB_CELLS]; - /// NRPCI - uint16_t nrpci[F1AP_MAX_NB_CELLS]; - /// num SI messages per DU cell - uint8_t num_SI[F1AP_MAX_NB_CELLS]; - /// SI message containers (up to 21 messages per cell) - uint8_t *SI_container[F1AP_MAX_NB_CELLS][21]; - int SI_container_length[F1AP_MAX_NB_CELLS][21]; + served_cells_to_activate_t cells_to_activate[F1AP_MAX_NB_CELLS]; } f1ap_setup_resp_t; +typedef struct f1ap_gnb_cu_configuration_update_s { + /* Connexion id used between SCTP/F1AP */ + uint16_t cnx_id; + + /* SCTP association id */ + int32_t assoc_id; + + /* Number of SCTP streams used for a mme association */ + uint16_t sctp_in_streams; + uint16_t sctp_out_streams; + + /// string holding gNB_CU_name + char *gNB_CU_name; + /// number of DU cells to activate + uint16_t num_cells_to_activate; //0< num_cells_to_activate/mod <= 512; + served_cells_to_activate_t cells_to_activate[F1AP_MAX_NB_CELLS]; +} f1ap_gnb_cu_configuration_update_t; + typedef struct f1ap_setup_failure_s { uint16_t cause; uint16_t time_to_wait; uint16_t criticality_diagnostics; } f1ap_setup_failure_t; +typedef struct f1ap_gnb_cu_configuration_update_acknowledge_s { + uint16_t num_cells_failed_to_be_activated; + uint16_t mcc[F1AP_MAX_NB_CELLS]; + uint16_t mnc[F1AP_MAX_NB_CELLS]; + uint8_t mnc_digit_length[F1AP_MAX_NB_CELLS]; + uint64_t nr_cellid[F1AP_MAX_NB_CELLS]; + uint16_t cause[F1AP_MAX_NB_CELLS]; + int have_criticality; + uint16_t criticality_diagnostics; + uint16_t noofTNLAssociations_to_setup; + uint16_t have_port[F1AP_MAX_NO_OF_TNL_ASSOCIATIONS]; + in_addr_t tl_address[F1AP_MAX_NO_OF_TNL_ASSOCIATIONS]; // currently only IPv4 supported + uint16_t noofTNLAssociations_failed; + in_addr_t tl_address_failed[F1AP_MAX_NO_OF_TNL_ASSOCIATIONS]; // currently only IPv4 supported + uint16_t cause_failed[F1AP_MAX_NO_OF_TNL_ASSOCIATIONS]; + uint16_t noofDedicatedSIDeliveryNeededUEs; + uint32_t gNB_CU_ue_id[F1AP_MAX_NO_UE_ID]; + uint16_t ue_mcc[F1AP_MAX_NO_UE_ID]; + uint16_t ue_mnc[F1AP_MAX_NO_UE_ID]; + uint8_t ue_mnc_digit_length[F1AP_MAX_NO_UE_ID]; + uint64_t ue_nr_cellid[F1AP_MAX_NO_UE_ID]; +} f1ap_gnb_cu_configuration_update_acknowledge_t; + +typedef struct f1ap_gnb_cu_configuration_update_failure_s { + uint16_t cause; + uint16_t time_to_wait; + uint16_t criticality_diagnostics; +} f1ap_gnb_cu_configuration_update_failure_t; + typedef struct f1ap_dl_rrc_message_s { uint32_t gNB_CU_ue_id; @@ -243,7 +299,7 @@ typedef struct f1ap_initial_ul_rrc_message_s { uint16_t crnti; uint8_t *rrc_container; int rrc_container_length; - uint8_t *du2cu_rrc_container; + int8_t *du2cu_rrc_container; int du2cu_rrc_container_length; } f1ap_initial_ul_rrc_message_t; diff --git a/openair2/COMMON/gtpv1_u_messages_def.h b/openair2/COMMON/gtpv1_u_messages_def.h index 86103dd911..e5ce496701 100644 --- a/openair2/COMMON/gtpv1_u_messages_def.h +++ b/openair2/COMMON/gtpv1_u_messages_def.h @@ -33,4 +33,5 @@ MESSAGE_DEF(GTPV1U_ENB_S1_REQ, MESSAGE_PRIORITY_MED, Gtpv1uS1Req, gtpv MESSAGE_DEF(GTPV1U_GNB_DELETE_TUNNEL_REQ, MESSAGE_PRIORITY_MED, gtpv1u_gnb_delete_tunnel_req_t, NRGtpv1uDeleteTunnelReq) MESSAGE_DEF(GTPV1U_GNB_DELETE_TUNNEL_RESP, MESSAGE_PRIORITY_MED, gtpv1u_gnb_delete_tunnel_resp_t, NRGtpv1uDeleteTunnelResp) -MESSAGE_DEF(GTPV1U_GNB_NG_REQ, MESSAGE_PRIORITY_MED, Gtpv1uNGReq, gtpv1uNGReq) +MESSAGE_DEF(GTPV1U_GNB_NG_REQ, MESSAGE_PRIORITY_MED, Gtpv1uNGReq, gtpv1uNGReq) +MESSAGE_DEF(GTPV1U_GNB_TUNNEL_DATA_REQ, MESSAGE_PRIORITY_MED, gtpv1u_gnb_tunnel_data_req_t, NRGtpv1uTunnelDataReq) diff --git a/openair2/COMMON/gtpv1_u_messages_types.h b/openair2/COMMON/gtpv1_u_messages_types.h index f5b6314fe1..75df3253f6 100644 --- a/openair2/COMMON/gtpv1_u_messages_types.h +++ b/openair2/COMMON/gtpv1_u_messages_types.h @@ -44,6 +44,7 @@ #define GTPV1U_GNB_DELETE_TUNNEL_REQ(mSGpTR) (mSGpTR)->ittiMsg.NRGtpv1uDeleteTunnelReq #define GTPV1U_GNB_DELETE_TUNNEL_RESP(mSGpTR) (mSGpTR)->ittiMsg.NRGtpv1uDeleteTunnelResp #define GTPV1U_GNB_NG_REQ(mSGpTR) (mSGpTR)->ittiMsg.gtpv1uNGReq +#define GTPV1U_GNB_TUNNEL_DATA_REQ(mSGpTR) (mSGpTR)->ittiMsg.NRGtpv1uTunnelDataReq #define GTPV1U_ALL_TUNNELS_TEID (teid_t)0xFFFFFFFF @@ -167,21 +168,23 @@ typedef struct gtpv1u_enb_end_marker_ind_s { uint32_t sdu_size; uint8_t *sdu_p; uint8_t mode; - uint16_t rnti; - uint8_t module_id; + uint16_t rnti; + uint8_t module_id; uint8_t eNB_index; } gtpv1u_enb_end_marker_ind_t; typedef struct { in_addr_t enb_ip_address_for_S1u_S12_S4_up; tcp_udp_port_t enb_port_for_S1u_S12_S4_up; - char addrStr[256]; - char portStr[256]; + char addrStr[256]; + char portStr[256]; } Gtpv1uS1Req; typedef struct { in_addr_t gnb_ip_address_for_NGu_up; tcp_udp_port_t gnb_port_for_NGu_up; + char addrStr[256]; + char portStr[256]; } Gtpv1uNGReq; typedef struct gtpv1u_gnb_create_tunnel_req_s { rnti_t rnti; @@ -212,4 +215,12 @@ typedef struct gtpv1u_gnb_delete_tunnel_resp_s { teid_t gnb_NGu_teid; ///< local NGU Tunnel Endpoint Identifier to be deleted } gtpv1u_gnb_delete_tunnel_resp_t; +typedef struct gtpv1u_gnb_tunnel_data_req_s { + uint8_t *buffer; + uint32_t length; + uint32_t offset; ///< start of message offset in buffer + rnti_t rnti; + pdusessionid_t pdusession_id; +} gtpv1u_gnb_tunnel_data_req_t; + #endif /* GTPV1_U_MESSAGES_TYPES_H_ */ diff --git a/openair2/COMMON/mac_messages_types.h b/openair2/COMMON/mac_messages_types.h index f42813ecb0..a9336a58a9 100644 --- a/openair2/COMMON/mac_messages_types.h +++ b/openair2/COMMON/mac_messages_types.h @@ -30,6 +30,7 @@ #define MAC_MESSAGES_TYPES_H_ #include +#include "OCTET_STRING.h" //-------------------------------------------------------------------------------------------// // Defines to access message fields. @@ -143,6 +144,7 @@ typedef struct NRRrcMacCcchDataInd_s { uint16_t rnti; uint32_t sdu_size; uint8_t sdu[CCCH_SDU_SIZE]; + OCTET_STRING_t *du_to_cu_rrc_container; uint8_t gnb_index; int CC_id; } NRRrcMacCcchDataInd; diff --git a/openair2/COMMON/pdcp_messages_def.h b/openair2/COMMON/pdcp_messages_def.h index b148ae0e1f..59d47eb81b 100644 --- a/openair2/COMMON/pdcp_messages_def.h +++ b/openair2/COMMON/pdcp_messages_def.h @@ -33,4 +33,5 @@ MESSAGE_DEF(RRC_DCCH_DATA_IND, MESSAGE_PRIORITY_MED_PLUS, RrcDcchDataIn MESSAGE_DEF(RRC_PCCH_DATA_REQ, MESSAGE_PRIORITY_MED_PLUS, RrcPcchDataReq, rrc_pcch_data_req) // gNB +MESSAGE_DEF(NR_RRC_DCCH_DATA_REQ, MESSAGE_PRIORITY_MED_PLUS, NRRrcDcchDataReq, nr_rrc_dcch_data_req) MESSAGE_DEF(NR_RRC_DCCH_DATA_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcDcchDataInd, nr_rrc_dcch_data_ind) diff --git a/openair2/COMMON/pdcp_messages_types.h b/openair2/COMMON/pdcp_messages_types.h index daf8ba7cac..0390fd80f4 100644 --- a/openair2/COMMON/pdcp_messages_types.h +++ b/openair2/COMMON/pdcp_messages_types.h @@ -36,6 +36,7 @@ #define RRC_PCCH_DATA_REQ(mSGpTR) (mSGpTR)->ittiMsg.rrc_pcch_data_req // gNB +#define NR_RRC_DCCH_DATA_REQ(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_dcch_data_req #define NR_RRC_DCCH_DATA_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_dcch_data_ind //-------------------------------------------------------------------------------------------// @@ -64,6 +65,20 @@ typedef struct RrcDcchDataInd_s { uint8_t eNB_index; // LG: needed in UE } RrcDcchDataInd; +typedef struct NRRrcDcchDataReq_s { + uint32_t frame; + uint8_t gnb_flag; + rb_id_t rb_id; + uint32_t muip; + uint32_t confirmp; + uint32_t sdu_size; + uint8_t *sdu_p; + uint8_t mode; + uint16_t rnti; + uint8_t module_id; + uint8_t gNB_index; +} NRRrcDcchDataReq; + typedef struct NRRrcDcchDataInd_s { uint32_t frame; uint8_t dcch_index; diff --git a/openair2/COMMON/platform_constants.h b/openair2/COMMON/platform_constants.h index acfc0920aa..c56a31711a 100644 --- a/openair2/COMMON/platform_constants.h +++ b/openair2/COMMON/platform_constants.h @@ -89,6 +89,7 @@ #define MAX_gNB 2 #endif +#define NUMBER_OF_NR_UCI_STATS_MAX 16 #define MAX_MANAGED_ENB_PER_MOBILE 2 #define MAX_MANAGED_GNB_PER_MOBILE 2 diff --git a/openair2/ENB_APP/enb_config.c b/openair2/ENB_APP/enb_config.c index fe706b2143..4751596931 100644 --- a/openair2/ENB_APP/enb_config.c +++ b/openair2/ENB_APP/enb_config.c @@ -3161,25 +3161,25 @@ void handle_f1ap_setup_resp(f1ap_setup_resp_t *resp) { rrc_eNB_carrier_data_t *carrier = &RC.rrc[i]->carrier[0]; // identify local index of cell j by nr_cellid, plmn identity and physical cell ID LOG_I(ENB_APP, "Checking cell %d, rrc inst %d : rrc->nr_cellid %lx, resp->nr_cellid %lx\n", - j,i,RC.rrc[i]->nr_cellid,resp->nr_cellid[j]); + j,i,RC.rrc[i]->nr_cellid,resp->cells_to_activate[j].nr_cellid); - if (RC.rrc[i]->nr_cellid == resp->nr_cellid[j] && - (check_plmn_identity(carrier, resp->mcc[j], resp->mnc[j], resp->mnc_digit_length[j])>0 && - resp->nrpci[j] == carrier->physCellId)) { + if (RC.rrc[i]->nr_cellid == resp->cells_to_activate[j].nr_cellid && + (check_plmn_identity(carrier, resp->cells_to_activate[j].mcc, resp->cells_to_activate[j].mnc, resp->cells_to_activate[j].mnc_digit_length)>0 && + resp->cells_to_activate[j].nrpci == carrier->physCellId)) { // copy system information and decode it - for (si_ind=2; si_ind<10; si_ind++) { - //printf("SI %d size %d: ", si_ind, resp->SI_container_length[j][si_ind]); - //for (int n=0;nSI_container_length[j][si_ind];n++) - // printf("%02x ",resp->SI_container[j][si_ind][n]); + for (si_ind=2; si_indcells_to_activate[j].num_SI + 2; si_ind++) { + //printf("SI %d size %d: ", si_ind, resp->cells_to_activate[j].SI_container_length[si_ind]); + //for (int n=0;ncells_to_activate[j].SI_container_length[si_ind];n++) + // printf("%02x ",resp->cells_to_activate[j].SI_container[si_ind][n]); //printf("\n"); if (si_ind==6) si_ind=9; - if (resp->SI_container[j][si_ind] != NULL) { - extract_and_decode_SI(i, + if (resp->cells_to_activate[j].SI_container[si_ind] != NULL) { + extract_and_decode_SI(i, si_ind, - resp->SI_container[j][si_ind], - resp->SI_container_length[j][si_ind]); + resp->cells_to_activate[j].SI_container[si_ind], + resp->cells_to_activate[j].SI_container_length[si_ind]); } - + } // perform MAC/L1 common configuration diff --git a/openair2/F1AP/dummy_enb.c b/openair2/F1AP/dummy_enb.c new file mode 100644 index 0000000000..ee98b9ec46 --- /dev/null +++ b/openair2/F1AP/dummy_enb.c @@ -0,0 +1,61 @@ +/* + * 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 "COMMON/platform_types.h" +#include "COMMON/platform_constants.h" +#include "common/ran_context.h" + +#include "common/utils/LOG/log.h" +#include "common/utils/LOG/vcd_signal_dumper.h" + +#include "NR_BCCH-BCH-Message.h" +#include "NR_ServingCellConfigCommon.h" + +#include "LAYER2/NR_MAC_gNB/mac_proto.h" +#include "SCHED_NR/phy_frame_config_nr.h" + +#include "NR_MIB.h" +#include "LAYER2/NR_MAC_COMMON/nr_mac_common.h" +#include "../../../../nfapi/oai_integration/vendor_ext.h" +/* Softmodem params */ +#include "executables/softmodem-common.h" + + +int rrc_mac_config_req_gNB(module_id_t Mod_idP, + int ssb_SubcarrierOffset, + int pdsch_AntennaPorts, + int pusch_AntennaPorts, + NR_ServingCellConfigCommon_t *scc, + int add_ue, + uint32_t rnti, + NR_CellGroupConfig_t *CellGroup){ +abort(); +return 0; +} +rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP, + const NR_SRB_ToAddModList_t * const srb2add_listP, + const NR_DRB_ToAddModList_t * const drb2add_listP, + const NR_DRB_ToReleaseList_t * const drb2release_listP, + const LTE_PMCH_InfoList_r9_t * const pmch_InfoList_r9_pP, + struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list){ +abort(); +return 0; +} diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c index 20926c10d1..057d0d97a0 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.c +++ b/openair2/F1AP/f1ap_cu_interface_management.c @@ -37,6 +37,7 @@ #include "f1ap_cu_interface_management.h" extern f1ap_setup_req_t *f1ap_du_data_from_du; +extern RAN_CONTEXT_t RC; int CU_send_RESET(instance_t instance, F1AP_Reset_t *Reset) { AssertFatal(1==0,"Not implemented yet\n"); @@ -102,8 +103,12 @@ int CU_handle_F1_SETUP_REQUEST(instance_t instance, assoc_id, stream); } - message_p = itti_alloc_new_message(TASK_RRC_ENB, 0, F1AP_SETUP_REQ); - + if(RC.nrrrc) { + message_p = itti_alloc_new_message(TASK_CU_F1, 0, F1AP_SETUP_REQ); + } else { + message_p = itti_alloc_new_message(TASK_RRC_ENB, 0, F1AP_SETUP_REQ); + } + /* assoc_id */ F1AP_SETUP_REQ(message_p).assoc_id = assoc_id; @@ -134,58 +139,57 @@ int CU_handle_F1_SETUP_REQUEST(instance_t instance, int num_cells_available = F1AP_SETUP_REQ(message_p).num_cells_available; for (i=0; ivalue.choice.GNB_DU_Served_Cells_List.list.array[i])->value.choice.GNB_DU_Served_Cells_Item); + served_cells_item_p = &(((F1AP_GNB_DU_Served_Cells_ItemIEs_t *)ie->value.choice.GNB_DU_Served_Cells_List.list.array[i])->value.choice.GNB_DU_Served_Cells_Item); /* tac */ - if (served_celles_item_p->served_Cell_Information.fiveGS_TAC) { - OCTET_STRING_TO_INT16(served_celles_item_p->served_Cell_Information.fiveGS_TAC, F1AP_SETUP_REQ(message_p).tac[i]); - LOG_D(F1AP, "F1AP_SETUP_REQ(message_p).tac[%d] %d \n", - i, F1AP_SETUP_REQ(message_p).tac[i]); + if (served_cells_item_p->served_Cell_Information.fiveGS_TAC) { + OCTET_STRING_TO_INT16(served_cells_item_p->served_Cell_Information.fiveGS_TAC, F1AP_SETUP_REQ(message_p).tac[i]); + LOG_D(F1AP, "F1AP_SETUP_REQ(message_p).tac[%d] %d \n", i, F1AP_SETUP_REQ(message_p).tac[i]); } /* - nRCGI */ - TBCD_TO_MCC_MNC(&(served_celles_item_p->served_Cell_Information.nRCGI.pLMN_Identity), F1AP_SETUP_REQ(message_p).mcc[i], + TBCD_TO_MCC_MNC(&(served_cells_item_p->served_Cell_Information.nRCGI.pLMN_Identity), F1AP_SETUP_REQ(message_p).mcc[i], F1AP_SETUP_REQ(message_p).mnc[i], F1AP_SETUP_REQ(message_p).mnc_digit_length[i]); // NR cellID - BIT_STRING_TO_NR_CELL_IDENTITY(&served_celles_item_p->served_Cell_Information.nRCGI.nRCellIdentity, + BIT_STRING_TO_NR_CELL_IDENTITY(&served_cells_item_p->served_Cell_Information.nRCGI.nRCellIdentity, F1AP_SETUP_REQ(message_p).nr_cellid[i]); LOG_D(F1AP, "[SCTP %d] Received nRCGI: MCC %d, MNC %d, CELL_ID %llu\n", assoc_id, F1AP_SETUP_REQ(message_p).mcc[i], F1AP_SETUP_REQ(message_p).mnc[i], (long long unsigned int)F1AP_SETUP_REQ(message_p).nr_cellid[i]); LOG_D(F1AP, "nr_cellId : %x %x %x %x %x\n", - served_celles_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[0], - served_celles_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[1], - served_celles_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[2], - served_celles_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[3], - served_celles_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[4]); + served_cells_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[0], + served_cells_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[1], + served_cells_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[2], + served_cells_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[3], + served_cells_item_p->served_Cell_Information.nRCGI.nRCellIdentity.buf[4]); /* - nRPCI */ - F1AP_SETUP_REQ(message_p).nr_pci[i] = served_celles_item_p->served_Cell_Information.nRPCI; + F1AP_SETUP_REQ(message_p).nr_pci[i] = served_cells_item_p->served_Cell_Information.nRPCI; LOG_D(F1AP, "F1AP_SETUP_REQ(message_p).nr_pci[%d] %d \n", i, F1AP_SETUP_REQ(message_p).nr_pci[i]); // System Information /* mib */ - F1AP_SETUP_REQ(message_p).mib[i] = calloc(served_celles_item_p->gNB_DU_System_Information->mIB_message.size + 1, sizeof(char)); - memcpy(F1AP_SETUP_REQ(message_p).mib[i], served_celles_item_p->gNB_DU_System_Information->mIB_message.buf, - served_celles_item_p->gNB_DU_System_Information->mIB_message.size); + F1AP_SETUP_REQ(message_p).mib[i] = calloc(served_cells_item_p->gNB_DU_System_Information->mIB_message.size + 1, sizeof(char)); + memcpy(F1AP_SETUP_REQ(message_p).mib[i], served_cells_item_p->gNB_DU_System_Information->mIB_message.buf, + served_cells_item_p->gNB_DU_System_Information->mIB_message.size); /* Convert the mme name to a printable string */ - F1AP_SETUP_REQ(message_p).mib[i][served_celles_item_p->gNB_DU_System_Information->mIB_message.size] = '\0'; - F1AP_SETUP_REQ(message_p).mib_length[i] = served_celles_item_p->gNB_DU_System_Information->mIB_message.size; + F1AP_SETUP_REQ(message_p).mib[i][served_cells_item_p->gNB_DU_System_Information->mIB_message.size] = '\0'; + F1AP_SETUP_REQ(message_p).mib_length[i] = served_cells_item_p->gNB_DU_System_Information->mIB_message.size; LOG_D(F1AP, "F1AP_SETUP_REQ(message_p).mib[%d] %s , len = %d \n", i, F1AP_SETUP_REQ(message_p).mib[i], F1AP_SETUP_REQ(message_p).mib_length[i]); /* sib1 */ - F1AP_SETUP_REQ(message_p).sib1[i] = calloc(served_celles_item_p->gNB_DU_System_Information->sIB1_message.size + 1, sizeof(char)); - memcpy(F1AP_SETUP_REQ(message_p).sib1[i], served_celles_item_p->gNB_DU_System_Information->sIB1_message.buf, - served_celles_item_p->gNB_DU_System_Information->sIB1_message.size); + F1AP_SETUP_REQ(message_p).sib1[i] = calloc(served_cells_item_p->gNB_DU_System_Information->sIB1_message.size + 1, sizeof(char)); + memcpy(F1AP_SETUP_REQ(message_p).sib1[i], served_cells_item_p->gNB_DU_System_Information->sIB1_message.buf, + served_cells_item_p->gNB_DU_System_Information->sIB1_message.size); /* Convert the mme name to a printable string */ - F1AP_SETUP_REQ(message_p).sib1[i][served_celles_item_p->gNB_DU_System_Information->sIB1_message.size] = '\0'; - F1AP_SETUP_REQ(message_p).sib1_length[i] = served_celles_item_p->gNB_DU_System_Information->sIB1_message.size; + F1AP_SETUP_REQ(message_p).sib1[i][served_cells_item_p->gNB_DU_System_Information->sIB1_message.size] = '\0'; + F1AP_SETUP_REQ(message_p).sib1_length[i] = served_cells_item_p->gNB_DU_System_Information->sIB1_message.size; LOG_D(F1AP, "F1AP_SETUP_REQ(message_p).sib1[%d] %s , len = %d \n", i, F1AP_SETUP_REQ(message_p).sib1[i], F1AP_SETUP_REQ(message_p).sib1_length[i]); } @@ -246,10 +250,18 @@ int CU_handle_F1_SETUP_REQUEST(instance_t instance, ); if (num_cells_available > 0) { - itti_send_msg_to_task(TASK_RRC_ENB, ENB_MODULE_ID_TO_INSTANCE(instance), message_p); + if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_CU) { + itti_send_msg_to_task(TASK_RRC_GNB, GNB_MODULE_ID_TO_INSTANCE(instance), message_p); + } else { + itti_send_msg_to_task(TASK_RRC_ENB, ENB_MODULE_ID_TO_INSTANCE(instance), message_p); + } } else { CU_send_F1_SETUP_FAILURE(instance); - itti_free(TASK_RRC_ENB,message_p); + if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_CU) { + itti_free(TASK_RRC_GNB,message_p); + } else { + itti_free(TASK_RRC_ENB,message_p); + } return -1; } return 0; @@ -306,45 +318,51 @@ int CU_send_F1_SETUP_RESPONSE(instance_t instance, /* mandatory */ /* c3. cells to be Activated list */ - ie = (F1AP_F1SetupResponseIEs_t *)calloc(1, sizeof(F1AP_F1SetupResponseIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_F1SetupResponseIEs__value_PR_Cells_to_be_Activated_List; - int num_cells_to_activate = f1ap_setup_resp->num_cells_to_activate; LOG_D(F1AP, "num_cells_to_activate = %d \n", num_cells_to_activate); - for (i=0; - i0) { + ie = (F1AP_F1SetupResponseIEs_t *)calloc(1, sizeof(F1AP_F1SetupResponseIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_F1SetupResponseIEs__value_PR_Cells_to_be_Activated_List; - F1AP_Cells_to_be_Activated_List_ItemIEs_t *cells_to_be_activated_list_item_ies; - cells_to_be_activated_list_item_ies = (F1AP_Cells_to_be_Activated_List_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Activated_List_ItemIEs_t)); - cells_to_be_activated_list_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; - cells_to_be_activated_list_item_ies->criticality = F1AP_Criticality_reject; - cells_to_be_activated_list_item_ies->value.present = F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item; + for (i=0; icells_to_activate[i].mcc, + f1ap_setup_resp->cells_to_activate[i].mnc, + f1ap_setup_resp->cells_to_activate[i].mnc_digit_length, + f1ap_setup_resp->cells_to_activate[i].nr_cellid); - /* - nRCGI */ - F1AP_NRCGI_t nRCGI; - memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); - MCC_MNC_TO_PLMNID(f1ap_setup_resp->mcc[i], f1ap_setup_resp->mnc[i], f1ap_setup_resp->mnc_digit_length[i], - &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(f1ap_setup_resp->nr_cellid[i], &nRCGI.nRCellIdentity); - cells_to_be_activated_list_item.nRCGI = nRCGI; + F1AP_Cells_to_be_Activated_List_ItemIEs_t *cells_to_be_activated_list_item_ies; + cells_to_be_activated_list_item_ies = (F1AP_Cells_to_be_Activated_List_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Activated_List_ItemIEs_t)); + cells_to_be_activated_list_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; + cells_to_be_activated_list_item_ies->criticality = F1AP_Criticality_reject; + cells_to_be_activated_list_item_ies->value.present = F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item; - /* optional */ - /* - nRPCI */ - if (1) { - cells_to_be_activated_list_item.nRPCI = (F1AP_NRPCI_t *)calloc(1, sizeof(F1AP_NRPCI_t)); - *cells_to_be_activated_list_item.nRPCI = f1ap_setup_resp->nrpci[i]; // int 0..1007 - } + /* 3.1 cells to be Activated list item */ + F1AP_Cells_to_be_Activated_List_Item_t cells_to_be_activated_list_item; + memset((void *)&cells_to_be_activated_list_item, 0, sizeof(F1AP_Cells_to_be_Activated_List_Item_t)); - /* optional */ - /* - gNB-CU System Information */ - if (1) { + /* - nRCGI */ + F1AP_NRCGI_t nRCGI; + memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); + MCC_MNC_TO_PLMNID(f1ap_setup_resp->cells_to_activate[i].mcc, f1ap_setup_resp->cells_to_activate[i].mnc, f1ap_setup_resp->cells_to_activate[i].mnc_digit_length, + &nRCGI.pLMN_Identity); + NR_CELL_ID_TO_BIT_STRING(f1ap_setup_resp->cells_to_activate[i].nr_cellid, &nRCGI.nRCellIdentity); + cells_to_be_activated_list_item.nRCGI = nRCGI; + + /* optional */ + /* - nRPCI */ + if (1) { + cells_to_be_activated_list_item.nRPCI = (F1AP_NRPCI_t *)calloc(1, sizeof(F1AP_NRPCI_t)); + *cells_to_be_activated_list_item.nRPCI = f1ap_setup_resp->cells_to_activate[i].nrpci; // int 0..1007 + } + + /* optional */ + /* - gNB-CU System Information */ + if (1) { /* 3.1.2 gNB-CUSystem Information */ F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *cells_to_be_activated_list_itemExtIEs; cells_to_be_activated_list_itemExtIEs = (F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Activated_List_ItemExtIEs_t)); @@ -356,47 +374,44 @@ int CU_send_F1_SETUP_RESPONSE(instance_t instance, //LOG_I(F1AP, "%s() SI %d size %d: ", __func__, i, f1ap_setup_resp->SI_container_length[i][0]); //for (int n = 0; n < f1ap_setup_resp->SI_container_length[i][0]; n++) // printf("%02x ", f1ap_setup_resp->SI_container[i][0][n]); - //printf("\n"); - for (int j=2; - j<10; - j++) { - if (j==6) j=9; - if (f1ap_setup_resp->SI_container[i][j]!=NULL) { - LOG_I(F1AP,"SETUP RESP: SI %d, Type %d\n",i,j); - AssertFatal((j < 6) || (j == 9), "Illegal SI type %d\n",j); + //printf("\n"); + + // for (int sIBtype=2;sIBtype<33;sIBtype++) { //21 ? 33 ? + for (int sIBtype=2;sIBtype<21;sIBtype++) { + if (f1ap_setup_resp->cells_to_activate[i].SI_container[sIBtype]!=NULL) { + AssertFatal(sIBtype < 6 || sIBtype == 9, "Illegal SI type %d\n",sIBtype); F1AP_SibtypetobeupdatedListItem_t *sib_item = calloc(1,sizeof(*sib_item)); - sib_item->sIBtype = j; + memset((void*)sib_item,0,sizeof(*sib_item)); + sib_item->sIBtype = sIBtype; OCTET_STRING_fromBuf(&sib_item->sIBmessage, - (const char*)f1ap_setup_resp->SI_container[i][j], - f1ap_setup_resp->SI_container_length[i][j]); - - LOG_I(F1AP, "f1ap_setup_resp->SI_container_length[%d][%d] = %d \n", i,j,f1ap_setup_resp->SI_container_length[i][j]); - ASN_SEQUENCE_ADD(&gNB_CUSystemInformation->sibtypetobeupdatedlist.list,sib_item); + (const char*)f1ap_setup_resp->cells_to_activate[i].SI_container[sIBtype], + f1ap_setup_resp->cells_to_activate[i].SI_container_length[sIBtype]); + + LOG_D(F1AP, "f1ap_setup_resp->SI_container_length[%d][%d] = %d \n", i,sIBtype,f1ap_setup_resp->cells_to_activate[i].SI_container_length[sIBtype]); + ASN_SEQUENCE_ADD(&gNB_CUSystemInformation->sibtypetobeupdatedlist.list,sib_item); } } cells_to_be_activated_list_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation = *gNB_CUSystemInformation; - F1AP_ProtocolExtensionContainer_154P112_t p_154P112_t; memset((void *)&p_154P112_t, 0, sizeof(F1AP_ProtocolExtensionContainer_154P112_t)); - ASN_SEQUENCE_ADD(&p_154P112_t.list, - cells_to_be_activated_list_itemExtIEs); + ASN_SEQUENCE_ADD(&p_154P112_t.list, cells_to_be_activated_list_itemExtIEs); cells_to_be_activated_list_item.iE_Extensions = (struct F1AP_ProtocolExtensionContainer*)&p_154P112_t; free(gNB_CUSystemInformation); gNB_CUSystemInformation = NULL; + } + /* ADD */ + cells_to_be_activated_list_item_ies->value.choice.Cells_to_be_Activated_List_Item = cells_to_be_activated_list_item; + ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Activated_List.list, cells_to_be_activated_list_item_ies); } - /* ADD */ - cells_to_be_activated_list_item_ies->value.choice.Cells_to_be_Activated_List_Item = cells_to_be_activated_list_item; - ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Activated_List.list, - cells_to_be_activated_list_item_ies); + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 setup response\n"); return -1; } @@ -482,7 +497,7 @@ int CU_send_F1_SETUP_FAILURE(instance_t instance) { /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 setup failure\n"); return -1; } @@ -521,7 +536,7 @@ int CU_send_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, */ //void CU_send_gNB_CU_CONFIGURATION_UPDATE(F1AP_GNBCUConfigurationUpdate_t *GNBCUConfigurationUpdate) { -int CU_send_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, module_id_t du_mod_idP) { +int CU_send_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, f1ap_gnb_cu_configuration_update_t *f1ap_gnb_cu_configuration_update) { F1AP_F1AP_PDU_t pdu; F1AP_GNBCUConfigurationUpdate_t *out; F1AP_GNBCUConfigurationUpdateIEs_t *ie; @@ -530,10 +545,6 @@ int CU_send_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, module_id_t du_mod_ uint32_t len; int i = 0; - // for test - int mcc = 208; - int mnc = 93; - int mnc_digit_length = 8; /* Create */ /* 0. Message Type */ @@ -551,304 +562,355 @@ int CU_send_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, module_id_t du_mod_ ie->id = F1AP_ProtocolIE_ID_id_TransactionID; ie->criticality = F1AP_Criticality_reject; ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_TransactionID; - ie->value.choice.TransactionID = F1AP_get_next_transaction_identifier(instance, du_mod_idP); + ie->value.choice.TransactionID = F1AP_get_next_transaction_identifier(instance, 0); ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - /* mandatory */ - /* c2. Cells_to_be_Activated_List */ - ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Activated_List; + // mandatory + // c2. Cells_to_be_Activated_List + if (f1ap_gnb_cu_configuration_update->num_cells_to_activate > 0) { + ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Activated_List; - for (i=0; - i<1; - i++) { + for (i=0; inum_cells_to_activate; i++) { - F1AP_Cells_to_be_Activated_List_ItemIEs_t *cells_to_be_activated_list_item_ies; - cells_to_be_activated_list_item_ies = (F1AP_Cells_to_be_Activated_List_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Activated_List_ItemIEs_t)); - cells_to_be_activated_list_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; - cells_to_be_activated_list_item_ies->criticality = F1AP_Criticality_reject; - cells_to_be_activated_list_item_ies->value.present = F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item; + LOG_D(F1AP, "[c2] (cell %d) mcc = %d, mnc = %d, mnc_digit_length = %d, nr_cellid = %lu\n", + i, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mcc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc_digit_length, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].nr_cellid); - /* 2.1 cells to be Activated list item */ - F1AP_Cells_to_be_Activated_List_Item_t cells_to_be_activated_list_item; - memset((void *)&cells_to_be_activated_list_item, 0, sizeof(F1AP_Cells_to_be_Activated_List_Item_t)); + F1AP_Cells_to_be_Activated_List_ItemIEs_t *cells_to_be_activated_list_item_ies; + cells_to_be_activated_list_item_ies = (F1AP_Cells_to_be_Activated_List_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Activated_List_ItemIEs_t)); + cells_to_be_activated_list_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; + cells_to_be_activated_list_item_ies->criticality = F1AP_Criticality_reject; + cells_to_be_activated_list_item_ies->value.present = F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item; - /* - nRCGI */ - F1AP_NRCGI_t nRCGI; - memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); - MCC_MNC_TO_PLMNID(mcc, mnc, mnc_digit_length, - &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(123456, &nRCGI.nRCellIdentity); - cells_to_be_activated_list_item.nRCGI = nRCGI; + // 2.1 cells to be Activated list item + F1AP_Cells_to_be_Activated_List_Item_t cells_to_be_activated_list_item; + memset((void *)&cells_to_be_activated_list_item, 0, sizeof(F1AP_Cells_to_be_Activated_List_Item_t)); - /* optional */ - /* - nRPCI */ - if (0) { - cells_to_be_activated_list_item.nRPCI = (F1AP_NRPCI_t *)calloc(1, sizeof(F1AP_NRPCI_t)); - *cells_to_be_activated_list_item.nRPCI = 321L; // int 0..1007 - } + // - nRCGI + F1AP_NRCGI_t nRCGI; + memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); + MCC_MNC_TO_PLMNID(f1ap_gnb_cu_configuration_update->cells_to_activate[i].mcc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc_digit_length, + &nRCGI.pLMN_Identity); + NR_CELL_ID_TO_BIT_STRING(f1ap_gnb_cu_configuration_update->cells_to_activate[i].nr_cellid, + &nRCGI.nRCellIdentity); + cells_to_be_activated_list_item.nRCGI = nRCGI; - /* optional */ - /* - gNB-CU System Information */ - //if (1) { + if(RC.nrrrc) { - //} - /* ADD */ - cells_to_be_activated_list_item_ies->value.choice.Cells_to_be_Activated_List_Item = cells_to_be_activated_list_item; - ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Activated_List.list, - cells_to_be_activated_list_item_ies); - } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + // optional + // -nRPCI + cells_to_be_activated_list_item.nRPCI = (F1AP_NRPCI_t *)calloc(1, sizeof(F1AP_NRPCI_t)); + *cells_to_be_activated_list_item.nRPCI = f1ap_gnb_cu_configuration_update->cells_to_activate[i].nrpci; // int 0..1007 + + // optional + // 3.1.2 gNB-CUSystem Information + F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *cells_to_be_activated_list_itemExtIEs; + cells_to_be_activated_list_itemExtIEs = (F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Activated_List_ItemExtIEs_t)); + cells_to_be_activated_list_itemExtIEs->id = F1AP_ProtocolIE_ID_id_gNB_CUSystemInformation; + cells_to_be_activated_list_itemExtIEs->criticality = F1AP_Criticality_reject; + cells_to_be_activated_list_itemExtIEs->extensionValue.present = F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_GNB_CUSystemInformation; + + if (f1ap_gnb_cu_configuration_update->cells_to_activate[i].num_SI > 0) { + F1AP_GNB_CUSystemInformation_t *gNB_CUSystemInformation = (F1AP_GNB_CUSystemInformation_t *)calloc(1, sizeof(F1AP_GNB_CUSystemInformation_t)); + //LOG_I(F1AP, "%s() SI %d size %d: ", __func__, i, f1ap_setup_resp->SI_container_length[i][0]); + //for (int n = 0; n < f1ap_setup_resp->SI_container_length[i][0]; n++) + // printf("%02x ", f1ap_setup_resp->SI_container[i][0][n]); + //printf("\n"); + + // for (int sIBtype=2;sIBtype<33;sIBtype++) { //21 ? 33 ? + for (int sIBtype=2;sIBtype<21;sIBtype++) { + if (f1ap_gnb_cu_configuration_update->cells_to_activate[i].SI_container[sIBtype]!=NULL) { + AssertFatal(sIBtype < 6 || sIBtype == 9, "Illegal SI type %d\n",sIBtype); + F1AP_SibtypetobeupdatedListItem_t *sib_item = calloc(1,sizeof(*sib_item)); + memset((void*)sib_item,0,sizeof(*sib_item)); + sib_item->sIBtype = sIBtype; + OCTET_STRING_fromBuf(&sib_item->sIBmessage, + (const char*)f1ap_gnb_cu_configuration_update->cells_to_activate[i].SI_container[sIBtype], + f1ap_gnb_cu_configuration_update->cells_to_activate[i].SI_container_length[sIBtype]); + + LOG_D(F1AP, "f1ap_setup_resp->SI_container_length[%d][%d] = %d \n", i,sIBtype,f1ap_gnb_cu_configuration_update->cells_to_activate[i].SI_container_length[sIBtype]); + ASN_SEQUENCE_ADD(&gNB_CUSystemInformation->sibtypetobeupdatedlist.list,sib_item); + } + } + cells_to_be_activated_list_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation = *gNB_CUSystemInformation; + F1AP_ProtocolExtensionContainer_154P112_t p_154P112_t; + memset((void *)&p_154P112_t, 0, sizeof(F1AP_ProtocolExtensionContainer_154P112_t)); - /* mandatory */ - /* c3. Cells_to_be_Deactivated_List */ - ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Deactivated_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Deactivated_List; + ASN_SEQUENCE_ADD(&p_154P112_t.list, + cells_to_be_activated_list_itemExtIEs); + cells_to_be_activated_list_item.iE_Extensions = (struct F1AP_ProtocolExtensionContainer*)&p_154P112_t; - for (i=0; - i<1; - i++) { + free(gNB_CUSystemInformation); + gNB_CUSystemInformation = NULL; + } + } - F1AP_Cells_to_be_Deactivated_List_ItemIEs_t *cells_to_be_deactivated_list_item_ies; - cells_to_be_deactivated_list_item_ies = (F1AP_Cells_to_be_Deactivated_List_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Deactivated_List_ItemIEs_t)); - cells_to_be_deactivated_list_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; - cells_to_be_deactivated_list_item_ies->criticality = F1AP_Criticality_reject; - cells_to_be_deactivated_list_item_ies->value.present = F1AP_Cells_to_be_Deactivated_List_ItemIEs__value_PR_Cells_to_be_Deactivated_List_Item; + // ADD + cells_to_be_activated_list_item_ies->value.choice.Cells_to_be_Activated_List_Item = cells_to_be_activated_list_item; + ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Activated_List.list, + cells_to_be_activated_list_item_ies); + } + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - /* 3.1 cells to be Deactivated list item */ - F1AP_Cells_to_be_Deactivated_List_Item_t cells_to_be_deactivated_list_item; - memset((void *)&cells_to_be_deactivated_list_item, 0, sizeof(F1AP_Cells_to_be_Deactivated_List_Item_t)); - - /* - nRCGI */ - F1AP_NRCGI_t nRCGI; - memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); - MCC_MNC_TO_PLMNID(mcc, mnc, mnc_digit_length, - &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(123456, &nRCGI.nRCellIdentity); - cells_to_be_deactivated_list_item.nRCGI = nRCGI; - - //} - /* ADD */ - cells_to_be_deactivated_list_item_ies->value.choice.Cells_to_be_Deactivated_List_Item = cells_to_be_deactivated_list_item; - ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Deactivated_List.list, - cells_to_be_deactivated_list_item_ies); - } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - - - /* mandatory */ - /* c4. GNB_CU_TNL_Association_To_Add_List */ - ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Add_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_GNB_CU_TNL_Association_To_Add_List; - - for (i=0; - i<1; - i++) { - - F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs_t *gnb_cu_tnl_association_to_add_item_ies; - gnb_cu_tnl_association_to_add_item_ies = (F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs_t *)calloc(1, sizeof(F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs_t)); - gnb_cu_tnl_association_to_add_item_ies->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Add_Item; - gnb_cu_tnl_association_to_add_item_ies->criticality = F1AP_Criticality_reject; - gnb_cu_tnl_association_to_add_item_ies->value.present = F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs__value_PR_GNB_CU_TNL_Association_To_Add_Item; - - /* 4.1 GNB_CU_TNL_Association_To_Add_Item */ - F1AP_GNB_CU_TNL_Association_To_Add_Item_t gnb_cu_tnl_association_to_add_item; - memset((void *)&gnb_cu_tnl_association_to_add_item, 0, sizeof(F1AP_GNB_CU_TNL_Association_To_Add_Item_t)); - - - /* 4.1.1 tNLAssociationTransportLayerAddress */ - F1AP_CP_TransportLayerAddress_t transportLayerAddress; - memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); - transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address; - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address); - - // memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); - // transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address_and_port; - // transportLayerAddress.choice.endpoint_IP_address_and_port = (F1AP_Endpoint_IP_address_and_port_t *)calloc(1, sizeof(F1AP_Endpoint_IP_address_and_port_t)); - // TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address_and_port.endpoint_IP_address); - - gnb_cu_tnl_association_to_add_item.tNLAssociationTransportLayerAddress = transportLayerAddress; - - /* 4.1.2 tNLAssociationUsage */ - gnb_cu_tnl_association_to_add_item.tNLAssociationUsage = F1AP_TNLAssociationUsage_non_ue; - - - /* ADD */ - gnb_cu_tnl_association_to_add_item_ies->value.choice.GNB_CU_TNL_Association_To_Add_Item = gnb_cu_tnl_association_to_add_item; - ASN_SEQUENCE_ADD(&ie->value.choice.GNB_CU_TNL_Association_To_Add_List.list, - gnb_cu_tnl_association_to_add_item_ies); - } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - - - - /* mandatory */ - /* c5. GNB_CU_TNL_Association_To_Remove_List */ - ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Remove_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_GNB_CU_TNL_Association_To_Remove_List; - for (i=0; - i<1; - i++) { - - F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs_t *gnb_cu_tnl_association_to_remove_item_ies; - gnb_cu_tnl_association_to_remove_item_ies = (F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs_t *)calloc(1, sizeof(F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs_t)); - gnb_cu_tnl_association_to_remove_item_ies->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Remove_Item; - gnb_cu_tnl_association_to_remove_item_ies->criticality = F1AP_Criticality_reject; - gnb_cu_tnl_association_to_remove_item_ies->value.present = F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs__value_PR_GNB_CU_TNL_Association_To_Remove_Item; - - /* 4.1 GNB_CU_TNL_Association_To_Remove_Item */ - F1AP_GNB_CU_TNL_Association_To_Remove_Item_t gnb_cu_tnl_association_to_remove_item; - memset((void *)&gnb_cu_tnl_association_to_remove_item, 0, sizeof(F1AP_GNB_CU_TNL_Association_To_Remove_Item_t)); - - - /* 4.1.1 tNLAssociationTransportLayerAddress */ - F1AP_CP_TransportLayerAddress_t transportLayerAddress; - memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); - transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address; - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address); - - // memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); - // transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address_and_port; - // transportLayerAddress.choice.endpoint_IP_address_and_port = (F1AP_Endpoint_IP_address_and_port_t *)calloc(1, sizeof(F1AP_Endpoint_IP_address_and_port_t)); - // TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address_and_port.endpoint_IP_address); - - gnb_cu_tnl_association_to_remove_item.tNLAssociationTransportLayerAddress = transportLayerAddress; - - - /* ADD */ - gnb_cu_tnl_association_to_remove_item_ies->value.choice.GNB_CU_TNL_Association_To_Remove_Item = gnb_cu_tnl_association_to_remove_item; - ASN_SEQUENCE_ADD(&ie->value.choice.GNB_CU_TNL_Association_To_Remove_List.list, - gnb_cu_tnl_association_to_remove_item_ies); - } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - - /* mandatory */ - /* c6. GNB_CU_TNL_Association_To_Update_List */ - ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Update_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_GNB_CU_TNL_Association_To_Update_List; - for (i=0; - i<1; - i++) { - - F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs_t *gnb_cu_tnl_association_to_update_item_ies; - gnb_cu_tnl_association_to_update_item_ies = (F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs_t *)calloc(1, sizeof(F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs_t)); - gnb_cu_tnl_association_to_update_item_ies->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Update_Item; - gnb_cu_tnl_association_to_update_item_ies->criticality = F1AP_Criticality_reject; - gnb_cu_tnl_association_to_update_item_ies->value.present = F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs__value_PR_GNB_CU_TNL_Association_To_Update_Item; - - /* 4.1 GNB_CU_TNL_Association_To_Update_Item */ - F1AP_GNB_CU_TNL_Association_To_Update_Item_t gnb_cu_tnl_association_to_update_item; - memset((void *)&gnb_cu_tnl_association_to_update_item, 0, sizeof(F1AP_GNB_CU_TNL_Association_To_Update_Item_t)); - - - /* 4.1.1 tNLAssociationTransportLayerAddress */ - F1AP_CP_TransportLayerAddress_t transportLayerAddress; - memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); - transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address; - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address); - - // memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); - // transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address_and_port; - // transportLayerAddress.choice.endpoint_IP_address_and_port = (F1AP_Endpoint_IP_address_and_port_t *)calloc(1, sizeof(F1AP_Endpoint_IP_address_and_port_t)); - // TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address_and_port.endpoint_IP_address); - - gnb_cu_tnl_association_to_update_item.tNLAssociationTransportLayerAddress = transportLayerAddress; - - - /* 4.1.2 tNLAssociationUsage */ - if (1) { - gnb_cu_tnl_association_to_update_item.tNLAssociationUsage = (F1AP_TNLAssociationUsage_t *)calloc(1, sizeof(F1AP_TNLAssociationUsage_t)); - *gnb_cu_tnl_association_to_update_item.tNLAssociationUsage = F1AP_TNLAssociationUsage_non_ue; - } - - /* ADD */ - gnb_cu_tnl_association_to_update_item_ies->value.choice.GNB_CU_TNL_Association_To_Update_Item = gnb_cu_tnl_association_to_update_item; - ASN_SEQUENCE_ADD(&ie->value.choice.GNB_CU_TNL_Association_To_Update_List.list, - gnb_cu_tnl_association_to_update_item_ies); - } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - - - - /* mandatory */ - /* c7. Cells_to_be_Barred_List */ - ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Barred_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Barred_List; - for (i=0; - i<1; - i++) { - - F1AP_Cells_to_be_Barred_ItemIEs_t *cells_to_be_barred_item_ies; - cells_to_be_barred_item_ies = (F1AP_Cells_to_be_Barred_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Barred_ItemIEs_t)); - cells_to_be_barred_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; - cells_to_be_barred_item_ies->criticality = F1AP_Criticality_reject; - cells_to_be_barred_item_ies->value.present = F1AP_Cells_to_be_Barred_ItemIEs__value_PR_Cells_to_be_Barred_Item; - - /* 7.1 cells to be Deactivated list item */ - F1AP_Cells_to_be_Barred_Item_t cells_to_be_barred_item; - memset((void *)&cells_to_be_barred_item, 0, sizeof(F1AP_Cells_to_be_Barred_Item_t)); - - /* - nRCGI */ - F1AP_NRCGI_t nRCGI; - memset(&nRCGI,0,sizeof(F1AP_NRCGI_t)); - MCC_MNC_TO_PLMNID(mcc, mnc, mnc_digit_length, - &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(123456, &nRCGI.nRCellIdentity); - cells_to_be_barred_item.nRCGI = nRCGI; - - /* 7.2 cellBarred*/ - cells_to_be_barred_item.cellBarred = F1AP_CellBarred_not_barred; - - /* ADD */ - cells_to_be_barred_item_ies->value.choice.Cells_to_be_Barred_Item = cells_to_be_barred_item; - ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Barred_List.list, - cells_to_be_barred_item_ies); } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + + if(!RC.nrrrc) { + + /* mandatory */ + /* c3. Cells_to_be_Deactivated_List */ + ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Deactivated_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Deactivated_List; + + for (i=0; + i<1; + i++) { + + F1AP_Cells_to_be_Deactivated_List_ItemIEs_t *cells_to_be_deactivated_list_item_ies; + cells_to_be_deactivated_list_item_ies = (F1AP_Cells_to_be_Deactivated_List_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Deactivated_List_ItemIEs_t)); + cells_to_be_deactivated_list_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; + cells_to_be_deactivated_list_item_ies->criticality = F1AP_Criticality_reject; + cells_to_be_deactivated_list_item_ies->value.present = F1AP_Cells_to_be_Deactivated_List_ItemIEs__value_PR_Cells_to_be_Deactivated_List_Item; + + // 3.1 cells to be Deactivated list item + F1AP_Cells_to_be_Deactivated_List_Item_t cells_to_be_deactivated_list_item; + memset((void *)&cells_to_be_deactivated_list_item, 0, sizeof(F1AP_Cells_to_be_Deactivated_List_Item_t)); + + + F1AP_NRCGI_t nRCGI; + memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); + MCC_MNC_TO_PLMNID(f1ap_gnb_cu_configuration_update->cells_to_activate[i].mcc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc_digit_length, + &nRCGI.pLMN_Identity); + NR_CELL_ID_TO_BIT_STRING(f1ap_gnb_cu_configuration_update->cells_to_activate[i].nr_cellid, &nRCGI.nRCellIdentity); + cells_to_be_deactivated_list_item.nRCGI = nRCGI; + + cells_to_be_deactivated_list_item_ies->value.choice.Cells_to_be_Deactivated_List_Item = cells_to_be_deactivated_list_item; + ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Deactivated_List.list, + cells_to_be_deactivated_list_item_ies); + } + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + + /* mandatory */ + /* c4. GNB_CU_TNL_Association_To_Add_List */ + ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Add_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_GNB_CU_TNL_Association_To_Add_List; + + for (i=0; + i<1; + i++) { + + F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs_t *gnb_cu_tnl_association_to_add_item_ies; + gnb_cu_tnl_association_to_add_item_ies = (F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs_t *)calloc(1, sizeof(F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs_t)); + gnb_cu_tnl_association_to_add_item_ies->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Add_Item; + gnb_cu_tnl_association_to_add_item_ies->criticality = F1AP_Criticality_reject; + gnb_cu_tnl_association_to_add_item_ies->value.present = F1AP_GNB_CU_TNL_Association_To_Add_ItemIEs__value_PR_GNB_CU_TNL_Association_To_Add_Item; + + // 4.1 GNB_CU_TNL_Association_To_Add_Item + F1AP_GNB_CU_TNL_Association_To_Add_Item_t gnb_cu_tnl_association_to_add_item; + memset((void *)&gnb_cu_tnl_association_to_add_item, 0, sizeof(F1AP_GNB_CU_TNL_Association_To_Add_Item_t)); + + + // 4.1.1 tNLAssociationTransportLayerAddress + F1AP_CP_TransportLayerAddress_t transportLayerAddress; + memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); + transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address; + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address); + + // memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); + // transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address_and_port; + // transportLayerAddress.choice.endpoint_IP_address_and_port = (F1AP_Endpoint_IP_address_and_port_t *)calloc(1, sizeof(F1AP_Endpoint_IP_address_and_port_t)); + // TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address_and_port.endpoint_IP_address); + + gnb_cu_tnl_association_to_add_item.tNLAssociationTransportLayerAddress = transportLayerAddress; + + // 4.1.2 tNLAssociationUsage + gnb_cu_tnl_association_to_add_item.tNLAssociationUsage = F1AP_TNLAssociationUsage_non_ue; - /* mandatory */ - /* c8. Protected_EUTRA_Resources_List */ - ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); - ie->id = F1AP_ProtocolIE_ID_id_Protected_EUTRA_Resources_List; - ie->criticality = F1AP_Criticality_reject; - ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Protected_EUTRA_Resources_List; - - for (i=0; - i<1; - i++) { + gnb_cu_tnl_association_to_add_item_ies->value.choice.GNB_CU_TNL_Association_To_Add_Item = gnb_cu_tnl_association_to_add_item; + ASN_SEQUENCE_ADD(&ie->value.choice.GNB_CU_TNL_Association_To_Add_List.list, + gnb_cu_tnl_association_to_add_item_ies); + } + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - F1AP_Protected_EUTRA_Resources_ItemIEs_t *protected_eutra_resources_item_ies; + /* mandatory */ + /* c5. GNB_CU_TNL_Association_To_Remove_List */ + ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Remove_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_GNB_CU_TNL_Association_To_Remove_List; + for (i=0; + i<1; + i++) { - /* 8.1 SpectrumSharingGroupID */ - protected_eutra_resources_item_ies = (F1AP_Protected_EUTRA_Resources_ItemIEs_t *)calloc(1, sizeof(F1AP_Protected_EUTRA_Resources_ItemIEs_t)); - protected_eutra_resources_item_ies->id = F1AP_ProtocolIE_ID_id_Protected_EUTRA_Resources_List; - protected_eutra_resources_item_ies->criticality = F1AP_Criticality_reject; - protected_eutra_resources_item_ies->value.present = F1AP_Protected_EUTRA_Resources_ItemIEs__value_PR_Protected_EUTRA_Resources_Item; - ((F1AP_Protected_EUTRA_Resources_Item_t*)&protected_eutra_resources_item_ies->value.choice.Protected_EUTRA_Resources_Item)->spectrumSharingGroupID = 123L; - memset(&protected_eutra_resources_item_ies->value.choice.Protected_EUTRA_Resources_Item,0, - sizeof(F1AP_Protected_EUTRA_Resources_Item_t)); - ASN_SEQUENCE_ADD(&ie->value.choice.Protected_EUTRA_Resources_List.list, protected_eutra_resources_item_ies); + F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs_t *gnb_cu_tnl_association_to_remove_item_ies; + gnb_cu_tnl_association_to_remove_item_ies = (F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs_t *)calloc(1, sizeof(F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs_t)); + gnb_cu_tnl_association_to_remove_item_ies->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Remove_Item; + gnb_cu_tnl_association_to_remove_item_ies->criticality = F1AP_Criticality_reject; + gnb_cu_tnl_association_to_remove_item_ies->value.present = F1AP_GNB_CU_TNL_Association_To_Remove_ItemIEs__value_PR_GNB_CU_TNL_Association_To_Remove_Item; -/* + // 4.1 GNB_CU_TNL_Association_To_Remove_Item + F1AP_GNB_CU_TNL_Association_To_Remove_Item_t gnb_cu_tnl_association_to_remove_item; + memset((void *)&gnb_cu_tnl_association_to_remove_item, 0, sizeof(F1AP_GNB_CU_TNL_Association_To_Remove_Item_t)); + + + // 4.1.1 tNLAssociationTransportLayerAddress + F1AP_CP_TransportLayerAddress_t transportLayerAddress; + memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); + transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address; + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address); + + // memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); + // transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address_and_port; + // transportLayerAddress.choice.endpoint_IP_address_and_port = (F1AP_Endpoint_IP_address_and_port_t *)calloc(1, sizeof(F1AP_Endpoint_IP_address_and_port_t)); + // TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address_and_port.endpoint_IP_address); + + gnb_cu_tnl_association_to_remove_item.tNLAssociationTransportLayerAddress = transportLayerAddress; + + + + gnb_cu_tnl_association_to_remove_item_ies->value.choice.GNB_CU_TNL_Association_To_Remove_Item = gnb_cu_tnl_association_to_remove_item; + ASN_SEQUENCE_ADD(&ie->value.choice.GNB_CU_TNL_Association_To_Remove_List.list, + gnb_cu_tnl_association_to_remove_item_ies); + } + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + + /* mandatory */ + /* c6. GNB_CU_TNL_Association_To_Update_List */ + ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Update_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_GNB_CU_TNL_Association_To_Update_List; + for (i=0; + i<1; + i++) { + + F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs_t *gnb_cu_tnl_association_to_update_item_ies; + gnb_cu_tnl_association_to_update_item_ies = (F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs_t *)calloc(1, sizeof(F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs_t)); + gnb_cu_tnl_association_to_update_item_ies->id = F1AP_ProtocolIE_ID_id_GNB_CU_TNL_Association_To_Update_Item; + gnb_cu_tnl_association_to_update_item_ies->criticality = F1AP_Criticality_reject; + gnb_cu_tnl_association_to_update_item_ies->value.present = F1AP_GNB_CU_TNL_Association_To_Update_ItemIEs__value_PR_GNB_CU_TNL_Association_To_Update_Item; + + // 4.1 GNB_CU_TNL_Association_To_Update_Item + F1AP_GNB_CU_TNL_Association_To_Update_Item_t gnb_cu_tnl_association_to_update_item; + memset((void *)&gnb_cu_tnl_association_to_update_item, 0, sizeof(F1AP_GNB_CU_TNL_Association_To_Update_Item_t)); + + + // 4.1.1 tNLAssociationTransportLayerAddress + F1AP_CP_TransportLayerAddress_t transportLayerAddress; + memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); + transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address; + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address); + + // memset((void *)&transportLayerAddress, 0, sizeof(F1AP_CP_TransportLayerAddress_t)); + // transportLayerAddress.present = F1AP_CP_TransportLayerAddress_PR_endpoint_IP_address_and_port; + // transportLayerAddress.choice.endpoint_IP_address_and_port = (F1AP_Endpoint_IP_address_and_port_t *)calloc(1, sizeof(F1AP_Endpoint_IP_address_and_port_t)); + // TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &transportLayerAddress.choice.endpoint_IP_address_and_port.endpoint_IP_address); + + gnb_cu_tnl_association_to_update_item.tNLAssociationTransportLayerAddress = transportLayerAddress; + + + // 4.1.2 tNLAssociationUsage + if (1) { + gnb_cu_tnl_association_to_update_item.tNLAssociationUsage = (F1AP_TNLAssociationUsage_t *)calloc(1, sizeof(F1AP_TNLAssociationUsage_t)); + *gnb_cu_tnl_association_to_update_item.tNLAssociationUsage = F1AP_TNLAssociationUsage_non_ue; + } + + + gnb_cu_tnl_association_to_update_item_ies->value.choice.GNB_CU_TNL_Association_To_Update_Item = gnb_cu_tnl_association_to_update_item; + ASN_SEQUENCE_ADD(&ie->value.choice.GNB_CU_TNL_Association_To_Update_List.list, + gnb_cu_tnl_association_to_update_item_ies); + } + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + + /* mandatory */ + /* c7. Cells_to_be_Barred_List */ + ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Barred_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Barred_List; + for (i=0; + i<1; + i++) { + + F1AP_Cells_to_be_Barred_ItemIEs_t *cells_to_be_barred_item_ies; + cells_to_be_barred_item_ies = (F1AP_Cells_to_be_Barred_ItemIEs_t *)calloc(1, sizeof(F1AP_Cells_to_be_Barred_ItemIEs_t)); + cells_to_be_barred_item_ies->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item; + cells_to_be_barred_item_ies->criticality = F1AP_Criticality_reject; + cells_to_be_barred_item_ies->value.present = F1AP_Cells_to_be_Barred_ItemIEs__value_PR_Cells_to_be_Barred_Item; + + // 7.1 cells to be Deactivated list item + F1AP_Cells_to_be_Barred_Item_t cells_to_be_barred_item; + memset((void *)&cells_to_be_barred_item, 0, sizeof(F1AP_Cells_to_be_Barred_Item_t)); + + // - nRCGI + F1AP_NRCGI_t nRCGI; + memset(&nRCGI,0,sizeof(F1AP_NRCGI_t)); + MCC_MNC_TO_PLMNID(f1ap_gnb_cu_configuration_update->cells_to_activate[i].mcc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc, + f1ap_gnb_cu_configuration_update->cells_to_activate[i].mnc_digit_length, + &nRCGI.pLMN_Identity); + NR_CELL_ID_TO_BIT_STRING(f1ap_gnb_cu_configuration_update->cells_to_activate[i].nr_cellid, &nRCGI.nRCellIdentity); + cells_to_be_barred_item.nRCGI = nRCGI; + + // 7.2 cellBarred + cells_to_be_barred_item.cellBarred = F1AP_CellBarred_not_barred; + + cells_to_be_barred_item_ies->value.choice.Cells_to_be_Barred_Item = cells_to_be_barred_item; + ASN_SEQUENCE_ADD(&ie->value.choice.Cells_to_be_Barred_List.list, + cells_to_be_barred_item_ies); + } + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + + /* mandatory */ + /* c8. Protected_EUTRA_Resources_List */ + ie = (F1AP_GNBCUConfigurationUpdateIEs_t *)calloc(1, sizeof(F1AP_GNBCUConfigurationUpdateIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_Protected_EUTRA_Resources_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Protected_EUTRA_Resources_List; + + for (i=0; + i<1; + i++) { + + + F1AP_Protected_EUTRA_Resources_ItemIEs_t *protected_eutra_resources_item_ies; + + // 8.1 SpectrumSharingGroupID + protected_eutra_resources_item_ies = (F1AP_Protected_EUTRA_Resources_ItemIEs_t *)calloc(1, sizeof(F1AP_Protected_EUTRA_Resources_ItemIEs_t)); + protected_eutra_resources_item_ies->id = F1AP_ProtocolIE_ID_id_Protected_EUTRA_Resources_List; + protected_eutra_resources_item_ies->criticality = F1AP_Criticality_reject; + protected_eutra_resources_item_ies->value.present = F1AP_Protected_EUTRA_Resources_ItemIEs__value_PR_Protected_EUTRA_Resources_Item; + ((F1AP_Protected_EUTRA_Resources_Item_t*)&protected_eutra_resources_item_ies->value.choice.Protected_EUTRA_Resources_Item)->spectrumSharingGroupID = 123L; + memset(&protected_eutra_resources_item_ies->value.choice.Protected_EUTRA_Resources_Item,0, + sizeof(F1AP_Protected_EUTRA_Resources_Item_t)); + ASN_SEQUENCE_ADD(&ie->value.choice.Protected_EUTRA_Resources_List.list, protected_eutra_resources_item_ies); + + /* F1AP_Served_EUTRA_Cells_Information_t served_eutra_cells_information; memset((void *)&served_eutra_cells_information, 0, sizeof(F1AP_Served_EUTRA_Cells_Information_t)); @@ -879,17 +941,22 @@ int CU_send_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, module_id_t du_mod_ ASN_SEQUENCE_ADD(&protected_eutra_resources_item_ies->value.choice.ListofEUTRACellsinGNBDUCoordination.list, &served_eutra_cells_information); ASN_SEQUENCE_ADD(&ie->value.choice.Protected_EUTRA_Resources_List.list, protected_eutra_resources_item_ies); -*/ + */ + } + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); } - ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 gNB-CU CONFIGURATION UPDATE\n"); return -1; } + LOG_D(F1AP, "F1AP gNB-CU CONFIGURATION UPDATE\n"); + //for (int i=0;iassoc_id, buffer, len, 0); return 0; } @@ -905,7 +972,8 @@ int CU_handle_gNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, uint32_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu) { - AssertFatal(1==0,"Not implemented yet\n"); + LOG_D(F1AP,"Cell Configuration ok (assoc_id %d)\n",assoc_id); + return(0); } diff --git a/openair2/F1AP/f1ap_cu_interface_management.h b/openair2/F1AP/f1ap_cu_interface_management.h index acad13836d..de7dcfe99b 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.h +++ b/openair2/F1AP/f1ap_cu_interface_management.h @@ -85,8 +85,7 @@ int CU_send_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, /* * gNB-CU Configuration Update */ -int CU_send_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, module_id_t du_mod_idP); - +int CU_send_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, f1ap_gnb_cu_configuration_update_t *f1ap_gnb_cu_configuration_update); int CU_handle_gNB_CU_CONFIGURATION_UPDATE_FAILURE(instance_t instance, uint32_t assoc_id, uint32_t stream, diff --git a/openair2/F1AP/f1ap_cu_rrc_message_transfer.c b/openair2/F1AP/f1ap_cu_rrc_message_transfer.c index f4c09c4725..03cfcc7329 100644 --- a/openair2/F1AP/f1ap_cu_rrc_message_transfer.c +++ b/openair2/F1AP/f1ap_cu_rrc_message_transfer.c @@ -114,28 +114,62 @@ int CU_handle_INITIAL_UL_RRC_MESSAGE_TRANSFER(instance_t instance, /* RRC Container */ F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_RRCContainer, true); - + AssertFatal(ie!=NULL,"RRCContainer is missing\n"); // create an ITTI message and copy SDU - message_p = itti_alloc_new_message (TASK_CU_F1, 0, RRC_MAC_CCCH_DATA_IND); - memset (RRC_MAC_CCCH_DATA_IND (message_p).sdu, 0, CCCH_SDU_SIZE); - ccch_sdu_len = ie->value.choice.RRCContainer.size; - memcpy(RRC_MAC_CCCH_DATA_IND (message_p).sdu, ie->value.choice.RRCContainer.buf, - ccch_sdu_len); + if (RC.nrrrc && RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type == ngran_gNB_CU) { + message_p = itti_alloc_new_message (TASK_CU_F1, 0, NR_RRC_MAC_CCCH_DATA_IND); + memset (NR_RRC_MAC_CCCH_DATA_IND (message_p).sdu, 0, CCCH_SDU_SIZE); + ccch_sdu_len = ie->value.choice.RRCContainer.size; + memcpy(NR_RRC_MAC_CCCH_DATA_IND (message_p).sdu, ie->value.choice.RRCContainer.buf, + ccch_sdu_len); + } else { + message_p = itti_alloc_new_message (TASK_CU_F1, 0, RRC_MAC_CCCH_DATA_IND); + memset (RRC_MAC_CCCH_DATA_IND (message_p).sdu, 0, CCCH_SDU_SIZE); + ccch_sdu_len = ie->value.choice.RRCContainer.size; + memcpy(RRC_MAC_CCCH_DATA_IND (message_p).sdu, ie->value.choice.RRCContainer.buf, + ccch_sdu_len); + } - //LOG_I(F1AP, "%s() RRCContainer (CCCH) size %ld: ", __func__, - // ie->value.choice.RRCContainer.size); - //for (int i = 0; i < ie->value.choice.RRCContainer.size; i++) - // printf("%02x ", RRC_MAC_CCCH_DATA_IND (message_p).sdu[i]); - //printf("\n"); + if (RC.nrrrc && RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type == ngran_gNB_CU) { + + LOG_D(F1AP, "%s() RRCContainer (CCCH) size %ld: ", __func__, ie->value.choice.RRCContainer.size); + //for (int i = 0; i < ie->value.choice.RRCContainer.size; i++) + // printf("%02x ", RRC_MAC_CCCH_DATA_IND (message_p).sdu[i]); + //printf("\n"); + + /* DUtoCURRCContainer */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_DUtoCURRCContainer, true); + if (ie) { + NR_RRC_MAC_CCCH_DATA_IND (message_p).du_to_cu_rrc_container = malloc(sizeof(OCTET_STRING_t)); + NR_RRC_MAC_CCCH_DATA_IND (message_p).du_to_cu_rrc_container->size = ie->value.choice.DUtoCURRCContainer.size; + NR_RRC_MAC_CCCH_DATA_IND (message_p).du_to_cu_rrc_container->buf = malloc( + ie->value.choice.DUtoCURRCContainer.size); + memcpy(NR_RRC_MAC_CCCH_DATA_IND (message_p).du_to_cu_rrc_container->buf, + ie->value.choice.DUtoCURRCContainer.buf, + ie->value.choice.DUtoCURRCContainer.size); + } + } // Find instance from nr_cellid int rrc_inst = -1; - for (int i=0;inode_type == ngran_gNB_CU) { + for (int i=0;inr_cellid == nr_cellid) { + rrc_inst = i; + break; + } + } + } else { + for (int i=0;inr_cellid == nr_cellid) { - rrc_inst = i; - break; + eNB_RRC_INST *rrc = RC.rrc[i]; + if (rrc->nr_cellid == nr_cellid) { + rrc_inst = i; + break; + } } } AssertFatal(rrc_inst>=0,"couldn't find an RRC instance for nr_cell %llu\n",(unsigned long long int)nr_cellid); @@ -148,15 +182,23 @@ int CU_handle_INITIAL_UL_RRC_MESSAGE_TRANSFER(instance_t instance, } f1ap_cu_inst[rrc_inst].f1ap_ue[f1ap_uid].du_ue_f1ap_id = du_ue_f1ap_id; - - RRC_MAC_CCCH_DATA_IND (message_p).frame = 0; - RRC_MAC_CCCH_DATA_IND (message_p).sub_frame = 0; - RRC_MAC_CCCH_DATA_IND (message_p).sdu_size = ccch_sdu_len; - RRC_MAC_CCCH_DATA_IND (message_p).enb_index = rrc_inst; // CU instance - RRC_MAC_CCCH_DATA_IND (message_p).rnti = rnti; - RRC_MAC_CCCH_DATA_IND (message_p).CC_id = CC_id; - itti_send_msg_to_task (TASK_RRC_ENB, instance, message_p); - + if (RC.nrrrc && RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type == ngran_gNB_CU) { + NR_RRC_MAC_CCCH_DATA_IND (message_p).frame = 0; + NR_RRC_MAC_CCCH_DATA_IND (message_p).sub_frame = 0; + NR_RRC_MAC_CCCH_DATA_IND (message_p).sdu_size = ccch_sdu_len; + NR_RRC_MAC_CCCH_DATA_IND (message_p).gnb_index = rrc_inst; // CU instance + NR_RRC_MAC_CCCH_DATA_IND (message_p).rnti = rnti; + NR_RRC_MAC_CCCH_DATA_IND (message_p).CC_id = CC_id; + itti_send_msg_to_task (TASK_RRC_GNB, instance, message_p); + } else { + RRC_MAC_CCCH_DATA_IND (message_p).frame = 0; + RRC_MAC_CCCH_DATA_IND (message_p).sub_frame = 0; + RRC_MAC_CCCH_DATA_IND (message_p).sdu_size = ccch_sdu_len; + RRC_MAC_CCCH_DATA_IND (message_p).enb_index = rrc_inst; // CU instance + RRC_MAC_CCCH_DATA_IND (message_p).rnti = rnti; + RRC_MAC_CCCH_DATA_IND (message_p).CC_id = CC_id; + itti_send_msg_to_task (TASK_RRC_ENB, instance, message_p); + } return 0; } @@ -171,7 +213,8 @@ int CU_send_DL_RRC_MESSAGE_TRANSFER(instance_t instance, f1ap_dl_rrc_message_t *f1ap_dl_rrc) { - F1AP_F1AP_PDU_t pdu; + LOG_D(F1AP, "CU send DL_RRC_MESSAGE_TRANSFER \n"); + F1AP_F1AP_PDU_t pdu; F1AP_DLRRCMessageTransfer_t *out; F1AP_DLRRCMessageTransferIEs_t *ie; @@ -252,10 +295,12 @@ int CU_send_DL_RRC_MESSAGE_TRANSFER(instance_t instance, OCTET_STRING_fromBuf(&ie->value.choice.RRCContainer, (const char*)f1ap_dl_rrc->rrc_container, f1ap_dl_rrc->rrc_container_length); ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - //LOG_I(F1AP, "%s() RRCContainer size %d: ", __func__, f1ap_dl_rrc->rrc_container_length); - //for (int i = 0; i < ie->value.choice.RRCContainer.size; i++) - // printf("%02x ", f1ap_dl_rrc->rrc_container[i]); - //printf("\n"); + if (RC.nrrrc && RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type == ngran_gNB_CU) { + LOG_I(F1AP, "%s() RRCContainer size %d: ", __func__, f1ap_dl_rrc->rrc_container_length); + for (int i = 0; i < ie->value.choice.RRCContainer.size; i++) + printf("%02x ", f1ap_dl_rrc->rrc_container[i]); + printf("\n"); + } /* optional */ /* c7. RAT_FrequencyPriorityInformation */ @@ -279,7 +324,7 @@ int CU_send_DL_RRC_MESSAGE_TRANSFER(instance_t instance, /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 DL RRC MESSAGE TRANSFER \n"); return -1; } @@ -302,7 +347,6 @@ int CU_handle_UL_RRC_MESSAGE_TRANSFER(instance_t instance, F1AP_ULRRCMessageTransfer_t *container; F1AP_ULRRCMessageTransferIEs_t *ie; - uint64_t cu_ue_f1ap_id; uint64_t du_ue_f1ap_id; uint64_t srb_id; @@ -351,6 +395,7 @@ int CU_handle_UL_RRC_MESSAGE_TRANSFER(instance_t instance, // print message in debug mode // create an ITTI message and copy SDU + /* message_p = itti_alloc_new_message (TASK_CU_F1, 0, RRC_DCCH_DATA_IND); @@ -373,6 +418,8 @@ int CU_handle_UL_RRC_MESSAGE_TRANSFER(instance_t instance, ctxt.instance = instance; ctxt.rnti = f1ap_get_rnti_by_cu_id(&f1ap_cu_inst[instance], cu_ue_f1ap_id); ctxt.enb_flag = 1; + ctxt.eNB_index = 0; + ctxt.configured = 1; mem_block_t *mb = get_free_mem_block(ie->value.choice.RRCContainer.size,__func__); memcpy((void*)mb->data,(void*)ie->value.choice.RRCContainer.buf,ie->value.choice.RRCContainer.size); LOG_I(F1AP, "Calling pdcp_data_ind for UE RNTI %x srb_id %lu with size %ld (DCCH) \n", ctxt.rnti, srb_id, ie->value.choice.RRCContainer.size); diff --git a/openair2/F1AP/f1ap_cu_task.c b/openair2/F1AP/f1ap_cu_task.c index d2f4c3a56b..b261d8e85b 100644 --- a/openair2/F1AP/f1ap_cu_task.c +++ b/openair2/F1AP/f1ap_cu_task.c @@ -39,6 +39,7 @@ #include "proto_agent.h" extern RAN_CONTEXT_t RC; +extern uint8_t proto_agent_flag; f1ap_setup_req_t *f1ap_du_data_from_du; f1ap_cudu_inst_t f1ap_cu_inst[MAX_eNB]; @@ -71,7 +72,12 @@ void cu_task_handle_sctp_association_resp(instance_t instance, sctp_new_associat f1ap_du_data_from_du->sctp_out_streams = sctp_new_association_resp->out_streams; /* setup parameters for F1U and start the server */ - const cudu_params_t params = { + const cudu_params_t params = (RC.nrrrc && RC.nrrrc[instance]->node_type == ngran_gNB_CU) ? (cudu_params_t){ + .local_ipv4_address = RC.nrrrc[instance]->eth_params_s.my_addr, + .local_port = RC.nrrrc[instance]->eth_params_s.my_portd, + .remote_ipv4_address = RC.nrrrc[instance]->eth_params_s.remote_addr, + .remote_port = RC.nrrrc[instance]->eth_params_s.remote_portd + } : (cudu_params_t){ .local_ipv4_address = RC.rrc[instance]->eth_params_s.my_addr, .local_port = RC.rrc[instance]->eth_params_s.my_portd, .remote_ipv4_address = RC.rrc[instance]->eth_params_s.remote_addr, @@ -79,6 +85,7 @@ void cu_task_handle_sctp_association_resp(instance_t instance, sctp_new_associat }; AssertFatal(proto_agent_start(instance, ¶ms) == 0, "could not start PROTO_AGENT for F1U on instance %ld!\n", instance); + proto_agent_flag = 1; } void cu_task_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind) { @@ -107,7 +114,11 @@ void cu_task_send_sctp_init_req(instance_t enb_id) { message_p->ittiMsg.sctp_init.ipv4 = 1; message_p->ittiMsg.sctp_init.ipv6 = 0; message_p->ittiMsg.sctp_init.nb_ipv4_addr = 1; - message_p->ittiMsg.sctp_init.ipv4_address[0] = inet_addr(RC.rrc[enb_id]->eth_params_s.my_addr); + if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_CU) { + message_p->ittiMsg.sctp_init.ipv4_address[0] = inet_addr(RC.nrrrc[enb_id]->eth_params_s.my_addr); + } else{ + message_p->ittiMsg.sctp_init.ipv4_address[0] = inet_addr(RC.rrc[enb_id]->eth_params_s.my_addr); + } /* * SR WARNING: ipv6 multi-homing fails sometimes for localhost. * * * * Disable it for now. @@ -166,13 +177,27 @@ void *F1AP_CU_task(void *arg) { &F1AP_SETUP_RESP(received_msg)); break; - case F1AP_DL_RRC_MESSAGE: // from rrc + case F1AP_GNB_CU_CONFIGURATION_UPDATE: // from rrc + LOG_I(F1AP, "CU Task Received F1AP_GNB_CU_CONFIGURAITON_UPDATE\n"); + // CU_send_f1setup_resp(ITTI_MSG_DESTINATION_INSTANCE(received_msg), + // &F1AP_SETUP_RESP(received_msg)); + CU_send_gNB_CU_CONFIGURATION_UPDATE(ITTI_MSG_DESTINATION_INSTANCE(received_msg), + &F1AP_GNB_CU_CONFIGURATION_UPDATE(received_msg)); + break; + + case F1AP_DL_RRC_MESSAGE: // from rrc LOG_I(F1AP, "CU Task Received F1AP_DL_RRC_MESSAGE\n"); CU_send_DL_RRC_MESSAGE_TRANSFER(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_DL_RRC_MESSAGE(received_msg)); break; - case F1AP_UE_CONTEXT_RELEASE_CMD: // from rrc + case F1AP_UE_CONTEXT_SETUP_REQ: // from rrc + LOG_I(F1AP, "CU Task Received F1AP_UE_CONTEXT_SETUP_REQ\n"); + CU_send_UE_CONTEXT_SETUP_REQUEST(ITTI_MSG_DESTINATION_INSTANCE(received_msg), + &F1AP_UE_CONTEXT_SETUP_REQ(received_msg)); + break; + + case F1AP_UE_CONTEXT_RELEASE_CMD: // from rrc LOG_I(F1AP, "CU Task Received F1AP_UE_CONTEXT_RELEASE_CMD\n"); CU_send_UE_CONTEXT_RELEASE_COMMAND(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_UE_CONTEXT_RELEASE_CMD(received_msg)); diff --git a/openair2/F1AP/f1ap_cu_ue_context_management.c b/openair2/F1AP/f1ap_cu_ue_context_management.c index 1857678ce9..317f0058b2 100644 --- a/openair2/F1AP/f1ap_cu_ue_context_management.c +++ b/openair2/F1AP/f1ap_cu_ue_context_management.c @@ -45,6 +45,7 @@ extern f1ap_setup_req_t *f1ap_du_data_from_du; extern f1ap_cudu_inst_t f1ap_cu_inst[MAX_eNB]; extern RAN_CONTEXT_t RC; +extern uint32_t f1ap_assoc_id; int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, f1ap_ue_context_setup_req_t *f1ap_ue_context_setup_req) { @@ -263,7 +264,7 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, f1ap_ue_context_setup_req->mnc, f1ap_ue_context_setup_req->mnc_digit_length, &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(123456, &nRCGI.nRCellIdentity); + NR_CELL_ID_TO_BIT_STRING(12345678, &nRCGI.nRCellIdentity); scell_toBeSetup_item.sCell_ID = nRCGI; /* 10.1.2 sCellIndex */ @@ -475,7 +476,7 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, /* OPTIONAL */ /* gBR_QoS_Flow_Information */ if (0) { - DRB_Information->dRB_QoS.gBR_QoS_Flow_Information = (F1AP_GBR_QoSFlowInformation_t *)calloc(1, sizeof(F1AP_GBR_QoSFlowInformation_t)); + DRB_Information->dRB_QoS.gBR_QoS_Flow_Information = (F1AP_GBR_QoSFlowInformation_t *)calloc(1, sizeof(F1AP_GBR_QoSFlowInformation_t)); asn_long2INTEGER(&DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxFlowBitRateDownlink, 1L); asn_long2INTEGER(&DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxFlowBitRateUplink, 1L); asn_long2INTEGER(&DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->guaranteedFlowBitRateDownlink, 1L); @@ -484,14 +485,14 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, /* OPTIONAL */ /* maxPacketLossRateDownlink */ if (0) { - DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxPacketLossRateDownlink = (F1AP_MaxPacketLossRate_t *)calloc(1, sizeof(F1AP_MaxPacketLossRate_t)); + DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxPacketLossRateDownlink = (F1AP_MaxPacketLossRate_t *)calloc(1, sizeof(F1AP_MaxPacketLossRate_t)); *DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxPacketLossRateDownlink = 1L; } /* OPTIONAL */ /* maxPacketLossRateUplink */ if (0) { - DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxPacketLossRateUplink = (F1AP_MaxPacketLossRate_t *)calloc(1, sizeof(F1AP_MaxPacketLossRate_t)); + DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxPacketLossRateUplink = (F1AP_MaxPacketLossRate_t *)calloc(1, sizeof(F1AP_MaxPacketLossRate_t)); *DRB_Information->dRB_QoS.gBR_QoS_Flow_Information->maxPacketLossRateUplink = 1L; } @@ -500,7 +501,7 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, /* OPTIONAL */ /* reflective_QoS_Attribute */ if (0) { - DRB_Information->dRB_QoS.reflective_QoS_Attribute = (long *)calloc(1, sizeof(long)); + DRB_Information->dRB_QoS.reflective_QoS_Attribute = (long *)calloc(1, sizeof(long)); *DRB_Information->dRB_QoS.reflective_QoS_Attribute = 1L; } @@ -581,8 +582,9 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, /* packetErrorRate */ flows_mapped_to_drb_item.qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI->packetErrorRate.pER_Scalar = 1L; - flows_mapped_to_drb_item.qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI->packetErrorRate.pER_Exponent = 6L; - /* OPTIONAL */ + flows_mapped_to_drb_item.qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI->packetErrorRate.pER_Exponent = 6L; + + /* OPTIONAL */ /* delayCritical */ if (0) { flows_mapped_to_drb_item.qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI->delayCritical = (long *)calloc(1, sizeof(long)); @@ -727,7 +729,7 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, ie->criticality = F1AP_Criticality_reject; ie->value.present = F1AP_UEContextSetupRequestIEs__value_PR_RAT_FrequencyPriorityInformation; - int endc = 1; // RK: Get this from somewhere ... + int endc = 1; // RK: Get this from somewhere ... if (endc) { ie->value.choice.RAT_FrequencyPriorityInformation.present = F1AP_RAT_FrequencyPriorityInformation_PR_eNDC; ie->value.choice.RAT_FrequencyPriorityInformation.choice.eNDC = 11L; @@ -740,13 +742,13 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, /* OPTIONAL */ /* RRCContainer */ - if (0) { + if(RC.nrrrc) { ie = (F1AP_UEContextSetupRequestIEs_t *)calloc(1, sizeof(F1AP_UEContextSetupRequestIEs_t)); ie->id = F1AP_ProtocolIE_ID_id_RRCContainer; ie->criticality = F1AP_Criticality_reject; ie->value.present = F1AP_UEContextSetupRequestIEs__value_PR_RRCContainer; - OCTET_STRING_fromBuf(&ie->value.choice.RRCContainer, "asdsa1d32sa1d31asd31as", - strlen("asdsa1d32sa1d31asd31as")); + OCTET_STRING_fromBuf(&ie->value.choice.RRCContainer, (const char*)f1ap_ue_context_setup_req->rrc_container, + f1ap_ue_context_setup_req->rrc_container_length); ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); } @@ -763,10 +765,28 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 UE CONTEXT SETUP REQUEST\n"); return -1; } + // xer_fprint(stdout, &asn_DEF_F1AP_F1AP_PDU, (void *)pdu); + + // asn_encode_to_new_buffer_result_t res = { NULL, {0, NULL, NULL} }; + // res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu); + // buffer = res.buffer; + // len = res.result.encoded; + + // if (res.result.encoded <= 0) { + // LOG_E(F1AP, "ASN1 message encoding failed (%s, %lu)!\n", res.result.failed_type->name, res.result.encoded); + // return -1; + // } + + LOG_D(F1AP,"F1AP UEContextSetupRequest Encoded %u bits\n", len); + + if(RC.nrrrc) { + cu_f1ap_itti_send_sctp_data_req(instance, f1ap_assoc_id /* BK: fix me*/ , buffer, len, 0 /* BK: fix me*/); + } + return 0; } @@ -774,7 +794,42 @@ int CU_handle_UE_CONTEXT_SETUP_RESPONSE(instance_t instance, uint32_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu) { - AssertFatal(1==0,"Not implemented yet\n"); + F1AP_UEContextSetupResponse_t *container; + F1AP_UEContextSetupResponseIEs_t *ie; + + DevAssert(pdu); + + container = &pdu->choice.successfulOutcome->value.choice.UEContextSetupResponse; + + /* GNB_CU_UE_F1AP_ID */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); + + /* GNB_DU_UE_F1AP_ID */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true); + + /* DUtoCURRCInformation */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_DUtoCURRCInformation, true); + + /* DRBs_Setup_List */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_DRBs_Setup_List, true); + + /* SRBs_FailedToBeSetup_List */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_SRBs_FailedToBeSetup_List, true); + + /* DRBs_FailedToBeSetup_List */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_DRBs_FailedToBeSetup_List, true); + + /* SCell_FailedtoSetup_List */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_SCell_FailedtoSetup_List, true); + + return 0; } int CU_handle_UE_CONTEXT_SETUP_FAILURE(instance_t instance, @@ -1030,8 +1085,8 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance) { // for test int mcc = 208; - int mnc = 93; - int mnc_digit_length = 8; + int mnc = 92; + int mnc_digit_length = 2; /* Create */ /* 0. Message Type */ @@ -1072,7 +1127,7 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance) { F1AP_NRCGI_t nRCGI; MCC_MNC_TO_PLMNID(mcc, mnc, mnc_digit_length, &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(123456, &nRCGI.nRCellIdentity); + NR_CELL_ID_TO_BIT_STRING(12345678, &nRCGI.nRCellIdentity); ie->value.choice.NRCGI = nRCGI; ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); @@ -1197,7 +1252,7 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance) { memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); MCC_MNC_TO_PLMNID(mcc, mnc, mnc_digit_length, &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(123456, &nRCGI.nRCellIdentity); + NR_CELL_ID_TO_BIT_STRING(12345678, &nRCGI.nRCellIdentity); scell_toBeSetupMod_item.sCell_ID = nRCGI; /* sCellIndex */ @@ -1237,7 +1292,7 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance) { memset(&nRCGI, 0, sizeof(F1AP_NRCGI_t)); MCC_MNC_TO_PLMNID(mcc, mnc, mnc_digit_length, &nRCGI.pLMN_Identity); - NR_CELL_ID_TO_BIT_STRING(123456, &nRCGI.nRCellIdentity); + NR_CELL_ID_TO_BIT_STRING(12345678, &nRCGI.nRCellIdentity); scell_toBeRemoved_item.sCell_ID = nRCGI; /* ADD */ @@ -1484,7 +1539,7 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance) { /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 UE CONTEXT_MODIFICATION REQUEST\n"); return -1; } diff --git a/openair2/F1AP/f1ap_decoder.c b/openair2/F1AP/f1ap_decoder.c index 7bd5359e29..ac55db7fda 100644 --- a/openair2/F1AP/f1ap_decoder.c +++ b/openair2/F1AP/f1ap_decoder.c @@ -49,6 +49,11 @@ static int f1ap_decode_initiating_message(F1AP_F1AP_PDU_t *pdu) LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_F1Setup\n", __func__); break; + case F1AP_ProcedureCode_id_gNBCUConfigurationUpdate: + //res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu); + LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_gNBCUConfigurationUpdate\n", __func__); + break; + case F1AP_ProcedureCode_id_InitialULRRCMessageTransfer: //res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu); LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_InitialULRRCMessageTransfer\n", __func__); @@ -69,6 +74,9 @@ static int f1ap_decode_initiating_message(F1AP_F1AP_PDU_t *pdu) case F1AP_ProcedureCode_id_UEContextReleaseRequest: LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_UEContextReleaseRequest\n", __func__); break; + case F1AP_ProcedureCode_id_UEContextSetup: + LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_UEContextSetup\n", __func__); + break; // case F1AP_ProcedureCode_id_InitialContextSetup: // res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu); // message_id = F1AP_INITIAL_CONTEXT_SETUP_LOG; @@ -102,6 +110,10 @@ static int f1ap_decode_successful_outcome(F1AP_F1AP_PDU_t *pdu) LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_F1Setup\n", __func__); break; + case F1AP_ProcedureCode_id_gNBCUConfigurationUpdate: + LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_gNBCUConfigurationUpdate\n", __func__); + break; + case F1AP_ProcedureCode_id_UEContextRelease: LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_UEContextRelease\n", __func__); break; @@ -157,7 +169,7 @@ int f1ap_decode_pdu(F1AP_F1AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t //LOG_I(F1AP, "f1ap_decode_pdu.dec_ret.code = %d\n", dec_ret.code); if (dec_ret.code != RC_OK) { - LOG_E(F1AP, "Failed to decode pdu\n"); + AssertFatal(1==0,"Failed to decode pdu\n"); return -1; } diff --git a/openair2/F1AP/f1ap_du_interface_management.c b/openair2/F1AP/f1ap_du_interface_management.c index 0b71ce38b7..b6ece9b13f 100644 --- a/openair2/F1AP/f1ap_du_interface_management.c +++ b/openair2/F1AP/f1ap_du_interface_management.c @@ -38,7 +38,18 @@ #include "assertions.h" extern f1ap_setup_req_t *f1ap_du_data; +extern RAN_CONTEXT_t RC; +int nrb_lut[29] = {11, 18, 24, 25, 31, 32, 38, 51, 52, 65, 66, 78, 79, 93, 106, 107, 121, 132, 133, 135, 160, 162, 189, 216, 217, 245, 264, 270, 273}; + +int to_NRNRB(int nrb) { + if (RC.nrrrc) { + for (int i=0;i<29;i++) if (nrb_lut[i] == nrb) return i; + AssertFatal(1==0,"nrb %d is not in the list of possible NRNRB\n",nrb); + } else { + return nrb; + } +} int DU_handle_RESET(instance_t instance, uint32_t assoc_id, @@ -188,9 +199,19 @@ int DU_send_F1_SETUP_REQUEST(instance_t instance) { served_cell_information.nRPCI = f1ap_du_data->nr_pci[i]; // int 0..1007 /* - fiveGS_TAC */ - OCTET_STRING_fromBuf(served_cell_information.fiveGS_TAC, - (const char*)&f1ap_du_data->tac[i], - 3); + if (RC.nrrrc) { + uint8_t fiveGS_TAC[3]; + fiveGS_TAC[0] = ((uint8_t*)&f1ap_du_data->tac[i])[2]; + fiveGS_TAC[1] = ((uint8_t*)&f1ap_du_data->tac[i])[1]; + fiveGS_TAC[2] = ((uint8_t*)&f1ap_du_data->tac[i])[0]; + OCTET_STRING_fromBuf(served_cell_information.fiveGS_TAC, + (const char *)fiveGS_TAC, + 3); + } else { + OCTET_STRING_fromBuf(served_cell_information.fiveGS_TAC, + (const char*)&f1ap_du_data->tac[i], + 3); + } /* - Configured_EPS_TAC */ if(0){ @@ -299,10 +320,10 @@ int DU_send_F1_SETUP_REQUEST(instance_t instance) { /* FDD.3 UL Transmission Bandwidth */ fDD_Info->uL_Transmission_Bandwidth.nRSCS = f1ap_du_data->nr_mode_info[i].fdd.ul_scs; - fDD_Info->uL_Transmission_Bandwidth.nRNRB = f1ap_du_data->nr_mode_info[i].fdd.ul_nrb; + fDD_Info->uL_Transmission_Bandwidth.nRNRB = to_NRNRB(f1ap_du_data->nr_mode_info[i].fdd.ul_nrb); /* FDD.4 DL Transmission Bandwidth */ fDD_Info->dL_Transmission_Bandwidth.nRSCS = f1ap_du_data->nr_mode_info[i].fdd.dl_scs; - fDD_Info->dL_Transmission_Bandwidth.nRNRB = f1ap_du_data->nr_mode_info[i].fdd.dl_nrb; + fDD_Info->dL_Transmission_Bandwidth.nRNRB = to_NRNRB(f1ap_du_data->nr_mode_info[i].fdd.dl_nrb); nR_Mode_Info.choice.fDD = fDD_Info; } else { // TDD @@ -354,7 +375,7 @@ int DU_send_F1_SETUP_REQUEST(instance_t instance) { /* TDD.2 transmission_Bandwidth */ tDD_Info->transmission_Bandwidth.nRSCS = f1ap_du_data->nr_mode_info[i].tdd.scs; - tDD_Info->transmission_Bandwidth.nRNRB = f1ap_du_data->nr_mode_info[i].tdd.nrb; + tDD_Info->transmission_Bandwidth.nRNRB = to_NRNRB(f1ap_du_data->nr_mode_info[i].tdd.nrb); nR_Mode_Info.choice.tDD = tDD_Info; } // if nR_Mode_Info @@ -376,6 +397,7 @@ int DU_send_F1_SETUP_REQUEST(instance_t instance) { (const char*)f1ap_du_data->mib[i],//f1ap_du_data->mib, f1ap_du_data->mib_length[i]); + LOG_D(F1AP,"Filling SIB1_message for cell %d, length %d\n",i,f1ap_du_data->sib1_length[i]); OCTET_STRING_fromBuf(&gNB_DU_System_Information->sIB1_message, // sept. 2018 (const char*)f1ap_du_data->sib1[i], f1ap_du_data->sib1_length[i]); @@ -391,6 +413,20 @@ int DU_send_F1_SETUP_REQUEST(instance_t instance) { } ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + /* mandatory */ + /* c5. RRC VERSION */ + if(RC.nrrrc) { + ie = (F1AP_F1SetupRequestIEs_t *) calloc(1, sizeof(F1AP_F1SetupRequestIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_GNB_DU_RRC_Version; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_F1SetupRequestIEs__value_PR_RRC_Version; + ie->value.choice.RRC_Version.latest_RRC_Version.buf = calloc(1, sizeof(char)); + ie->value.choice.RRC_Version.latest_RRC_Version.buf[0] = 0xe0; + ie->value.choice.RRC_Version.latest_RRC_Version.size = 1; + ie->value.choice.RRC_Version.latest_RRC_Version.bits_unused = 5; + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + } + /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { LOG_E(F1AP, "Failed to encode F1 setup request\n"); @@ -417,158 +453,175 @@ int DU_handle_F1_SETUP_RESPONSE(instance_t instance, F1AP_F1AP_PDU_t *pdu) { - LOG_D(F1AP, "DU_handle_F1_SETUP_RESPONSE\n"); + LOG_D(F1AP, "DU_handle_F1_SETUP_RESPONSE\n"); - AssertFatal(pdu->present == F1AP_F1AP_PDU_PR_successfulOutcome, - "pdu->present != F1AP_F1AP_PDU_PR_successfulOutcome\n"); - AssertFatal(pdu->choice.successfulOutcome->procedureCode == F1AP_ProcedureCode_id_F1Setup, - "pdu->choice.successfulOutcome->procedureCode != F1AP_ProcedureCode_id_F1Setup\n"); - AssertFatal(pdu->choice.successfulOutcome->criticality == F1AP_Criticality_reject, - "pdu->choice.successfulOutcome->criticality != F1AP_Criticality_reject\n"); - AssertFatal(pdu->choice.successfulOutcome->value.present == F1AP_SuccessfulOutcome__value_PR_F1SetupResponse, - "pdu->choice.successfulOutcome->value.present != F1AP_SuccessfulOutcome__value_PR_F1SetupResponse\n"); + AssertFatal(pdu->present == F1AP_F1AP_PDU_PR_successfulOutcome, + "pdu->present != F1AP_F1AP_PDU_PR_successfulOutcome\n"); + AssertFatal(pdu->choice.successfulOutcome->procedureCode == F1AP_ProcedureCode_id_F1Setup, + "pdu->choice.successfulOutcome->procedureCode != F1AP_ProcedureCode_id_F1Setup\n"); + AssertFatal(pdu->choice.successfulOutcome->criticality == F1AP_Criticality_reject, + "pdu->choice.successfulOutcome->criticality != F1AP_Criticality_reject\n"); + AssertFatal(pdu->choice.successfulOutcome->value.present == F1AP_SuccessfulOutcome__value_PR_F1SetupResponse, + "pdu->choice.successfulOutcome->value.present != F1AP_SuccessfulOutcome__value_PR_F1SetupResponse\n"); - F1AP_F1SetupResponse_t *in = &pdu->choice.successfulOutcome->value.choice.F1SetupResponse; + F1AP_F1SetupResponse_t *in = &pdu->choice.successfulOutcome->value.choice.F1SetupResponse; - F1AP_F1SetupResponseIEs_t *ie; - int TransactionId = -1; - int num_cells_to_activate = 0; - F1AP_Cells_to_be_Activated_List_Item_t *cell; + F1AP_F1SetupResponseIEs_t *ie; + int TransactionId = -1; + int num_cells_to_activate = 0; + F1AP_Cells_to_be_Activated_List_Item_t *cell; - MessageDef *msg_p = itti_alloc_new_message (TASK_DU_F1, 0, F1AP_SETUP_RESP); + MessageDef *msg_p = itti_alloc_new_message (TASK_DU_F1, 0, F1AP_SETUP_RESP); - LOG_D(F1AP, "F1AP: F1Setup-Resp: protocolIEs.list.count %d\n", - in->protocolIEs.list.count); - for (int i=0;i < in->protocolIEs.list.count; i++) { - ie = in->protocolIEs.list.array[i]; - switch (ie->id) { - case F1AP_ProtocolIE_ID_id_TransactionID: - AssertFatal(ie->criticality == F1AP_Criticality_reject, - "ie->criticality != F1AP_Criticality_reject\n"); - AssertFatal(ie->value.present == F1AP_F1SetupResponseIEs__value_PR_TransactionID, - "ie->value.present != F1AP_F1SetupResponseIEs__value_PR_TransactionID\n"); - TransactionId=ie->value.choice.TransactionID; - LOG_D(F1AP, "F1AP: F1Setup-Resp: TransactionId %d\n", - TransactionId); - break; - case F1AP_ProtocolIE_ID_id_gNB_CU_Name: - AssertFatal(ie->criticality == F1AP_Criticality_ignore, - "ie->criticality != F1AP_Criticality_ignore\n"); - AssertFatal(ie->value.present == F1AP_F1SetupResponseIEs__value_PR_GNB_CU_Name, - "ie->value.present != F1AP_F1SetupResponseIEs__value_PR_TransactionID\n"); - F1AP_SETUP_RESP (msg_p).gNB_CU_name = malloc(ie->value.choice.GNB_CU_Name.size+1); - memcpy(F1AP_SETUP_RESP (msg_p).gNB_CU_name,ie->value.choice.GNB_CU_Name.buf,ie->value.choice.GNB_CU_Name.size); - F1AP_SETUP_RESP (msg_p).gNB_CU_name[ie->value.choice.GNB_CU_Name.size]='\0'; - LOG_D(F1AP, "F1AP: F1Setup-Resp: gNB_CU_name %s\n", - F1AP_SETUP_RESP (msg_p).gNB_CU_name); - break; - case F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List: - AssertFatal(ie->criticality == F1AP_Criticality_reject, - "ie->criticality != F1AP_Criticality_reject\n"); - AssertFatal(ie->value.present == F1AP_F1SetupResponseIEs__value_PR_Cells_to_be_Activated_List, - "ie->value.present != F1AP_F1SetupResponseIEs__value_PR_Cells_to_be_Activated_List\n"); - num_cells_to_activate = ie->value.choice.Cells_to_be_Activated_List.list.count; - LOG_D(F1AP, "F1AP: Activating %d cells\n",num_cells_to_activate); - for (int i=0;ivalue.choice.Cells_to_be_Activated_List.list.array[i]; + LOG_D(F1AP, "F1AP: F1Setup-Resp: protocolIEs.list.count %d\n", + in->protocolIEs.list.count); + for (int i=0;i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_TransactionID: + AssertFatal(ie->criticality == F1AP_Criticality_reject, + "ie->criticality != F1AP_Criticality_reject\n"); + AssertFatal(ie->value.present == F1AP_F1SetupResponseIEs__value_PR_TransactionID, + "ie->value.present != F1AP_F1SetupResponseIEs__value_PR_TransactionID\n"); + TransactionId=ie->value.choice.TransactionID; + LOG_D(F1AP, "F1AP: F1Setup-Resp: TransactionId %d\n", + TransactionId); + break; + case F1AP_ProtocolIE_ID_id_gNB_CU_Name: + AssertFatal(ie->criticality == F1AP_Criticality_ignore, + "ie->criticality != F1AP_Criticality_ignore\n"); + AssertFatal(ie->value.present == F1AP_F1SetupResponseIEs__value_PR_GNB_CU_Name, + "ie->value.present != F1AP_F1SetupResponseIEs__value_PR_TransactionID\n"); + F1AP_SETUP_RESP (msg_p).gNB_CU_name = malloc(ie->value.choice.GNB_CU_Name.size+1); + memcpy(F1AP_SETUP_RESP (msg_p).gNB_CU_name,ie->value.choice.GNB_CU_Name.buf,ie->value.choice.GNB_CU_Name.size); + F1AP_SETUP_RESP (msg_p).gNB_CU_name[ie->value.choice.GNB_CU_Name.size]='\0'; + LOG_D(F1AP, "F1AP: F1Setup-Resp: gNB_CU_name %s\n", + F1AP_SETUP_RESP (msg_p).gNB_CU_name); + break; + case F1AP_ProtocolIE_ID_id_GNB_CU_RRC_Version: + LOG_D(F1AP, "F1AP: Received GNB-CU-RRC-Version, ignoring\n"); + break; + case F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List: + { + AssertFatal(ie->criticality == F1AP_Criticality_reject, + "ie->criticality != F1AP_Criticality_reject\n"); + AssertFatal(ie->value.present == F1AP_F1SetupResponseIEs__value_PR_Cells_to_be_Activated_List, + "ie->value.present != F1AP_F1SetupResponseIEs__value_PR_Cells_to_be_Activated_List\n"); + num_cells_to_activate = ie->value.choice.Cells_to_be_Activated_List.list.count; + LOG_D(F1AP, "F1AP: Activating %d cells\n",num_cells_to_activate); + for (int i=0;iid == F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item, - "cells_to_be_activated_list_item_ies->id != F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item"); - AssertFatal(cells_to_be_activated_list_item_ies->criticality == F1AP_Criticality_reject, - "cells_to_be_activated_list_item_ies->criticality == F1AP_Criticality_reject"); - AssertFatal(cells_to_be_activated_list_item_ies->value.present == F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item, - "cells_to_be_activated_list_item_ies->value.present == F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item"); + F1AP_Cells_to_be_Activated_List_ItemIEs_t *cells_to_be_activated_list_item_ies = (F1AP_Cells_to_be_Activated_List_ItemIEs_t *) ie->value.choice.Cells_to_be_Activated_List.list.array[i]; - cell = &cells_to_be_activated_list_item_ies->value.choice.Cells_to_be_Activated_List_Item; + AssertFatal(cells_to_be_activated_list_item_ies->id == F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item, + "cells_to_be_activated_list_item_ies->id != F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item"); + AssertFatal(cells_to_be_activated_list_item_ies->criticality == F1AP_Criticality_reject, + "cells_to_be_activated_list_item_ies->criticality == F1AP_Criticality_reject"); + AssertFatal(cells_to_be_activated_list_item_ies->value.present == F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item, + "cells_to_be_activated_list_item_ies->value.present == F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item"); - TBCD_TO_MCC_MNC(&cell->nRCGI.pLMN_Identity, F1AP_SETUP_RESP (msg_p).mcc[i], F1AP_SETUP_RESP (msg_p).mnc[i], F1AP_SETUP_RESP (msg_p).mnc_digit_length[i]); - AssertFatal(cell->nRPCI != NULL, "nRPCI is null\n"); - LOG_D(F1AP, "nr_cellId : %x %x %x %x %x\n", - cell->nRCGI.nRCellIdentity.buf[0], - cell->nRCGI.nRCellIdentity.buf[1], - cell->nRCGI.nRCellIdentity.buf[2], - cell->nRCGI.nRCellIdentity.buf[3], - cell->nRCGI.nRCellIdentity.buf[4]); - BIT_STRING_TO_NR_CELL_IDENTITY(&cell->nRCGI.nRCellIdentity, - F1AP_SETUP_RESP (msg_p).nr_cellid[i]); - F1AP_SETUP_RESP (msg_p).nrpci[i] = *cell->nRPCI; + cell = &cells_to_be_activated_list_item_ies->value.choice.Cells_to_be_Activated_List_Item; - F1AP_ProtocolExtensionContainer_154P112_t *ext = (F1AP_ProtocolExtensionContainer_154P112_t *)cell->iE_Extensions; - AssertFatal(ext!=NULL,"Extension is null\n"); - for (int cnt=0;cntlist.count;cnt++) { + TBCD_TO_MCC_MNC(&cell->nRCGI.pLMN_Identity, F1AP_SETUP_RESP (msg_p).cells_to_activate[i].mcc, F1AP_SETUP_RESP (msg_p).cells_to_activate[i].mnc, F1AP_SETUP_RESP (msg_p).cells_to_activate[i].mnc_digit_length); + + LOG_D(F1AP, "nr_cellId : %x %x %x %x %x\n", + cell->nRCGI.nRCellIdentity.buf[0], + cell->nRCGI.nRCellIdentity.buf[1], + cell->nRCGI.nRCellIdentity.buf[2], + cell->nRCGI.nRCellIdentity.buf[3], + cell->nRCGI.nRCellIdentity.buf[4]); + + BIT_STRING_TO_NR_CELL_IDENTITY(&cell->nRCGI.nRCellIdentity, + F1AP_SETUP_RESP (msg_p).cells_to_activate[i].nr_cellid); + F1AP_ProtocolExtensionContainer_154P112_t *ext = (F1AP_ProtocolExtensionContainer_154P112_t *)cell->iE_Extensions; + + if (ext==NULL) continue; + + for (int cnt=0;cntlist.count;cnt++) { F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *cells_to_be_activated_list_itemExtIEs=(F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *)ext->list.array[cnt]; switch (cells_to_be_activated_list_itemExtIEs->id) { -/* - case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_NOTHING: - case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_GNB_CUSystemInformation, - case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_AvailablePLMNList, - case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_ExtendedAvailablePLMN_List, - case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_IAB_Info_IAB_donor_CU, - case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_AvailableSNPN_ID_List -*/ - case F1AP_ProtocolIE_ID_id_gNB_CUSystemInformation: - { - F1AP_GNB_CUSystemInformation_t *gNB_CUSystemInformation = (F1AP_GNB_CUSystemInformation_t*)&cells_to_be_activated_list_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation; - F1AP_SETUP_RESP (msg_p).num_SI[i] = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.count; - AssertFatal(ext->list.count==1,"At least one SI message should be there, and only 1 for now!\n"); - LOG_D(F1AP, "F1AP: F1Setup-Resp Cell %d MCC %d MNC %d NRCellid %lx num_si %d\n", - i, F1AP_SETUP_RESP (msg_p).mcc[i], F1AP_SETUP_RESP (msg_p).mnc[i], - F1AP_SETUP_RESP (msg_p).nr_cellid[i], F1AP_SETUP_RESP (msg_p).num_SI[i]); - for (int si =0;si < gNB_CUSystemInformation->sibtypetobeupdatedlist.list.count;si++) { - F1AP_SibtypetobeupdatedListItem_t *sib_item = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.array[si]; - size_t size = sib_item->sIBmessage.size; - F1AP_SETUP_RESP (msg_p).SI_container_length[i][sib_item->sIBtype] = size; - LOG_I(F1AP, "F1AP: F1Setup-Resp SI_container_length[%d][%d] %ld bytes\n", i, (int)sib_item->sIBtype, size); - F1AP_SETUP_RESP (msg_p).SI_container[i][sib_item->sIBtype] = malloc(size); - memcpy((void*)F1AP_SETUP_RESP (msg_p).SI_container[i][sib_item->sIBtype], - (void*)sib_item->sIBmessage.buf, - size); - } - } - break; - case F1AP_ProtocolIE_ID_id_AvailablePLMNList: - AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); - break; - case F1AP_ProtocolIE_ID_id_ExtendedAvailablePLMN_List: - AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); - break; - case F1AP_ProtocolIE_ID_id_IAB_Info_IAB_donor_CU: - AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); - break; - case F1AP_ProtocolIE_ID_id_AvailableSNPN_ID_List: - AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); - break; - default: - AssertFatal(1==0,"F1AP_ProtocolIE_ID_id %d unknown\n",(int)cells_to_be_activated_list_itemExtIEs->id); - break; + /* + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_NOTHING: + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_GNB_CUSystemInformation, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_AvailablePLMNList, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_ExtendedAvailablePLMN_List, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_IAB_Info_IAB_donor_CU, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_AvailableSNPN_ID_List + */ + case F1AP_ProtocolIE_ID_id_gNB_CUSystemInformation: + { + F1AP_SETUP_RESP (msg_p).cells_to_activate[i].nrpci = (cell->nRPCI != NULL) ? *cell->nRPCI : 0; + F1AP_GNB_CUSystemInformation_t *gNB_CUSystemInformation = (F1AP_GNB_CUSystemInformation_t*)&cells_to_be_activated_list_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation; + F1AP_SETUP_RESP (msg_p).cells_to_activate[i].num_SI = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.count; + AssertFatal(ext->list.count==1,"At least one SI message should be there, and only 1 for now!\n"); + LOG_D(F1AP, "F1AP: Cell %d MCC %d MNC %d NRCellid %lx num_si %d\n", + i, F1AP_SETUP_RESP (msg_p).cells_to_activate[i].mcc, F1AP_SETUP_RESP (msg_p).cells_to_activate[i].mnc, + F1AP_SETUP_RESP (msg_p).cells_to_activate[i].nr_cellid, F1AP_SETUP_RESP (msg_p).cells_to_activate[i].num_SI); + for (int si = 0;si < gNB_CUSystemInformation->sibtypetobeupdatedlist.list.count;si++) { + F1AP_SibtypetobeupdatedListItem_t *sib_item = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.array[si]; + size_t size = sib_item->sIBmessage.size; + F1AP_SETUP_RESP (msg_p).cells_to_activate[i].SI_container_length[sib_item->sIBtype] = size; + LOG_I(F1AP, "F1AP: SI_container_length[%d][%d] %ld bytes\n", i, (int)sib_item->sIBtype, size); + F1AP_SETUP_RESP (msg_p).cells_to_activate[i].SI_container[sib_item->sIBtype] = malloc(size); + memcpy((void*)F1AP_SETUP_RESP (msg_p).cells_to_activate[i].SI_container[sib_item->sIBtype], + (void*)sib_item->sIBmessage.buf, + size); + } + break; + } + case F1AP_ProtocolIE_ID_id_AvailablePLMNList: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + case F1AP_ProtocolIE_ID_id_ExtendedAvailablePLMN_List: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + case F1AP_ProtocolIE_ID_id_IAB_Info_IAB_donor_CU: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + case F1AP_ProtocolIE_ID_id_AvailableSNPN_ID_List: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + default: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id %d unknown\n",(int)cells_to_be_activated_list_itemExtIEs->id); + break; } - } // for (cnt=... - } // for (cells_to_activate... - } // switch ie - } // for IE - AssertFatal(TransactionId!=-1,"TransactionId was not sent\n"); - AssertFatal(num_cells_to_activate>0,"No cells activated\n"); - F1AP_SETUP_RESP (msg_p).num_cells_to_activate = num_cells_to_activate; + } // for (cnt=... + } // for (cells_to_activate... + break; + } // case F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List - for (int i=0;i 0, "System Information %d is missing",i); + default: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id %d unknown\n", (int)ie->id); + break; + } // switch ie + } // for IE + AssertFatal(TransactionId!=-1,"TransactionId was not sent\n"); + F1AP_SETUP_RESP (msg_p).num_cells_to_activate = num_cells_to_activate; - MSC_LOG_RX_MESSAGE( - MSC_F1AP_DU, - MSC_F1AP_CU, - 0, - 0, - MSC_AS_TIME_FMT" DU_handle_F1_SETUP_RESPONSE successfulOutcome assoc_id %d", - 0,0,//MSC_AS_TIME_ARGS(ctxt_pP), - assoc_id); - - LOG_D(F1AP, "Sending F1AP_SETUP_RESP ITTI message to ENB_APP with assoc_id (%d->%d)\n", + for (int i=0;i 0, "System Information %d is missing",i); + + MSC_LOG_RX_MESSAGE( + MSC_F1AP_DU, + MSC_F1AP_CU, + 0, + 0, + MSC_AS_TIME_FMT" DU_handle_F1_SETUP_RESPONSE successfulOutcome assoc_id %d", + 0,0,//MSC_AS_TIME_ARGS(ctxt_pP), + assoc_id); + + if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_DU) { + LOG_D(F1AP, "Sending F1AP_SETUP_RESP ITTI message to GNB_APP with assoc_id (%d->%d)\n", assoc_id,ENB_MODULE_ID_TO_INSTANCE(assoc_id)); - itti_send_msg_to_task(TASK_ENB_APP, instance, msg_p); + itti_send_msg_to_task(TASK_GNB_APP, GNB_MODULE_ID_TO_INSTANCE(assoc_id), msg_p); + } else { + LOG_D(F1AP, "Sending F1AP_SETUP_RESP ITTI message to ENB_APP with assoc_id (%d->%d)\n", + assoc_id,ENB_MODULE_ID_TO_INSTANCE(assoc_id)); + itti_send_msg_to_task(TASK_ENB_APP, instance, msg_p); + } - return 0; + return 0; } // SETUP FAILURE @@ -577,6 +630,26 @@ int DU_handle_F1_SETUP_FAILURE(instance_t instance, uint32_t stream, F1AP_F1AP_PDU_t *pdu) { LOG_E(F1AP, "DU_handle_F1_SETUP_FAILURE\n"); + + F1AP_F1SetupFailure_t *out; + F1AP_F1SetupFailureIEs_t *ie; + + out = &pdu->choice.unsuccessfulOutcome->value.choice.F1SetupFailure; + + /* Transaction ID */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_F1SetupFailureIEs_t, ie, out, + F1AP_ProtocolIE_ID_id_TransactionID, true); + + /* Cause */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_F1SetupFailureIEs_t, ie, out, + F1AP_ProtocolIE_ID_id_Cause, true); + + if(0) { + /* TimeToWait */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_F1SetupFailureIEs_t, ie, out, + F1AP_ProtocolIE_ID_id_TimeToWait, true); + } + return 0; } @@ -659,9 +732,19 @@ int DU_send_gNB_DU_CONFIGURATION_UPDATE(instance_t instance, served_cell_information.nRPCI = f1ap_setup_req->nr_pci[i]; // int 0..1007 /* - fiveGS_TAC */ - OCTET_STRING_fromBuf(served_cell_information.fiveGS_TAC, - (const char *) &f1ap_setup_req->tac[i], - 3); + if (RC.nrrrc) { + uint8_t fiveGS_TAC[3]; + fiveGS_TAC[0] = ((uint8_t*)&f1ap_setup_req->tac[i])[2]; + fiveGS_TAC[1] = ((uint8_t*)&f1ap_setup_req->tac[i])[1]; + fiveGS_TAC[2] = ((uint8_t*)&f1ap_setup_req->tac[i])[0]; + OCTET_STRING_fromBuf(served_cell_information.fiveGS_TAC, + (const char *)fiveGS_TAC, + 3); + } else { + OCTET_STRING_fromBuf(served_cell_information.fiveGS_TAC, + (const char *) &f1ap_setup_req->tac[i], + 3); + } /* - Configured_EPS_TAC */ if(1){ @@ -676,7 +759,6 @@ int DU_send_gNB_DU_CONFIGURATION_UPDATE(instance_t instance, MCC_MNC_TO_PLMNID(f1ap_du_data->mcc[i], f1ap_du_data->mnc[i], f1ap_du_data->mnc_digit_length[i], &servedPLMN_item->pLMN_Identity); ASN_SEQUENCE_ADD(&served_cell_information.servedPLMNs.list, servedPLMN_item); - // // /* - CHOICE NR-MODE-Info */ F1AP_NR_Mode_Info_t nR_Mode_Info; @@ -838,8 +920,6 @@ int DU_send_gNB_DU_CONFIGURATION_UPDATE(instance_t instance, MCC_MNC_TO_PLMNID(f1ap_du_data->mcc[i], f1ap_du_data->mnc[i], f1ap_du_data->mnc_digit_length[i], &servedPLMN_item->pLMN_Identity); ASN_SEQUENCE_ADD(&served_cell_information.servedPLMNs.list, servedPLMN_item); - - // // /* - CHOICE NR-MODE-Info */ F1AP_NR_Mode_Info_t nR_Mode_Info; @@ -978,7 +1058,7 @@ int DU_send_gNB_DU_CONFIGURATION_UPDATE(instance_t instance, if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 gNB-DU CONFIGURATION UPDATE\n"); return -1; } @@ -1006,17 +1086,229 @@ int DU_handle_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, uint32_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu) { - AssertFatal(1==0,"Not implemented yet\n"); + + if(!RC.nrrrc) { + return 0; + } + + LOG_D(F1AP, "DU_handle_gNB_CU_CONFIGURATION_UPDATE\n"); + + AssertFatal(pdu->present == F1AP_F1AP_PDU_PR_initiatingMessage, + "pdu->present != F1AP_F1AP_PDU_PR_initiatingMessage\n"); + AssertFatal(pdu->choice.initiatingMessage->procedureCode == F1AP_ProcedureCode_id_gNBCUConfigurationUpdate, + "pdu->choice.initiatingMessage->procedureCode != F1AP_ProcedureCode_id_gNBCUConfigurationUpdate\n"); + AssertFatal(pdu->choice.initiatingMessage->criticality == F1AP_Criticality_reject, + "pdu->choice.initiatingMessage->criticality != F1AP_Criticality_reject\n"); + AssertFatal(pdu->choice.initiatingMessage->value.present == F1AP_InitiatingMessage__value_PR_GNBCUConfigurationUpdate, + "pdu->choice.initiatingMessage->value.present != F1AP_InitiatingMessage__value_PR_GNBCUConfigurationUpdate\n"); + + F1AP_GNBCUConfigurationUpdate_t *in = &pdu->choice.initiatingMessage->value.choice.GNBCUConfigurationUpdate; + + + F1AP_GNBCUConfigurationUpdateIEs_t *ie; + int TransactionId = -1; + int num_cells_to_activate = 0; + F1AP_Cells_to_be_Activated_List_Item_t *cell; + + MessageDef *msg_p = itti_alloc_new_message (TASK_DU_F1, 0, F1AP_GNB_CU_CONFIGURATION_UPDATE); + + LOG_D(F1AP, "F1AP: gNB_CU_Configuration_Update: protocolIEs.list.count %d\n", + in->protocolIEs.list.count); + for (int i=0;i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_TransactionID: + AssertFatal(ie->criticality == F1AP_Criticality_reject, + "ie->criticality != F1AP_Criticality_reject\n"); + AssertFatal(ie->value.present == F1AP_GNBCUConfigurationUpdateIEs__value_PR_TransactionID, + "ie->value.present != F1AP_GNBCUConfigurationUpdateIEs__value_PR_TransactionID\n"); + TransactionId=ie->value.choice.TransactionID; + LOG_D(F1AP, "F1AP: GNB-CU-ConfigurationUpdate: TransactionId %d\n", + TransactionId); + break; + case F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List: + { + AssertFatal(ie->criticality == F1AP_Criticality_reject, + "ie->criticality != F1AP_Criticality_reject\n"); + AssertFatal(ie->value.present == F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Activated_List, + "ie->value.present != F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Activated_List\n"); + num_cells_to_activate = ie->value.choice.Cells_to_be_Activated_List.list.count; + LOG_D(F1AP, "F1AP: Activating %d cells\n",num_cells_to_activate); + for (int i=0;ivalue.choice.Cells_to_be_Activated_List.list.array[i]; + + AssertFatal(cells_to_be_activated_list_item_ies->id == F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item, + "cells_to_be_activated_list_item_ies->id != F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List_Item"); + AssertFatal(cells_to_be_activated_list_item_ies->criticality == F1AP_Criticality_reject, + "cells_to_be_activated_list_item_ies->criticality == F1AP_Criticality_reject"); + AssertFatal(cells_to_be_activated_list_item_ies->value.present == F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item, + "cells_to_be_activated_list_item_ies->value.present == F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item"); + + cell = &cells_to_be_activated_list_item_ies->value.choice.Cells_to_be_Activated_List_Item; + + TBCD_TO_MCC_MNC(&cell->nRCGI.pLMN_Identity, F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].mcc, F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].mnc, F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].mnc_digit_length); + + LOG_D(F1AP, "nr_cellId : %x %x %x %x %x\n", + cell->nRCGI.nRCellIdentity.buf[0], + cell->nRCGI.nRCellIdentity.buf[1], + cell->nRCGI.nRCellIdentity.buf[2], + cell->nRCGI.nRCellIdentity.buf[3], + cell->nRCGI.nRCellIdentity.buf[4]); + BIT_STRING_TO_NR_CELL_IDENTITY(&cell->nRCGI.nRCellIdentity, + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].nr_cellid); + F1AP_ProtocolExtensionContainer_154P112_t *ext = (F1AP_ProtocolExtensionContainer_154P112_t *)cell->iE_Extensions; + + if (ext==NULL) continue; + + for (int cnt=0;cntlist.count;cnt++) { + F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *cells_to_be_activated_list_itemExtIEs=(F1AP_Cells_to_be_Activated_List_ItemExtIEs_t *)ext->list.array[cnt]; + switch (cells_to_be_activated_list_itemExtIEs->id) { + /* + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_NOTHING: + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_GNB_CUSystemInformation, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_AvailablePLMNList, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_ExtendedAvailablePLMN_List, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_IAB_Info_IAB_donor_CU, + case F1AP_Cells_to_be_Activated_List_ItemExtIEs__extensionValue_PR_AvailableSNPN_ID_List + */ + case F1AP_ProtocolIE_ID_id_gNB_CUSystemInformation: + { + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].nrpci = (cell->nRPCI != NULL) ? *cell->nRPCI : 0; + F1AP_GNB_CUSystemInformation_t *gNB_CUSystemInformation = (F1AP_GNB_CUSystemInformation_t*)&cells_to_be_activated_list_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].num_SI = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.count; + AssertFatal(ext->list.count==1,"At least one SI message should be there, and only 1 for now!\n"); + LOG_D(F1AP, "F1AP: Cell %d MCC %d MNC %d NRCellid %lx num_si %d\n", + i, F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].mcc, F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].mnc, + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].nr_cellid, F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].num_SI); + for (int si = 0;si < gNB_CUSystemInformation->sibtypetobeupdatedlist.list.count;si++) { + F1AP_SibtypetobeupdatedListItem_t *sib_item = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.array[si]; + size_t size = sib_item->sIBmessage.size; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].SI_container_length[sib_item->sIBtype] = size; + LOG_I(F1AP, "F1AP: SI_container_length[%d][%d] %ld bytes\n", i, (int)sib_item->sIBtype, size); + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].SI_container[sib_item->sIBtype] = malloc(size); + memcpy((void*)F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).cells_to_activate[i].SI_container[sib_item->sIBtype], + (void*)sib_item->sIBmessage.buf, + size); + } + break; + } + case F1AP_ProtocolIE_ID_id_AvailablePLMNList: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + case F1AP_ProtocolIE_ID_id_ExtendedAvailablePLMN_List: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + case F1AP_ProtocolIE_ID_id_IAB_Info_IAB_donor_CU: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + case F1AP_ProtocolIE_ID_id_AvailableSNPN_ID_List: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id_AvailablePLMNList not supported yet\n"); + break; + default: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id %d unknown\n",(int)cells_to_be_activated_list_itemExtIEs->id); + break; + } + } // for (cnt=... + } // for (cells_to_activate... + break; + } // case F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List + + default: + AssertFatal(1==0,"F1AP_ProtocolIE_ID_id %d unknown\n", (int)ie->id); + break; + } // switch ie + } // for IE + AssertFatal(TransactionId!=-1,"TransactionId was not sent\n"); + LOG_D(F1AP,"F1AP: num_cells_to_activate %d\n",num_cells_to_activate); + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p).num_cells_to_activate = num_cells_to_activate; + + MSC_LOG_RX_MESSAGE( + MSC_F1AP_DU, + MSC_F1AP_CU, + 0, + 0, + MSC_AS_TIME_FMT" DU_handle_GNB_CU_CONFIGURATION_UPDATE initiatingMessage assoc_id %d", + 0,0,//MSC_AS_TIME_ARGS(ctxt_pP), + assoc_id); + + if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_DU) { + LOG_D(F1AP, "Sending F1AP_GNB_CU_CONFIGURATION_UPDATE ITTI message to GNB_APP with assoc_id (%d->%d)\n", + assoc_id,ENB_MODULE_ID_TO_INSTANCE(assoc_id)); + itti_send_msg_to_task(TASK_GNB_APP, GNB_MODULE_ID_TO_INSTANCE(assoc_id), msg_p); + } else { + LOG_D(F1AP, "Sending F1AP_GNB_CU_CONFIGURATION_UPDATE ITTI message to ENB_APP with assoc_id (%d->%d)\n", + assoc_id,ENB_MODULE_ID_TO_INSTANCE(assoc_id)); + itti_send_msg_to_task(TASK_ENB_APP, ENB_MODULE_ID_TO_INSTANCE(assoc_id), msg_p); + } + + return 0; } int DU_send_gNB_CU_CONFIGURATION_UPDATE_FAILURE(instance_t instance, - F1AP_GNBCUConfigurationUpdateFailure_t *GNBCUConfigurationUpdateFailure) { - AssertFatal(1==0,"Not implemented yet\n"); + f1ap_gnb_cu_configuration_update_failure_t *GNBCUConfigurationUpdateFailure) { + AssertFatal(1==0,"received gNB CU CONFIGURATION UPDATE FAILURE with cause %d\n", + GNBCUConfigurationUpdateFailure->cause); } int DU_send_gNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, - F1AP_GNBCUConfigurationUpdateAcknowledge_t *GNBCUConfigurationUpdateAcknowledge) { - AssertFatal(1==0,"Not implemented yet\n"); + f1ap_gnb_cu_configuration_update_acknowledge_t *GNBCUConfigurationUpdateAcknowledge) { + + if(!RC.nrrrc) { + return 0; + } + + AssertFatal(GNBCUConfigurationUpdateAcknowledge->num_cells_failed_to_be_activated == 0, + "%d cells failed to activate\n", + GNBCUConfigurationUpdateAcknowledge->num_cells_failed_to_be_activated); + + AssertFatal(GNBCUConfigurationUpdateAcknowledge->noofTNLAssociations_to_setup == 0, + "%d TNLAssociations to setup, handle this ...\n", + GNBCUConfigurationUpdateAcknowledge->noofTNLAssociations_to_setup); + + + AssertFatal(GNBCUConfigurationUpdateAcknowledge->noofTNLAssociations_failed == 0, + "%d TNLAssociations failed\n", + GNBCUConfigurationUpdateAcknowledge->noofTNLAssociations_failed); + + AssertFatal(GNBCUConfigurationUpdateAcknowledge->noofDedicatedSIDeliveryNeededUEs == 0, + "%d DedicatedSIDeliveryNeededUEs\n", + GNBCUConfigurationUpdateAcknowledge->noofDedicatedSIDeliveryNeededUEs); + + F1AP_F1AP_PDU_t pdu; + uint8_t *buffer; + uint32_t len; + + /* Create */ + /* 0. pdu Type */ + memset(&pdu, 0, sizeof(pdu)); + pdu.present = F1AP_F1AP_PDU_PR_successfulOutcome; + pdu.choice.successfulOutcome = (F1AP_SuccessfulOutcome_t *)calloc(1, sizeof(F1AP_SuccessfulOutcome_t)); + pdu.choice.successfulOutcome->procedureCode = F1AP_ProcedureCode_id_gNBCUConfigurationUpdate; + pdu.choice.successfulOutcome->criticality = F1AP_Criticality_reject; + pdu.choice.successfulOutcome->value.present = F1AP_SuccessfulOutcome__value_PR_GNBCUConfigurationUpdateAcknowledge; + F1AP_GNBCUConfigurationUpdateAcknowledge_t *out = &pdu.choice.successfulOutcome->value.choice.GNBCUConfigurationUpdateAcknowledge; + + /* mandatory */ + /* c1. Transaction ID (integer value)*/ + F1AP_GNBCUConfigurationUpdateAcknowledgeIEs_t *ie = (F1AP_GNBCUConfigurationUpdateAcknowledgeIEs_t *)calloc(1, sizeof(F1AP_F1SetupResponseIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_TransactionID; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_GNBCUConfigurationUpdateAcknowledgeIEs__value_PR_TransactionID; + ie->value.choice.TransactionID = F1AP_get_next_transaction_identifier(0, 0); + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + /* encode */ + if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { + LOG_E(F1AP, "Failed to encode GNB-DU-Configuration-Update-Acknowledge\n"); + return -1; + } + + du_f1ap_itti_send_sctp_data_req(instance, f1ap_du_data->assoc_id, buffer, len, 0); + + + + return 0; } diff --git a/openair2/F1AP/f1ap_du_interface_management.h b/openair2/F1AP/f1ap_du_interface_management.h index 941b86a6d4..619b725f38 100644 --- a/openair2/F1AP/f1ap_du_interface_management.h +++ b/openair2/F1AP/f1ap_du_interface_management.h @@ -98,17 +98,17 @@ int DU_handle_gNB_CU_CONFIGURATION_UPDATE(instance_t instance, F1AP_F1AP_PDU_t *pdu); int DU_send_gNB_CU_CONFIGURATION_UPDATE_FAILURE(instance_t instance, - F1AP_GNBCUConfigurationUpdateFailure_t *GNBCUConfigurationUpdateFailure); + f1ap_gnb_cu_configuration_update_failure_t *GNBCUConfigurationUpdateFailure); int DU_send_gNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, - F1AP_GNBCUConfigurationUpdateAcknowledge_t *GNBCUConfigurationUpdateAcknowledge); + f1ap_gnb_cu_configuration_update_acknowledge_t *GNBCUConfigurationUpdateAcknowledge); /* * gNB-DU Resource Coordination */ int DU_send_gNB_DU_RESOURCE_COORDINATION_REQUEST(instance_t instance, - F1AP_GNBDUResourceCoordinationRequest_t *GNBDUResourceCoordinationRequest); + F1AP_GNBDUResourceCoordinationRequest_t *GNBDUResourceCoordinationRequest); int DU_handle_gNB_DU_RESOURCE_COORDINATION_RESPONSE(instance_t instance, uint32_t assoc_id, diff --git a/openair2/F1AP/f1ap_du_rrc_message_transfer.c b/openair2/F1AP/f1ap_du_rrc_message_transfer.c index 1d6e729ab2..07df0b4a78 100644 --- a/openair2/F1AP/f1ap_du_rrc_message_transfer.c +++ b/openair2/F1AP/f1ap_du_rrc_message_transfer.c @@ -42,13 +42,19 @@ #include "LTE_DL-DCCH-Message.h" #include "LTE_UL-DCCH-Message.h" +#include "NR_DL-CCCH-Message.h" +#include "NR_UL-CCCH-Message.h" +#include "NR_DL-DCCH-Message.h" +#include "NR_UL-DCCH-Message.h" // for SRB1_logicalChannelConfig_defaultValue #include "rrc_extern.h" #include "common/ran_context.h" #include "rrc_eNB_UE_context.h" +#include "openair2/RRC/NR/rrc_gNB_UE_context.h" #include "asn1_msg.h" #include "intertask_interface.h" +#include "LAYER2/NR_MAC_gNB/mac_proto.h" // undefine C_RNTI from // openair1/PHY/LTE_TRANSPORT/transport_common.h which @@ -61,13 +67,32 @@ extern f1ap_setup_req_t *f1ap_du_data; extern RAN_CONTEXT_t RC; extern f1ap_cudu_inst_t f1ap_du_inst[MAX_eNB]; +extern rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP, + const NR_SRB_ToAddModList_t * const srb2add_listP, + const NR_DRB_ToAddModList_t * const drb2add_listP, + const NR_DRB_ToReleaseList_t * const drb2release_listP, + const LTE_PMCH_InfoList_r9_t * const pmch_InfoList_r9_pP, + struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_srb_bearer2add_list, + struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_drb_bearer2add_list); +uint8_t du_ccch_flag = 1; + +int DU_handle_DL_NR_RRC_MESSAGE_TRANSFER(instance_t instance, + uint32_t assoc_id, + uint32_t stream, + F1AP_F1AP_PDU_t *pdu); /* DL RRC Message Transfer */ int DU_handle_DL_RRC_MESSAGE_TRANSFER(instance_t instance, uint32_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu) { + + if (RC.nrrrc && RC.nrrrc[instance]->node_type == ngran_gNB_DU) { + LOG_I(F1AP, "node is gNB DU, call DU_handle_DL_NR_RRC_MESSAGE_TRANSFER \n"); + return DU_handle_DL_NR_RRC_MESSAGE_TRANSFER(instance, assoc_id, stream, pdu); + } + LOG_D(F1AP, "DU_handle_DL_RRC_MESSAGE_TRANSFER \n"); F1AP_DLRRCMessageTransfer_t *container; @@ -454,36 +479,36 @@ int DU_handle_DL_RRC_MESSAGE_TRANSFER(instance_t instance, DRB2LCHAN[i] = (uint8_t) * DRB_configList->list.array[i]->logicalChannelIdentity; } - rrc_mac_config_req_eNB( - ctxt.module_id, - 0,0,0,0,0,0, - 0, - ue_context_p->ue_context.rnti, - (LTE_BCCH_BCH_Message_t *) NULL, - (LTE_RadioResourceConfigCommonSIB_t *) NULL, - (LTE_RadioResourceConfigCommonSIB_t *) NULL, - physicalConfigDedicated, - (LTE_SCellToAddMod_r10_t *)NULL, - //(struct PhysicalConfigDedicatedSCell_r10 *)NULL, - (LTE_MeasObjectToAddMod_t **) NULL, - mac_MainConfig, - DRB2LCHAN[i], - DRB_configList->list.array[i]->logicalChannelConfig, - measGapConfig, - (LTE_TDD_Config_t *) NULL, - NULL, - (LTE_SchedulingInfoList_t *) NULL, - 0, NULL, NULL, (LTE_MBSFN_SubframeConfigList_t *) NULL - , 0, (LTE_MBSFN_AreaInfoList_r9_t *) NULL, (LTE_PMCH_InfoList_r9_t *) NULL, - (LTE_SystemInformationBlockType1_v1310_IEs_t *)NULL, - 0, - (LTE_BCCH_DL_SCH_Message_MBMS_t *) NULL, - (LTE_SchedulingInfo_MBMS_r14_t *) NULL, - (struct LTE_NonMBSFN_SubframeConfig_r14 *) NULL, - (LTE_SystemInformationBlockType1_MBMS_r14_t *) NULL, - (LTE_MBSFN_AreaInfoList_r9_t *) NULL, - (LTE_MBSFNAreaConfiguration_r9_t*) NULL - ); + rrc_mac_config_req_eNB( + ctxt.module_id, + 0,0,0,0,0,0, + 0, + ue_context_p->ue_context.rnti, + (LTE_BCCH_BCH_Message_t *) NULL, + (LTE_RadioResourceConfigCommonSIB_t *) NULL, + (LTE_RadioResourceConfigCommonSIB_t *) NULL, + physicalConfigDedicated, + (LTE_SCellToAddMod_r10_t *)NULL, + //(struct PhysicalConfigDedicatedSCell_r10 *)NULL, + (LTE_MeasObjectToAddMod_t **) NULL, + mac_MainConfig, + DRB2LCHAN[i], + DRB_configList->list.array[i]->logicalChannelConfig, + measGapConfig, + (LTE_TDD_Config_t *) NULL, + NULL, + (LTE_SchedulingInfoList_t *) NULL, + 0, NULL, NULL, (LTE_MBSFN_SubframeConfigList_t *) NULL + , 0, (LTE_MBSFN_AreaInfoList_r9_t *) NULL, (LTE_PMCH_InfoList_r9_t *) NULL, + (LTE_SystemInformationBlockType1_v1310_IEs_t *)NULL, + 0, + (LTE_BCCH_DL_SCH_Message_MBMS_t *) NULL, + (LTE_SchedulingInfo_MBMS_r14_t *) NULL, + (struct LTE_NonMBSFN_SubframeConfig_r14 *) NULL, + (LTE_SystemInformationBlockType1_MBMS_r14_t *) NULL, + (LTE_MBSFN_AreaInfoList_r9_t *) NULL, + (LTE_MBSFNAreaConfiguration_r9_t*) NULL + ); } } else { // remove LCHAN from MAC/PHY @@ -765,7 +790,7 @@ int DU_send_UL_RRC_MESSAGE_TRANSFER(instance_t instance, } /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 UL RRC MESSAGE TRANSFER\n"); return -1; } @@ -780,7 +805,10 @@ int DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(module_id_t module_idP, int UE_id, rnti_t rntiP, const uint8_t *sduP, - sdu_size_t sdu_lenP) { + sdu_size_t sdu_lenP, + const int8_t *sdu2P, + sdu_size_t sdu2_lenP) { + F1AP_F1AP_PDU_t pdu; F1AP_InitialULRRCMessageTransfer_t *out; F1AP_InitialULRRCMessageTransferIEs_t *ie; @@ -850,29 +878,36 @@ int DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(module_id_t module_idP, /* optional */ /* c5. DUtoCURRCContainer */ - if (0) { + if (sdu2P && RC.nrrrc) { ie = (F1AP_InitialULRRCMessageTransferIEs_t *)calloc(1, sizeof(F1AP_InitialULRRCMessageTransferIEs_t)); ie->id = F1AP_ProtocolIE_ID_id_DUtoCURRCContainer; ie->criticality = F1AP_Criticality_reject; ie->value.present = F1AP_InitialULRRCMessageTransferIEs__value_PR_DUtoCURRCContainer; - OCTET_STRING_fromBuf(&ie->value.choice.DUtoCURRCContainer, "dummy_val", - strlen("dummy_val")); + OCTET_STRING_fromBuf(&ie->value.choice.DUtoCURRCContainer, (char *)sdu2P, sdu2_lenP); ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); - } + } /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 INITIAL UL RRC MESSAGE TRANSFER\n"); return -1; } - - struct rrc_eNB_ue_context_s* ue_context_p = rrc_eNB_allocate_new_UE_context(RC.rrc[module_idP]); - ue_context_p->ue_id_rnti = rntiP; - ue_context_p->ue_context.rnti = rntiP; - ue_context_p->ue_context.random_ue_identity = rntiP; - ue_context_p->ue_context.Srb0.Active = 1; - RB_INSERT(rrc_ue_tree_s, &RC.rrc[module_idP]->rrc_ue_head, ue_context_p); + if (RC.nrrrc && RC.nrrrc[module_idP]->node_type == ngran_gNB_DU) { + struct rrc_gNB_ue_context_s* ue_context_p = rrc_gNB_allocate_new_UE_context(RC.nrrrc[module_idP]); + ue_context_p->ue_id_rnti = rntiP; + ue_context_p->ue_context.rnti = rntiP; + ue_context_p->ue_context.random_ue_identity = rntiP; + ue_context_p->ue_context.Srb0.Active = 1; + RB_INSERT(rrc_nr_ue_tree_s, &RC.nrrrc[module_idP]->rrc_ue_head, ue_context_p); + } else { + struct rrc_eNB_ue_context_s* ue_context_p = rrc_eNB_allocate_new_UE_context(RC.rrc[module_idP]); + ue_context_p->ue_id_rnti = rntiP; + ue_context_p->ue_context.rnti = rntiP; + ue_context_p->ue_context.random_ue_identity = rntiP; + ue_context_p->ue_context.Srb0.Active = 1; + RB_INSERT(rrc_ue_tree_s, &RC.rrc[module_idP]->rrc_ue_head, ue_context_p); + } du_f1ap_itti_send_sctp_data_req(module_idP, f1ap_du_data->assoc_id, buffer, len, f1ap_du_data->default_sctp_stream_id); return 0; @@ -884,4 +919,631 @@ void init_f1ap_du_ue_inst (void) { memset(f1ap_du_inst, 0, sizeof(f1ap_du_inst)); } +int DU_send_UL_NR_RRC_MESSAGE_TRANSFER(instance_t instance, + const f1ap_ul_rrc_message_t *msg) { + const rnti_t rnti = msg->rnti; + F1AP_F1AP_PDU_t pdu; + F1AP_ULRRCMessageTransfer_t *out; + F1AP_ULRRCMessageTransferIEs_t *ie; + + uint8_t *buffer = NULL; + uint32_t len; + + + LOG_I(F1AP, "[DU %ld] %s: size %d UE RNTI %x in SRB %d\n", + instance, __func__, msg->rrc_container_length, rnti, msg->srb_id); + + //LOG_I(F1AP, "%s() RRCContainer size %d: ", __func__, msg->rrc_container_length); + //for (int i = 0;i < msg->rrc_container_length; i++) + // printf("%02x ", msg->rrc_container[i]); + //printf("\n"); + + /* Create */ + /* 0. Message Type */ + memset(&pdu, 0, sizeof(pdu)); + pdu.present = F1AP_F1AP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = (F1AP_InitiatingMessage_t *)calloc(1, sizeof(F1AP_InitiatingMessage_t)); + pdu.choice.initiatingMessage->procedureCode = F1AP_ProcedureCode_id_ULRRCMessageTransfer; + pdu.choice.initiatingMessage->criticality = F1AP_Criticality_ignore; + pdu.choice.initiatingMessage->value.present = F1AP_InitiatingMessage__value_PR_ULRRCMessageTransfer; + out = &pdu.choice.initiatingMessage->value.choice.ULRRCMessageTransfer; + + /* mandatory */ + /* c1. GNB_CU_UE_F1AP_ID */ + ie = (F1AP_ULRRCMessageTransferIEs_t *)calloc(1, sizeof(F1AP_ULRRCMessageTransferIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_GNB_CU_UE_F1AP_ID; + + ie->value.choice.GNB_CU_UE_F1AP_ID = f1ap_get_cu_ue_f1ap_id(&f1ap_du_inst[instance], rnti); + + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + /* mandatory */ + /* c2. GNB_DU_UE_F1AP_ID */ + ie = (F1AP_ULRRCMessageTransferIEs_t *)calloc(1, sizeof(F1AP_ULRRCMessageTransferIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_GNB_DU_UE_F1AP_ID; + ie->value.choice.GNB_DU_UE_F1AP_ID = f1ap_get_du_ue_f1ap_id(&f1ap_du_inst[instance], rnti); + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + /* mandatory */ + /* c3. SRBID */ + ie = (F1AP_ULRRCMessageTransferIEs_t *)calloc(1, sizeof(F1AP_ULRRCMessageTransferIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_SRBID; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_SRBID; + ie->value.choice.SRBID = msg->srb_id; + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + // issue in here + /* mandatory */ + /* c4. RRCContainer */ + ie = (F1AP_ULRRCMessageTransferIEs_t *)calloc(1, sizeof(F1AP_ULRRCMessageTransferIEs_t)); + ie->id = F1AP_ProtocolIE_ID_id_RRCContainer; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_RRCContainer; + OCTET_STRING_fromBuf(&ie->value.choice.RRCContainer, + (const char *) msg->rrc_container, + msg->rrc_container_length); + ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); + + if (msg->srb_id == 1 || msg->srb_id == 2) { + struct rrc_gNB_ue_context_s* ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], rnti); + + + NR_UL_DCCH_Message_t* ul_dcch_msg=NULL; + asn_dec_rval_t dec_rval; + dec_rval = uper_decode(NULL, + &asn_DEF_NR_UL_DCCH_Message, + (void**)&ul_dcch_msg, + &ie->value.choice.RRCContainer.buf[1], // buf[0] includes the pdcp header + msg->rrc_container_length, 0, 0); + + if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { + LOG_E(F1AP, " Failed to decode UL-DCCH (%zu bytes)\n",dec_rval.consumed); + /* for rfsim, because UE send RRCSetupRequest in SRB1 */ + // NR_UL_CCCH_Message_t *ul_ccch_msg; + // dec_rval = uper_decode(NULL, + // &asn_DEF_NR_UL_CCCH_Message, + // (void**)&ul_ccch_msg, + // &ie->value.choice.RRCContainer.buf[1], // buf[0] includes the pdcp header + // msg->rrc_container_length, 0, 0); + // if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { + // LOG_E(F1AP, " Failed to decode UL-CCCH (%zu bytes)\n",dec_rval.consumed); + // } else { + // LOG_I(F1AP, "decode UL-CCCH success \n"); + // LOG_I(F1AP, "Received message: present %d and c1 present %d\n", + // ul_ccch_msg->message.present, ul_ccch_msg->message.choice.c1->present); + + // if (ul_ccch_msg->message.present == NR_UL_CCCH_MessageType_PR_c1) { + // if (ul_ccch_msg->message.choice.c1->present == NR_UL_CCCH_MessageType__c1_PR_rrcSetupRequest) { + // LOG_I(F1AP, "[MSG] RRC Setup Request\n"); + + // } + // } + // } + } + else + LOG_I(F1AP, "Received message: present %d and c1 present %d\n", + ul_dcch_msg->message.present, ul_dcch_msg->message.choice.c1->present); + + if (ul_dcch_msg->message.present == NR_UL_DCCH_MessageType_PR_c1) { + + switch (ul_dcch_msg->message.choice.c1->present) { + case NR_UL_DCCH_MessageType__c1_PR_NOTHING: /* No components present */ + break; + + case NR_UL_DCCH_MessageType__c1_PR_measurementReport: + break; + + case NR_UL_DCCH_MessageType__c1_PR_rrcReconfigurationComplete: + LOG_I(F1AP, "[MSG] RRC UL rrcReconfigurationComplete\n"); + + /* CDRX: activated when RRC Connection Reconfiguration Complete is received */ +#if(0) + int UE_id_mac = find_nr_UE_id(instance, rnti); + + if (UE_id_mac == -1) { + LOG_E(F1AP, "Can't find UE_id(MAC) of UE rnti %x\n", rnti); + break; + } + + UE_sched_ctrl_t *UE_scheduling_control = &(RC.nrmac[instance]->UE_info.UE_sched_ctrl[UE_id_mac]); + + if (UE_scheduling_control->cdrx_waiting_ack == TRUE) { + UE_scheduling_control->cdrx_waiting_ack = FALSE; + UE_scheduling_control->cdrx_configured = TRUE; // Set to TRUE when RRC Connection Reconfiguration Complete is received + LOG_I(F1AP, "CDRX configuration activated after RRC Connection Reconfiguration Complete reception\n"); + } + /* End of CDRX processing */ +#endif + break; + + case NR_UL_DCCH_MessageType__c1_PR_rrcSetupComplete: + LOG_I(F1AP, "[MSG] RRC UL rrcSetupComplete \n"); + + if(!ue_context_p){ + LOG_E(F1AP, "Did not find the UE context associated with UE RNTOI %x, ue_context_p is NULL\n", rnti); + + } else { + LOG_I(F1AP, "Processing RRCSetupComplete UE %x\n", rnti); + ue_context_p->ue_context.StatusRrc = NR_RRC_CONNECTED; + } + break; + + case NR_UL_DCCH_MessageType__c1_PR_rrcReestablishmentComplete: + LOG_I(F1AP, "[MSG] RRC ReestablishmentComplete \n"); + break; + + case NR_UL_DCCH_MessageType__c1_PR_rrcResumeComplete: + LOG_I(F1AP, "[MSG] RRC ResumeComplete \n"); + break; + + case NR_UL_DCCH_MessageType__c1_PR_securityModeComplete: + LOG_I(F1AP, "[MSG] RRC securityModeComplete \n"); + break; + + case NR_UL_DCCH_MessageType__c1_PR_securityModeFailure: + LOG_I(F1AP, "[MSG] RRC securityModeFailure \n"); + break; + + case NR_UL_DCCH_MessageType__c1_PR_ulInformationTransfer: + LOG_I(F1AP, "[MSG] RRC UL Information Transfer \n"); + break; + + case NR_UL_DCCH_MessageType__c1_PR_locationMeasurementIndication: + break; + + case NR_UL_DCCH_MessageType__c1_PR_ueCapabilityInformation: + LOG_I(F1AP, "[MSG] RRC ueCapabilityInformation \n"); + break; + + case NR_UL_DCCH_MessageType__c1_PR_ueAssistanceInformation: + break; + + case NR_UL_DCCH_MessageType__c1_PR_failureInformation: + break; + + case NR_UL_DCCH_MessageType__c1_PR_scgFailureInformation: + break; + + case NR_UL_DCCH_MessageType__c1_PR_scgFailureInformationEUTRA: + break; + + default: + LOG_E(NR_RRC, "Unknown UL DCCH message type, present %d \n", ul_dcch_msg->message.choice.c1->present); + break; + } + } + } + /* encode */ + if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { + LOG_E(F1AP, "Failed to encode F1 UL RRC MESSAGE TRANSFER \n"); + return -1; + } + + du_f1ap_itti_send_sctp_data_req(instance, f1ap_du_data->assoc_id, buffer, len, f1ap_du_data->default_sctp_stream_id); + return 0; +} + +/* DL NR RRC Message Transfer */ +int DU_handle_DL_NR_RRC_MESSAGE_TRANSFER(instance_t instance, + uint32_t assoc_id, + uint32_t stream, + F1AP_F1AP_PDU_t *pdu) { + LOG_D(F1AP, "DU_handle_DL_NR_RRC_MESSAGE_TRANSFER \n"); + + F1AP_DLRRCMessageTransfer_t *container; + F1AP_DLRRCMessageTransferIEs_t *ie; + + uint64_t cu_ue_f1ap_id; + uint64_t du_ue_f1ap_id; + uint64_t srb_id; + int executeDuplication; + sdu_size_t rrc_dl_sdu_len; + //uint64_t subscriberProfileIDforRFP; + //uint64_t rAT_FrequencySelectionPriority; + + DevAssert(pdu != NULL); + + if (stream != 0) { + LOG_E(F1AP, "[SCTP %d] Received F1 on stream != 0 (%d)\n", + assoc_id, stream); + return -1; + } + + container = &pdu->choice.initiatingMessage->value.choice.DLRRCMessageTransfer; + + + /* GNB_CU_UE_F1AP_ID */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_DLRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); + cu_ue_f1ap_id = ie->value.choice.GNB_CU_UE_F1AP_ID; + LOG_D(F1AP, "cu_ue_f1ap_id %lu \n", cu_ue_f1ap_id); + + + /* GNB_DU_UE_F1AP_ID */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_DLRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true); + du_ue_f1ap_id = ie->value.choice.GNB_DU_UE_F1AP_ID; + LOG_D(F1AP, "du_ue_f1ap_id %lu associated with UE RNTI %x \n", + du_ue_f1ap_id, + f1ap_get_rnti_by_du_id(&f1ap_du_inst[instance], du_ue_f1ap_id)); // this should be the one transmitted via initial ul rrc message transfer + + if (f1ap_du_add_cu_ue_id(&f1ap_du_inst[instance],du_ue_f1ap_id, cu_ue_f1ap_id) < 0 ) { + LOG_E(F1AP, "Failed to find the F1AP UID \n"); + //return -1; + } + + /* optional */ + /* oldgNB_DU_UE_F1AP_ID */ + if (0) { + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_DLRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_oldgNB_DU_UE_F1AP_ID, true); + } + + /* mandatory */ + /* SRBID */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_DLRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_SRBID, true); + srb_id = ie->value.choice.SRBID; + LOG_D(F1AP, "srb_id %lu \n", srb_id); + + /* optional */ + /* ExecuteDuplication */ + if (0) { + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_DLRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_ExecuteDuplication, true); + executeDuplication = ie->value.choice.ExecuteDuplication; + LOG_D(F1AP, "ExecuteDuplication %d \n", executeDuplication); + } + + // issue in here + /* mandatory */ + /* RRC Container */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_DLRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_RRCContainer, true); + // BK: need check + // create an ITTI message and copy SDU + + // message_p = itti_alloc_new_message (TASK_CU_F1, RRC_MAC_CCCH_DATA_IND); + // memset (RRC_MAC_CCCH_DATA_IND (message_p).sdu, 0, CCCH_SDU_SIZE); + rrc_dl_sdu_len = ie->value.choice.RRCContainer.size; + // memcpy(RRC_MAC_CCCH_DATA_IND (message_p).sdu, ie->value.choice.RRCContainer.buf, + // ccch_sdu_len); + + //LOG_I(F1AP, "%s() RRCContainer size %lu: ", __func__, ie->value.choice.RRCContainer.size); + //for (int i = 0;i < ie->value.choice.RRCContainer.size; i++) + // printf("%02x ", ie->value.choice.RRCContainer.buf[i]); + //printf("\n"); + + /* optional */ + /* RAT_FrequencyPriorityInformation */ + if (0) { + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_DLRRCMessageTransferIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_RAT_FrequencyPriorityInformation, true); + + switch(ie->value.choice.RAT_FrequencyPriorityInformation.present) { + case F1AP_RAT_FrequencyPriorityInformation_PR_eNDC: + //subscriberProfileIDforRFP = ie->value.choice.RAT_FrequencyPriorityInformation.choice.subscriberProfileIDforRFP; + break; + case F1AP_RAT_FrequencyPriorityInformation_PR_nGRAN: + //rAT_FrequencySelectionPriority = ie->value.choice.RAT_FrequencyPriorityInformation.choice.rAT_FrequencySelectionPriority; + break; + default: + LOG_W(F1AP, "unhandled IE RAT_FrequencyPriorityInformation.present\n"); + break; + } + } + + // decode RRC Container and act on the message type + AssertFatal(srb_id<3,"illegal srb_id\n"); + + protocol_ctxt_t ctxt; + ctxt.rnti = f1ap_get_rnti_by_du_id(&f1ap_du_inst[instance], du_ue_f1ap_id); + ctxt.module_id = instance; + ctxt.instance = instance; + ctxt.enb_flag = 1; + + struct rrc_gNB_ue_context_s* ue_context_p = rrc_gNB_get_ue_context( + RC.nrrrc[ctxt.module_id], + ctxt.rnti); + + if (srb_id == 0) { + NR_DL_CCCH_Message_t* dl_ccch_msg=NULL; + asn_dec_rval_t dec_rval; + dec_rval = uper_decode(NULL, + &asn_DEF_NR_DL_CCCH_Message, + (void**)&dl_ccch_msg, + ie->value.choice.RRCContainer.buf, + rrc_dl_sdu_len,0,0); + AssertFatal(dec_rval.code == RC_OK, "could not decode F1AP message\n"); + switch (dl_ccch_msg->message.choice.c1->present) { + + case NR_DL_CCCH_MessageType__c1_PR_NOTHING: + + LOG_I(F1AP, "Received PR_NOTHING on DL-CCCH-Message\n"); + break; + + case NR_DL_CCCH_MessageType__c1_PR_rrcReject: + LOG_I(F1AP, + "Logical Channel DL-CCCH (SRB0), Received RRCReject\n"); + break; + + case NR_DL_CCCH_MessageType__c1_PR_rrcSetup: + { + LOG_I(F1AP, + "Logical Channel DL-CCCH (SRB0), Received RRCSetup DU_ID %lx/RNTI %x\n", + du_ue_f1ap_id, + f1ap_get_rnti_by_du_id(&f1ap_du_inst[instance], du_ue_f1ap_id)); + // Get configuration + + NR_RRCSetup_t* rrcSetup = dl_ccch_msg->message.choice.c1->choice.rrcSetup; + AssertFatal(rrcSetup!=NULL, "rrcSetup is null\n"); + NR_RRCSetup_IEs_t *rrcSetup_ies = rrcSetup->criticalExtensions.choice.rrcSetup; + + ue_context_p->ue_context.SRB_configList = rrcSetup_ies->radioBearerConfig.srb_ToAddModList; + AssertFatal(rrcSetup_ies->masterCellGroup.buf!=NULL,"masterCellGroup is null\n"); + asn_dec_rval_t dec_rval; + dec_rval = uper_decode(NULL, + &asn_DEF_NR_CellGroupConfig, + (void**)&ue_context_p->ue_context.masterCellGroup, + rrcSetup_ies->masterCellGroup.buf, + rrcSetup_ies->masterCellGroup.size,0,0); + AssertFatal(dec_rval.code == RC_OK, "could not decode masterCellGroup\n"); + + // configure MAC + gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id]; + rrc_mac_config_req_gNB(ctxt.module_id, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, + NULL, + 0, + ue_context_p->ue_context.rnti, + ue_context_p->ue_context.masterCellGroup + ); + + // rrc_rlc_config_asn1_req + nr_rrc_rlc_config_asn1_req(&ctxt, + ue_context_p->ue_context.SRB_configList, + NULL, + NULL, + NULL, + NULL, + NULL); + + // This should be somewhere in the f1ap_cudu_ue_inst_t + /*int macrlc_instance = 0; + + rnti_t rnti = f1ap_get_rnti_by_du_id(&f1ap_du_inst[0], du_ue_f1ap_id); + struct rrc_gNB_ue_context_s *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[macrlc_instance],rnti); + */ + gNB_RRC_UE_t *ue_p = &ue_context_p->ue_context; + AssertFatal(ue_p->Srb0.Active == 1,"SRB0 is not active\n"); + + memcpy((void*)ue_p->Srb0.Tx_buffer.Payload, + (void*)ie->value.choice.RRCContainer.buf, + rrc_dl_sdu_len); // ie->value.choice.RRCContainer.size + + ue_p->Srb0.Tx_buffer.payload_size = rrc_dl_sdu_len; + + // NR_MAC_CellGroupConfig_t *mac_CellGroupConfig = NULL; + // if (cellGroupConfig->mac_CellGroupConfig) + // mac_CellGroupConfig = cellGroupConfig->mac_CellGroupConfig; + + // rrc_mac_config_req_gNB + break; + } // case + + case NR_DL_CCCH_MessageType__c1_PR_spare2: + LOG_I(F1AP, + "Logical Channel DL-CCCH (SRB0), Received spare2\n"); + break; + + case NR_DL_CCCH_MessageType__c1_PR_spare1: + LOG_I(F1AP, + "Logical Channel DL-CCCH (SRB0), Received spare1\n"); + break; + + default: + AssertFatal(1==0, + "Unknown message\n"); + break; + }// switch case + return(0); + } else if (srb_id == 1) { + + NR_DL_DCCH_Message_t* dl_dcch_msg=NULL; + asn_dec_rval_t dec_rval; + dec_rval = uper_decode(NULL, + &asn_DEF_NR_DL_DCCH_Message, + (void**)&dl_dcch_msg, + &ie->value.choice.RRCContainer.buf[2], // buf[0] includes the pdcp header + rrc_dl_sdu_len-6,0,0); + + if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) + LOG_E(F1AP," Failed to decode DL-DCCH (%zu bytes)\n",dec_rval.consumed); + else + LOG_D(F1AP, "Received message: present %d and c1 present %d\n", + dl_dcch_msg->message.present, dl_dcch_msg->message.choice.c1->present); + + if (dl_dcch_msg->message.present == NR_DL_DCCH_MessageType_PR_c1) { + switch (dl_dcch_msg->message.choice.c1->present) { + case NR_DL_DCCH_MessageType__c1_PR_NOTHING: + LOG_I(F1AP, "Received PR_NOTHING on DL-DCCH-Message\n"); + return 0; + + case NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration: + // handle RRCReconfiguration + LOG_I(F1AP, + "Logical Channel DL-DCCH (SRB1), Received RRCReconfiguration DU_ID %lx/RNTI %x\n", + du_ue_f1ap_id, + f1ap_get_rnti_by_du_id(&f1ap_du_inst[instance], du_ue_f1ap_id)); + + NR_RRCReconfiguration_t* rrcReconfiguration = dl_dcch_msg->message.choice.c1->choice.rrcReconfiguration; + + if (rrcReconfiguration->criticalExtensions.present == NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration) { + NR_RRCReconfiguration_IEs_t* rrcReconfiguration_ies = + rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration; + + if (rrcReconfiguration_ies->measConfig != NULL) { + LOG_I(F1AP, "Measurement Configuration is present\n"); + } + + if (rrcReconfiguration_ies->radioBearerConfig) { + LOG_I(F1AP, "Radio Resource Configuration is present\n"); + long drb_id; + int i; + NR_DRB_ToAddModList_t *DRB_configList = rrcReconfiguration_ies->radioBearerConfig->drb_ToAddModList; + NR_SRB_ToAddModList_t *SRB_configList = rrcReconfiguration_ies->radioBearerConfig->srb_ToAddModList; + // NR_DRB_ToReleaseList_t *DRB_ReleaseList = rrcReconfiguration_ies->radioBearerConfig->drb_ToReleaseList; + + // rrc_rlc_config_asn1_req + + if (SRB_configList != NULL) { + for (i = 0; (i < SRB_configList->list.count) && (i < 3); i++) { + if (SRB_configList->list.array[i]->srb_Identity == 1 ){ + ue_context_p->ue_context.Srb1.Active=1; + } + else if (SRB_configList->list.array[i]->srb_Identity == 2 ) { + ue_context_p->ue_context.Srb2.Active=1; + ue_context_p->ue_context.Srb2.Srb_info.Srb_id=2; + LOG_I(F1AP, "[DU %d] SRB2 is now active\n",ctxt.module_id); + } else { + LOG_W(F1AP, "[DU %d] invalide SRB identity %ld\n",ctxt.module_id, + SRB_configList->list.array[i]->srb_Identity); + } + } + } + + if (DRB_configList != NULL) { + for (i = 0; i < DRB_configList->list.count; i++) { // num max DRB (11-3-8) + if (DRB_configList->list.array[i]) { + drb_id = (int)DRB_configList->list.array[i]->drb_Identity; + LOG_I(F1AP, + "[DU %d] Logical Channel UL-DCCH, Received RRCConnectionReconfiguration for UE rnti %x, reconfiguring DRB %d\n", + ctxt.module_id, + ctxt.rnti, + (int)DRB_configList->list.array[i]->drb_Identity); + // (int)*DRB_configList->list.array[i]->logicalChannelIdentity); + + if (ue_context_p->ue_context.DRB_active[drb_id] == 0) { + ue_context_p->ue_context.DRB_active[drb_id] = 1; + + // logicalChannelIdentity + // rrc_mac_config_req_eNB + } + + } else { // remove LCHAN from MAC/PHY + AssertFatal(1==0,"Can't handle this yet in DU\n"); + } + } + } + } + } + break; + case NR_DL_DCCH_MessageType__c1_PR_rrcResume: + LOG_I(F1AP,"Received rrcResume\n"); + break; + case NR_DL_DCCH_MessageType__c1_PR_rrcRelease: + LOG_I(F1AP,"Received rrcRelease\n"); + break; + case NR_DL_DCCH_MessageType__c1_PR_rrcReestablishment: + LOG_I(F1AP,"Received rrcReestablishment\n"); + break; + case NR_DL_DCCH_MessageType__c1_PR_securityModeCommand: + LOG_I(F1AP,"Received securityModeCommand\n"); + break; + case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransfer: + LOG_I(F1AP, "Received dlInformationTransfer\n"); + break; + case NR_DL_DCCH_MessageType__c1_PR_ueCapabilityEnquiry: + LOG_I(F1AP, "Received ueCapabilityEnquiry\n"); + break; + case NR_DL_DCCH_MessageType__c1_PR_counterCheck: + LOG_I(F1AP, "Received counterCheck\n"); + break; + case NR_DL_DCCH_MessageType__c1_PR_mobilityFromNRCommand: + case NR_DL_DCCH_MessageType__c1_PR_dlDedicatedMessageSegment_r16: + case NR_DL_DCCH_MessageType__c1_PR_ueInformationRequest_r16: + case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransferMRDC_r16: + case NR_DL_DCCH_MessageType__c1_PR_loggedMeasurementConfiguration_r16: + case NR_DL_DCCH_MessageType__c1_PR_spare3: + case NR_DL_DCCH_MessageType__c1_PR_spare2: + case NR_DL_DCCH_MessageType__c1_PR_spare1: + break; + } + } + } + else if (srb_id == 2) { + // TODO + } + + LOG_I(F1AP, "Received DL RRC Transfer on srb_id %ld\n", srb_id); + + +// rlc_op_status_t rlc_status; +// boolean_t ret = TRUE; + mem_block_t *pdcp_pdu_p = NULL; + pdcp_pdu_p = get_free_mem_block(rrc_dl_sdu_len, __func__); + + //LOG_I(F1AP, "PRRCContainer size %lu:", ie->value.choice.RRCContainer.size); + //for (int i = 0; i < ie->value.choice.RRCContainer.size; i++) + // printf("%02x ", ie->value.choice.RRCContainer.buf[i]); + + //printf (", PDCP PDU size %d:", rrc_dl_sdu_len); + //for (int i=0;idata[i]); + //printf("\n"); + + if (pdcp_pdu_p != NULL) { + memset(pdcp_pdu_p->data, 0, rrc_dl_sdu_len); + memcpy(&pdcp_pdu_p->data[0], ie->value.choice.RRCContainer.buf, rrc_dl_sdu_len); + + /* for rfsim */ + du_rlc_data_req(&ctxt, 1, 0x00, 1, 1, 0, rrc_dl_sdu_len, pdcp_pdu_p); + // rlc_status = rlc_data_req(&ctxt + // , 1 + // , MBMS_FLAG_NO + // , srb_id + // , 0 + // , 0 + // , rrc_dl_sdu_len + // , pdcp_pdu_p + // ,NULL + // ,NULL + // ); + // switch (rlc_status) { + // case RLC_OP_STATUS_OK: + // //LOG_I(F1AP, "Data sending request over RLC succeeded!\n"); + // ret=TRUE; + // break; + + // case RLC_OP_STATUS_BAD_PARAMETER: + // LOG_W(F1AP, "Data sending request over RLC failed with 'Bad Parameter' reason!\n"); + // ret= FALSE; + // break; + + // case RLC_OP_STATUS_INTERNAL_ERROR: + // LOG_W(F1AP, "Data sending request over RLC failed with 'Internal Error' reason!\n"); + // ret= FALSE; + // break; + + // case RLC_OP_STATUS_OUT_OF_RESSOURCES: + // LOG_W(F1AP, "Data sending request over RLC failed with 'Out of Resources' reason!\n"); + // ret= FALSE; + // break; + + // default: + // LOG_W(F1AP, "RLC returned an unknown status code after PDCP placed the order to send some data (Status Code:%d)\n", rlc_status); + // ret= FALSE; + // break; + // } // switch case + // return ret; + } // if pdcp_pdu_p + + return 0; +} diff --git a/openair2/F1AP/f1ap_du_rrc_message_transfer.h b/openair2/F1AP/f1ap_du_rrc_message_transfer.h index 1406e48f3d..3036c66d70 100644 --- a/openair2/F1AP/f1ap_du_rrc_message_transfer.h +++ b/openair2/F1AP/f1ap_du_rrc_message_transfer.h @@ -42,12 +42,15 @@ int DU_handle_DL_RRC_MESSAGE_TRANSFER(instance_t instance, F1AP_F1AP_PDU_t *pdu); int DU_send_UL_RRC_MESSAGE_TRANSFER(instance_t instance, const f1ap_ul_rrc_message_t *msg); +int DU_send_UL_NR_RRC_MESSAGE_TRANSFER(instance_t instance, const f1ap_ul_rrc_message_t *msg); int DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(module_id_t module_idP, int CC_idP, int UE_id, rnti_t rntiP, const uint8_t *sduP, - sdu_size_t sdu_lenP); + sdu_size_t sdu_lenP, + const int8_t *sdu2P, + sdu_size_t sdu2_lenP); #endif /* F1AP_DU_RRC_MESSAGE_TRANSFER_H_ */ diff --git a/openair2/F1AP/f1ap_du_task.c b/openair2/F1AP/f1ap_du_task.c index c8aa4a6119..50f7cdff80 100644 --- a/openair2/F1AP/f1ap_du_task.c +++ b/openair2/F1AP/f1ap_du_task.c @@ -102,7 +102,12 @@ void du_task_handle_sctp_association_resp(instance_t instance, sctp_new_associat f1ap_du_data->default_sctp_stream_id = 0; /* setup parameters for F1U and start the server */ - const cudu_params_t params = { + const cudu_params_t params = (RC.nrrrc && RC.nrrrc[instance]->node_type == ngran_gNB_DU) ? (cudu_params_t){ + .local_ipv4_address = RC.nrmac[instance]->eth_params_n.my_addr, + .local_port = RC.nrmac[instance]->eth_params_n.my_portd, + .remote_ipv4_address = RC.nrmac[instance]->eth_params_n.remote_addr, + .remote_port = RC.nrmac[instance]->eth_params_n.remote_portd + } : (cudu_params_t){ .local_ipv4_address = RC.mac[instance]->eth_params_n.my_addr, .local_port = RC.mac[instance]->eth_params_n.my_portd, .remote_ipv4_address = RC.mac[instance]->eth_params_n.remote_addr, @@ -159,6 +164,14 @@ void *F1AP_DU_task(void *arg) { du_task_send_sctp_association_req(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_SETUP_REQ(received_msg)); break; + case F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE: + DU_send_gNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(ITTI_MSG_DESTINATION_INSTANCE(received_msg), + &F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(received_msg)); + break; + case F1AP_GNB_CU_CONFIGURATION_UPDATE_FAILURE: + DU_send_gNB_CU_CONFIGURATION_UPDATE_FAILURE(ITTI_MSG_DESTINATION_INSTANCE(received_msg), + &F1AP_GNB_CU_CONFIGURATION_UPDATE_FAILURE(received_msg)); + break; case SCTP_NEW_ASSOCIATION_RESP: // 1. store the respon @@ -175,10 +188,26 @@ void *F1AP_DU_task(void *arg) { &received_msg->ittiMsg.sctp_data_ind); break; + case F1AP_INITIAL_UL_RRC_MESSAGE: // to rrc + LOG_I(F1AP, "DU Task Received F1AP_INITIAL_UL_RRC_MESSAGE\n"); + + f1ap_initial_ul_rrc_message_t *msg = &F1AP_INITIAL_UL_RRC_MESSAGE(received_msg); + DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(0,0,0,msg->crnti, + msg->rrc_container, + msg->rrc_container_length, + (const int8_t*)msg->du2cu_rrc_container, + msg->du2cu_rrc_container_length); + break; + case F1AP_UL_RRC_MESSAGE: // to rrc LOG_I(F1AP, "DU Task Received F1AP_UL_RRC_MESSAGE\n"); - DU_send_UL_RRC_MESSAGE_TRANSFER(ITTI_MSG_DESTINATION_INSTANCE(received_msg), - &F1AP_UL_RRC_MESSAGE(received_msg)); + if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_DU) { + DU_send_UL_NR_RRC_MESSAGE_TRANSFER(ITTI_MSG_DESTINATION_INSTANCE(received_msg), + &F1AP_UL_RRC_MESSAGE(received_msg)); + } else { + DU_send_UL_RRC_MESSAGE_TRANSFER(ITTI_MSG_DESTINATION_INSTANCE(received_msg), + &F1AP_UL_RRC_MESSAGE(received_msg)); + } break; case F1AP_UE_CONTEXT_RELEASE_REQ: // from MAC diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c index bfd0e8e9af..b79e9feb58 100644 --- a/openair2/F1AP/f1ap_du_ue_context_management.c +++ b/openair2/F1AP/f1ap_du_ue_context_management.c @@ -108,51 +108,82 @@ int DU_handle_UE_CONTEXT_SETUP_REQUEST(instance_t instance, f1ap_ue_context_setup_req->cellULConfigured = NULL; } - /* CUtoDURRCInformation */ - /* Candidate_SpCell_List */ - /* optional */ - /* DRXCycle */ - /* optional */ - /* ResourceCoordinationTransferContainer */ - /* SCell_ToBeSetup_List */ - /* SRBs_ToBeSetup_List */ - /* DRBs_ToBeSetup_List */ - /* Decode DRBs_ToBeSetup_List */ - F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupRequestIEs_t, ie, container, - F1AP_ProtocolIE_ID_id_DRBs_ToBeSetup_List, true); - f1ap_ue_context_setup_req->drbs_to_be_setup_length = ie->value.choice.DRBs_ToBeSetup_List.list.count; - f1ap_ue_context_setup_req->drbs_to_be_setup = calloc(f1ap_ue_context_setup_req->drbs_to_be_setup_length, - sizeof(f1ap_drb_to_be_setup_t)); - AssertFatal(f1ap_ue_context_setup_req->drbs_to_be_setup, - "could not allocate memory for f1ap_ue_context_setup_req->drbs_to_be_setup\n"); + if (RC.nrrrc) { + /* RRCContainer */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupRequestIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_RRCContainer, false); + if (ie) { + /* correct here */ + f1ap_ue_context_setup_req->rrc_container = malloc(ie->value.choice.RRCContainer.size); + memcpy(f1ap_ue_context_setup_req->rrc_container, ie->value.choice.RRCContainer.buf, ie->value.choice.RRCContainer.size); + } else { + LOG_E(F1AP, "can't find RRCContainer in UEContextSetupRequestIEs by id %ld \n", F1AP_ProtocolIE_ID_id_RRCContainer); + } - for (i = 0; i < f1ap_ue_context_setup_req->drbs_to_be_setup_length; ++i) { - f1ap_drb_to_be_setup_t *drb_p = &f1ap_ue_context_setup_req->drbs_to_be_setup[i]; - F1AP_DRBs_ToBeSetup_Item_t *drbs_tobesetup_item_p; - drbs_tobesetup_item_p = &((F1AP_DRBs_ToBeSetup_ItemIEs_t *)ie->value.choice.DRBs_ToBeSetup_List.list.array[i])->value.choice.DRBs_ToBeSetup_Item; - drb_p->drb_id = drbs_tobesetup_item_p->dRBID; - /* TODO in the following, assume only one UP UL TNL is present. - * this matches/assumes OAI CU implementation, can be up to 2! */ - drb_p->up_ul_tnl_length = 1; - AssertFatal(drbs_tobesetup_item_p->uLUPTNLInformation_ToBeSetup_List.list.count > 0, - "no UL UP TNL Information in DRBs to be Setup list\n"); - F1AP_ULUPTNLInformation_ToBeSetup_Item_t *ul_up_tnl_info_p = (F1AP_ULUPTNLInformation_ToBeSetup_Item_t *)drbs_tobesetup_item_p->uLUPTNLInformation_ToBeSetup_List.list.array[0]; - F1AP_GTPTunnel_t *ul_up_tnl0 = ul_up_tnl_info_p->uLUPTNLInformation.choice.gTPTunnel; - BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&ul_up_tnl0->transportLayerAddress, drb_p->up_ul_tnl[0].tl_address); - OCTET_STRING_TO_INT32(&ul_up_tnl0->gTP_TEID, drb_p->up_ul_tnl[0].gtp_teid); + // AssertFatal(0, "check configuration, send to appropriate handler\n"); - switch (drbs_tobesetup_item_p->rLCMode) { - case F1AP_RLCMode_rlc_am: - drb_p->rlc_mode = RLC_MODE_AM; - break; + protocol_ctxt_t ctxt; + // ctxt.rnti = f1ap_get_rnti_by_du_id(&f1ap_du_inst[instance], ie->value.choice.GNB_DU_UE_F1AP_ID); + ctxt.rnti = 0x1234; + ctxt.module_id = instance; + ctxt.instance = instance; + ctxt.enb_flag = 1; - default: - drb_p->rlc_mode = RLC_MODE_TM; - break; + mem_block_t *pdcp_pdu_p = NULL; + pdcp_pdu_p = get_free_mem_block(ie->value.choice.RRCContainer.size, __func__); + if (pdcp_pdu_p != NULL) { + memset(pdcp_pdu_p->data, 0, ie->value.choice.RRCContainer.size); + memcpy(&pdcp_pdu_p->data[0], ie->value.choice.RRCContainer.buf, ie->value.choice.RRCContainer.size); + + /* for rfsim */ + du_rlc_data_req(&ctxt, 1, 0x00, 1, 1, 0, ie->value.choice.RRCContainer.size, pdcp_pdu_p); + } + } else { + /* CUtoDURRCInformation */ + /* Candidate_SpCell_List */ + /* optional */ + /* DRXCycle */ + /* optional */ + /* ResourceCoordinationTransferContainer */ + /* SCell_ToBeSetup_List */ + /* SRBs_ToBeSetup_List */ + /* DRBs_ToBeSetup_List */ + /* Decode DRBs_ToBeSetup_List */ + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupRequestIEs_t, ie, container, + F1AP_ProtocolIE_ID_id_DRBs_ToBeSetup_List, true); + f1ap_ue_context_setup_req->drbs_to_be_setup_length = ie->value.choice.DRBs_ToBeSetup_List.list.count; + f1ap_ue_context_setup_req->drbs_to_be_setup = calloc(f1ap_ue_context_setup_req->drbs_to_be_setup_length, + sizeof(f1ap_drb_to_be_setup_t)); + AssertFatal(f1ap_ue_context_setup_req->drbs_to_be_setup, + "could not allocate memory for f1ap_ue_context_setup_req->drbs_to_be_setup\n"); + + for (i = 0; i < f1ap_ue_context_setup_req->drbs_to_be_setup_length; ++i) { + f1ap_drb_to_be_setup_t *drb_p = &f1ap_ue_context_setup_req->drbs_to_be_setup[i]; + F1AP_DRBs_ToBeSetup_Item_t *drbs_tobesetup_item_p; + drbs_tobesetup_item_p = &((F1AP_DRBs_ToBeSetup_ItemIEs_t *)ie->value.choice.DRBs_ToBeSetup_List.list.array[i])->value.choice.DRBs_ToBeSetup_Item; + drb_p->drb_id = drbs_tobesetup_item_p->dRBID; + /* TODO in the following, assume only one UP UL TNL is present. + * this matches/assumes OAI CU implementation, can be up to 2! */ + drb_p->up_ul_tnl_length = 1; + AssertFatal(drbs_tobesetup_item_p->uLUPTNLInformation_ToBeSetup_List.list.count > 0, + "no UL UP TNL Information in DRBs to be Setup list\n"); + F1AP_ULUPTNLInformation_ToBeSetup_Item_t *ul_up_tnl_info_p = (F1AP_ULUPTNLInformation_ToBeSetup_Item_t *)drbs_tobesetup_item_p->uLUPTNLInformation_ToBeSetup_List.list.array[0]; + F1AP_GTPTunnel_t *ul_up_tnl0 = ul_up_tnl_info_p->uLUPTNLInformation.choice.gTPTunnel; + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&ul_up_tnl0->transportLayerAddress, drb_p->up_ul_tnl[0].tl_address); + OCTET_STRING_TO_INT32(&ul_up_tnl0->gTP_TEID, drb_p->up_ul_tnl[0].gtp_teid); + + switch (drbs_tobesetup_item_p->rLCMode) { + case F1AP_RLCMode_rlc_am: + drb_p->rlc_mode = RLC_MODE_AM; + break; + + default: + drb_p->rlc_mode = RLC_MODE_TM; + break; + } } } - AssertFatal(0, "check configuration, send to appropriate handler\n"); return 0; } @@ -511,7 +542,7 @@ int DU_send_UE_CONTEXT_SETUP_RESPONSE(instance_t instance) { /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 UE CONTEXT SETUP RESPONSE\n"); return -1; } @@ -1181,7 +1212,7 @@ int DU_send_UE_CONTEXT_MODIFICATION_RESPONSE(instance_t instance) { /* encode */ if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1 setup request\n"); + LOG_E(F1AP, "Failed to encode F1 UE CONTEXT MODIFICATION RESPONSE\n"); return -1; } diff --git a/openair2/F1AP/f1ap_handlers.c b/openair2/F1AP/f1ap_handlers.c index 45f76d3dab..5aca059ae4 100644 --- a/openair2/F1AP/f1ap_handlers.c +++ b/openair2/F1AP/f1ap_handlers.c @@ -50,7 +50,7 @@ f1ap_message_decoded_callback f1ap_messages_callback[][3] = { { CU_handle_F1_SETUP_REQUEST, DU_handle_F1_SETUP_RESPONSE, DU_handle_F1_SETUP_FAILURE }, /* F1Setup */ { 0, 0, 0 }, /* ErrorIndication */ { 0, 0, 0 }, /* gNBDUConfigurationUpdate */ - { 0, 0, 0 }, /* gNBCUConfigurationUpdate */ + { DU_handle_gNB_CU_CONFIGURATION_UPDATE, CU_handle_gNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE, CU_handle_gNB_CU_CONFIGURATION_UPDATE_FAILURE }, /* gNBCUConfigurationUpdate */ { DU_handle_UE_CONTEXT_SETUP_REQUEST, CU_handle_UE_CONTEXT_SETUP_RESPONSE, 0 }, /* UEContextSetup */ { DU_handle_UE_CONTEXT_RELEASE_COMMAND, CU_handle_UE_CONTEXT_RELEASE_COMPLETE, 0 }, /* UEContextRelease */ { 0, 0, 0 }, /* UEContextModification */ diff --git a/openair2/GNB_APP/L1_nr_paramdef.h b/openair2/GNB_APP/L1_nr_paramdef.h index 88f2cb1615..fd22e92730 100644 --- a/openair2/GNB_APP/L1_nr_paramdef.h +++ b/openair2/GNB_APP/L1_nr_paramdef.h @@ -47,7 +47,9 @@ #define CONFIG_STRING_L1_REMOTE_N_PORTD "remote_n_portd" #define CONFIG_STRING_L1_TRANSPORT_N_PREFERENCE "tr_n_preference" #define CONFIG_STRING_L1_PUSCH_PROC_THREADS "pusch_proc_threads" - +#define CONFIG_STRING_L1_PUCCH0_DTX_THRESHOLD "pucch0_dtx_threshold" +#define CONFIG_STRING_L1_PRACH_DTX_THRESHOLD "prach_dtx_threshold" +#define CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD "pusch_dtx_threshold" /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ /* L1 configuration parameters */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ @@ -62,7 +64,10 @@ {CONFIG_STRING_L1_REMOTE_N_PORTC, NULL, 0, uptr:NULL, defintval:50030, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_LOCAL_N_PORTD, NULL, 0, uptr:NULL, defintval:50031, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_REMOTE_N_PORTD, NULL, 0, uptr:NULL, defintval:50031, TYPE_UINT, 0}, \ -{CONFIG_STRING_L1_PUSCH_PROC_THREADS, NULL, 0, uptr:NULL, defintval:3, TYPE_UINT, 0} \ +{CONFIG_STRING_L1_PUSCH_PROC_THREADS, NULL, 0, uptr:NULL, defintval:1, TYPE_UINT, 0}, \ +{CONFIG_STRING_L1_PUCCH0_DTX_THRESHOLD, NULL, 0, uptr:NULL, defintval:100, TYPE_UINT, 0}, \ +{CONFIG_STRING_L1_PRACH_DTX_THRESHOLD, NULL, 0, uptr:NULL, defintval:200, TYPE_UINT, 0}, \ +{CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD, NULL, 0, uptr:NULL, defintval:50, TYPE_UINT, 0} \ } #define L1_CC_IDX 0 #define L1_TRANSPORT_N_PREFERENCE_IDX 1 @@ -74,6 +79,9 @@ #define L1_LOCAL_N_PORTD_IDX 7 #define L1_REMOTE_N_PORTD_IDX 8 #define L1_PUSCH_PROC_THREADS 9 +#define L1_PUCCH0_DTX_THRESHOLD 10 +#define L1_PRACH_DTX_THRESHOLD 11 +#define L1_PUSCH_DTX_THRESHOLD 12 /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ #endif diff --git a/openair2/GNB_APP/MACRLC_nr_paramdef.h b/openair2/GNB_APP/MACRLC_nr_paramdef.h index 60b40cb349..bf8b32f591 100644 --- a/openair2/GNB_APP/MACRLC_nr_paramdef.h +++ b/openair2/GNB_APP/MACRLC_nr_paramdef.h @@ -56,6 +56,10 @@ #define CONFIG_STRING_MACRLC_LOCAL_S_PORTD "local_s_portd" #define CONFIG_STRING_MACRLC_REMOTE_S_PORTD "remote_s_portd" #define CONFIG_STRING_MACRLC_ULSCH_MAX_SLOTS_INACTIVITY "ulsch_max_slots_inactivity" +#define CONFIG_STRING_MACRLC_PUSCHTARGETSNRX10 "pusch_TargetSNRx10" +#define CONFIG_STRING_MACRLC_PUCCHTARGETSNRX10 "pucch_TargetSNRx10" +#define CONFIG_STRING_MACRLC_PUCCHFAILURETHRES "pucch_FailureThres" +#define CONFIG_STRING_MACRLC_PUSCHFAILURETHRES "pusch_FailureThres" /*-------------------------------------------------------------------------------------------------------------------------------------------------------*/ /* MacRLC configuration parameters */ @@ -80,6 +84,10 @@ {CONFIG_STRING_MACRLC_LOCAL_S_PORTD, NULL, 0, uptr:NULL, defintval:50021, TYPE_UINT, 0}, \ {CONFIG_STRING_MACRLC_REMOTE_S_PORTD, NULL, 0, uptr:NULL, defintval:50021, TYPE_UINT, 0}, \ {CONFIG_STRING_MACRLC_ULSCH_MAX_SLOTS_INACTIVITY, "Maximum number of slots before a UE is scheduled ULSCH due to inactivity", 0, uptr:NULL, defintval:200, TYPE_UINT, 0}, \ +{CONFIG_STRING_MACRLC_PUSCHTARGETSNRX10, NULL, 0, iptr:NULL, defintval:200, TYPE_INT, 0}, \ +{CONFIG_STRING_MACRLC_PUCCHTARGETSNRX10, NULL, 0, iptr:NULL, defintval:150, TYPE_INT, 0}, \ +{CONFIG_STRING_MACRLC_PUCCHFAILURETHRES, NULL, 0, iptr:NULL, defintval:10, TYPE_INT, 0}, \ +{CONFIG_STRING_MACRLC_PUSCHFAILURETHRES, NULL, 0, iptr:NULL, defintval:10, TYPE_INT, 0}, \ } #define MACRLC_CC_IDX 0 #define MACRLC_TRANSPORT_N_PREFERENCE_IDX 1 @@ -99,5 +107,9 @@ #define MACRLC_LOCAL_S_PORTD_IDX 15 #define MACRLC_REMOTE_S_PORTD_IDX 16 #define MACRLC_ULSCH_MAX_SLOTS_INACTIVITY 17 +#define MACRLC_PUSCHTARGETSNRX10_IDX 17 +#define MACRLC_PUCCHTARGETSNRX10_IDX 18 +#define MACRLC_PUCCHFAILURETHRES_IDX 19 +#define MACRLC_PUSCHFAILURETHRES_IDX 20 /*---------------------------------------------------------------------------------------------------------------------------------------------------------*/ #endif diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index a410bcac6a..c1cb4afdd9 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -141,6 +141,7 @@ #define GNB_CONFIG_STRING_PUCCHGROUPHOPPING "pucchGroupHopping" #define GNB_CONFIG_STRING_HOPPINGID "hoppingId" #define GNB_CONFIG_STRING_P0NOMINAL "p0_nominal" +#define GNB_CONFIG_STRING_PUCCHRES "pucch_ResourceCommon" #define GNB_CONFIG_STRING_INITIALULBWPK2_0 "initialULBWPk2_0" #define GNB_CONFIG_STRING_INITIALULBWPMAPPINGTYPE_0 "initialULBWPmappingType_0" #define GNB_CONFIG_STRING_INITIALULBWPSTARTSYMBOLANDLENGTH_0 "initialULBWPstartSymbolAndLength_0" @@ -413,6 +414,7 @@ {GNB_CONFIG_STRING_PUCCHGROUPHOPPING, NULL,0,i64ptr:&scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup->pucch_GroupHopping,defint64val:NR_PUCCH_ConfigCommon__pucch_GroupHopping_neither,TYPE_INT64,0},\ {GNB_CONFIG_STRING_HOPPINGID, NULL,0,i64ptr:scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup->hoppingId,defint64val:40,TYPE_INT64,0},\ {GNB_CONFIG_STRING_P0NOMINAL, NULL,0,i64ptr:scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup->p0_nominal,defint64val:1,TYPE_INT64,0},\ +{GNB_CONFIG_STRING_PUCCHRES, NULL,0,i64ptr:scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup->pucch_ResourceCommon,defint64val:0,TYPE_INT64,0},\ {GNB_CONFIG_STRING_SSBPOSITIONSINBURSTPR,NULL,0,uptr:&scc->ssb_PositionsInBurst->present,defuintval:NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_mediumBitmap,TYPE_UINT,0/*140*/}, \ {GNB_CONFIG_STRING_SSBPOSITIONSINBURST,NULL,0,u64ptr:&ssb_bitmap,defintval:0xff,TYPE_UINT64,0}, \ {GNB_CONFIG_STRING_REFERENCESUBCARRIERSPACING,NULL,0,i64ptr:&scc->tdd_UL_DL_ConfigurationCommon->referenceSubcarrierSpacing,defint64val:NR_SubcarrierSpacing_kHz30,TYPE_INT64,0},\ diff --git a/openair2/GNB_APP/gnb_app.c b/openair2/GNB_APP/gnb_app.c index b525e7edb7..66302d056b 100644 --- a/openair2/GNB_APP/gnb_app.c +++ b/openair2/GNB_APP/gnb_app.c @@ -29,6 +29,10 @@ #include #include +#include +#include +#include +#include #include "gnb_app.h" #include "gnb_config.h" @@ -43,7 +47,9 @@ #include "sctp_eNB_task.h" #include "gtpv1u_eNB_task.h" #include "PHY/INIT/phy_init.h" - +#include "f1ap_cu_task.h" +#include "f1ap_du_task.h" +#include "nfapi/oai_integration/vendor_ext.h" extern unsigned char NB_gNB_INST; extern RAN_CONTEXT_t RC; @@ -61,7 +67,7 @@ static void configure_nr_rrc(uint32_t gnb_id) if (RC.nrrrc[gnb_id]) { RCconfig_NRRRC(msg_p,gnb_id, RC.nrrrc[gnb_id]); - + LOG_I(GNB_APP, "RRC starting with node type %d\n", RC.nrrrc[gnb_id]->node_type); LOG_I(GNB_APP,"Sending configuration message to NR_RRC task\n"); itti_send_msg_to_task (TASK_RRC_GNB, GNB_MODULE_ID_TO_INSTANCE(gnb_id), msg_p); @@ -82,26 +88,25 @@ static uint32_t gNB_app_register(uint32_t gnb_id_start, uint32_t gnb_id_end)//, { if(NGAP_CONF_MODE){ ngap_register_gnb_req_t *ngap_register_gNB; //Type Temporarily reuse - - // note: there is an implicit relationship between the data structure and the message name + + // note: there is an implicit relationship between the data structure and the message name msg_p = itti_alloc_new_message (TASK_GNB_APP, 0, NGAP_REGISTER_GNB_REQ); //Message Temporarily reuse RCconfig_NR_NG(msg_p, gnb_id); - + ngap_register_gNB = &NGAP_REGISTER_GNB_REQ(msg_p); //Message Temporarily reuse - LOG_I(GNB_APP,"default drx %d\n",ngap_register_gNB->default_drx); - - itti_send_msg_to_task (TASK_NGAP, GNB_MODULE_ID_TO_INSTANCE(gnb_id), msg_p); - } + LOG_I(GNB_APP,"default drx %d\n",ngap_register_gNB->default_drx); + itti_send_msg_to_task (TASK_NGAP, GNB_MODULE_ID_TO_INSTANCE(gnb_id), msg_p); + } if (gnb_id == 0) RCconfig_nr_gtpu(); - - LOG_I(GNB_APP,"[gNB %d] gNB_app_register for instance %d\n", gnb_id, GNB_MODULE_ID_TO_INSTANCE(gnb_id)); - - register_gnb_pending++; } - } + + LOG_I(GNB_APP,"[gNB %d] gNB_app_register for instance %d\n", gnb_id, GNB_MODULE_ID_TO_INSTANCE(gnb_id)); + + register_gnb_pending++; + } return register_gnb_pending; } @@ -126,15 +131,42 @@ static uint32_t gNB_app_register_x2(uint32_t gnb_id_start, uint32_t gnb_id_end) return register_gnb_x2_pending; } +/*------------------------------------------------------------------------------*/ + +static void init_pdcp(void) { + if (!NODE_IS_DU(RC.nrrrc[0]->node_type)) { + pdcp_layer_init(); + uint32_t pdcp_initmask = (IS_SOFTMODEM_NOS1) ? + (PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT) : LINK_ENB_PDCP_TO_GTPV1U_BIT; + if (IS_SOFTMODEM_NOS1) { + LOG_I(PDCP, "IS_SOFTMODEM_NOS1 option enabled\n"); + pdcp_initmask = pdcp_initmask | ENB_NAS_USE_TUN_BIT | SOFTMODEM_NOKRNMOD_BIT; + } + + pdcp_module_init(pdcp_initmask); + + if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { + LOG_I(PDCP, "node is CU, pdcp send rlc_data_req by proto_agent \n"); + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t)proto_agent_send_rlc_data_req); + } else { + LOG_I(PDCP, "node is gNB \n"); + pdcp_set_rlc_data_req_func((send_rlc_data_req_func_t) rlc_data_req); + pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) pdcp_data_ind); + } + } else { + LOG_I(PDCP, "node is DU, rlc send pdcp_data_ind by proto_agent \n"); + pdcp_set_pdcp_data_ind_func((pdcp_data_ind_func_t) proto_agent_send_pdcp_data_ind); + } +} /*------------------------------------------------------------------------------*/ + void *gNB_app_task(void *args_p) { uint32_t gnb_nb = RC.nb_nr_inst; uint32_t gnb_id_start = 0; uint32_t gnb_id_end = gnb_id_start + gnb_nb; - uint32_t gnb_id; MessageDef *msg_p = NULL; const char *msg_name = NULL; @@ -143,11 +175,12 @@ void *gNB_app_task(void *args_p) /* for no gcc warnings */ (void)instance; + int cell_to_activate = 0; itti_mark_task_ready (TASK_GNB_APP); LOG_I(PHY, "%s() Task ready initialize structures\n", __FUNCTION__); - RCconfig_nr_macrlc(); + if (RC.nb_nr_macrlc_inst>0) RCconfig_nr_macrlc(); LOG_I(PHY, "%s() RC.nb_nr_L1_inst:%d\n", __FUNCTION__, RC.nb_nr_L1_inst); @@ -169,19 +202,43 @@ void *gNB_app_task(void *args_p) configure_nr_rrc(gnb_id); } + if (RC.nb_nr_inst > 0) { + init_pdcp(); + } + if (is_x2ap_enabled() ) { //&& !NODE_IS_DU(RC.rrc[0]->node_type) LOG_I(X2AP, "X2AP enabled \n"); __attribute__((unused)) uint32_t x2_register_gnb_pending = gNB_app_register_x2 (gnb_id_start, gnb_id_end); } - if (AMF_MODE_ENABLED) { - /* Try to register each gNB */ - //registered_gnb = 0; - __attribute__((unused)) uint32_t register_gnb_pending = gNB_app_register (gnb_id_start, gnb_id_end);//, gnb_properties_p); - } else { - /* Start L2L1 task */ - msg_p = itti_alloc_new_message(TASK_GNB_APP, 0, INITIALIZE_MESSAGE); - itti_send_msg_to_task(TASK_L2L1, INSTANCE_DEFAULT, msg_p); + /* For the CU case the gNB registration with the AMF might have to take place after the F1 setup, as the PLMN info + * can originate from the DU. Add check on whether x2ap is enabled to account for ENDC NSA scenario.*/ + if ((AMF_MODE_ENABLED || is_x2ap_enabled()) && !NODE_IS_DU(RC.nrrrc[0]->node_type) && !NODE_IS_CU(RC.nrrrc[0]->node_type)) { + /* Try to register each gNB */ + //registered_gnb = 0; + __attribute__((unused)) uint32_t register_gnb_pending = gNB_app_register (gnb_id_start, gnb_id_end); + } + + if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { + + if (itti_create_task(TASK_CU_F1, F1AP_CU_task, NULL) < 0) { + LOG_E(F1AP, "Create task for F1AP CU failed\n"); + AssertFatal(1==0,"exiting"); + } + } + + if (NODE_IS_DU(RC.nrrrc[0]->node_type)) { + + if (itti_create_task(TASK_DU_F1, F1AP_DU_task, NULL) < 0) { + LOG_E(F1AP, "Create task for F1AP DU failed\n"); + AssertFatal(1==0,"exiting"); + } + // configure F1AP here for F1C + LOG_I(GNB_APP,"ngran_gNB_DU: Allocating ITTI message for F1AP_SETUP_REQ\n"); + msg_p = itti_alloc_new_message (TASK_GNB_APP, 0, F1AP_SETUP_REQ); + RCconfig_NR_DU_F1(msg_p, 0); + + itti_send_msg_to_task (TASK_DU_F1, GNB_MODULE_ID_TO_INSTANCE(0), msg_p); } do { @@ -245,6 +302,29 @@ void *gNB_app_task(void *args_p) */ break; + case F1AP_SETUP_RESP: + AssertFatal(NODE_IS_DU(RC.nrrrc[0]->node_type), "Should not have received F1AP_SETUP_RESP in CU/gNB\n"); + + LOG_I(GNB_APP, "Received %s: associated ngran_gNB_CU %s with %d cells to activate\n", ITTI_MSG_NAME (msg_p), + F1AP_SETUP_RESP(msg_p).gNB_CU_name,F1AP_SETUP_RESP(msg_p).num_cells_to_activate); + cell_to_activate = F1AP_SETUP_RESP(msg_p).num_cells_to_activate; + + gNB_app_handle_f1ap_setup_resp(&F1AP_SETUP_RESP(msg_p)); + + break; + case F1AP_GNB_CU_CONFIGURATION_UPDATE: + AssertFatal(NODE_IS_DU(RC.nrrrc[0]->node_type), "Should not have received F1AP_GNB_CU_CONFIGURATION_UPDATE in CU/gNB\n"); + + LOG_I(GNB_APP, "Received %s: associated ngran_gNB_CU %s with %d cells to activate\n", ITTI_MSG_NAME (msg_p), + F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p).gNB_CU_name,F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p).num_cells_to_activate); + + cell_to_activate += F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p).num_cells_to_activate; + gNB_app_handle_f1ap_gnb_cu_configuration_update(&F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p)); + + /* Check if at least gNB is registered with one AMF */ + AssertFatal(cell_to_activate == 1,"No cells to activate or cells > 1 %d\n",cell_to_activate); + + break; case NGAP_DEREGISTERED_GNB_IND: LOG_W(GNB_APP, "[gNB %ld] Received %s: associated AMF %d\n", instance, msg_name, NGAP_DEREGISTERED_GNB_IND(msg_p).nb_amf); diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index b9f394dad9..2a75e5955e 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -43,6 +43,7 @@ #include "ngap_gNB.h" #include "sctp_eNB_task.h" #include "sctp_default_values.h" +#include "F1AP_CauseRadioNetwork.h" // #include "SystemInformationBlockType2.h" // #include "LAYER2/MAC/extern.h" // #include "LAYER2/MAC/proto.h" @@ -79,7 +80,12 @@ #include "NR_ControlResourceSet.h" #include "NR_EUTRA-MBSFN-SubframeConfig.h" +#include "RRC/NR/MESSAGES/asn1_msg.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp.h" + extern uint16_t sf_ahead; +int macrlc_has_f1 = 0; +extern ngran_node_t node_type; extern int config_check_band_frequencies(int ind, int16_t band, uint64_t downlink_frequency, int32_t uplink_frequency_offset, uint32_t frame_type); @@ -323,6 +329,10 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { free(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing); scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing=NULL; } + + // check pucch_ResourceConfig + AssertFatal(*scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup->pucch_ResourceCommon < 2, + "pucch_ResourceConfig should be 0 or 1 for now\n"); } /* Function to allocate dedicated serving cell config strutures */ @@ -605,11 +615,13 @@ void RCconfig_NR_L1(void) { RC.gNB[j] = (PHY_VARS_gNB *)malloc(sizeof(PHY_VARS_gNB)); LOG_I(NR_PHY,"RC.gNB[%d] = %p\n",j,RC.gNB[j]); memset(RC.gNB[j],0,sizeof(PHY_VARS_gNB)); - RC.gNB[j]->Mod_id = j; + RC.gNB[j]->Mod_id = j; } RC.gNB[j]->pusch_proc_threads = *(L1_ParamList.paramarray[j][L1_PUSCH_PROC_THREADS].uptr); - + RC.gNB[j]->pucch0_thres = *(L1_ParamList.paramarray[j][L1_PUCCH0_DTX_THRESHOLD].uptr); + RC.gNB[j]->prach_thres = *(L1_ParamList.paramarray[j][L1_PRACH_DTX_THRESHOLD].uptr); + RC.gNB[j]->pusch_thres = *(L1_ParamList.paramarray[j][L1_PUSCH_DTX_THRESHOLD].uptr); if(strcmp(*(L1_ParamList.paramarray[j][L1_TRANSPORT_N_PREFERENCE_IDX].strptr), "local_mac") == 0) { //sf_ahead = 2; // Need 4 subframe gap between RX and TX }else if (strcmp(*(L1_ParamList.paramarray[j][L1_TRANSPORT_N_PREFERENCE_IDX].strptr), "nfapi") == 0) { @@ -679,10 +691,25 @@ void RCconfig_nr_macrlc() { for (j=0;jpusch_target_snrx10 = *(MacRLC_ParamList.paramarray[j][MACRLC_PUSCHTARGETSNRX10_IDX].iptr); + RC.nrmac[j]->pucch_target_snrx10 = *(MacRLC_ParamList.paramarray[j][MACRLC_PUCCHTARGETSNRX10_IDX].iptr); + RC.nrmac[j]->pucch_failure_thres = *(MacRLC_ParamList.paramarray[j][MACRLC_PUCCHFAILURETHRES_IDX].iptr); + RC.nrmac[j]->pusch_failure_thres = *(MacRLC_ParamList.paramarray[j][MACRLC_PUSCHFAILURETHRES_IDX].iptr); + if (strcmp(*(MacRLC_ParamList.paramarray[j][MACRLC_TRANSPORT_N_PREFERENCE_IDX].strptr), "local_RRC") == 0) { // check number of instances is same as RRC/PDCP + }else if (strcmp(*(MacRLC_ParamList.paramarray[j][MACRLC_TRANSPORT_N_PREFERENCE_IDX].strptr), "f1") == 0) { + printf("Configuring F1 interfaces for MACRLC\n"); + RC.nrmac[j]->eth_params_n.local_if_name = strdup(*(MacRLC_ParamList.paramarray[j][MACRLC_LOCAL_N_IF_NAME_IDX].strptr)); + RC.nrmac[j]->eth_params_n.my_addr = strdup(*(MacRLC_ParamList.paramarray[j][MACRLC_LOCAL_N_ADDRESS_IDX].strptr)); + RC.nrmac[j]->eth_params_n.remote_addr = strdup(*(MacRLC_ParamList.paramarray[j][MACRLC_REMOTE_N_ADDRESS_IDX].strptr)); + RC.nrmac[j]->eth_params_n.my_portc = *(MacRLC_ParamList.paramarray[j][MACRLC_LOCAL_N_PORTC_IDX].iptr); + RC.nrmac[j]->eth_params_n.remote_portc = *(MacRLC_ParamList.paramarray[j][MACRLC_REMOTE_N_PORTC_IDX].iptr); + RC.nrmac[j]->eth_params_n.my_portd = *(MacRLC_ParamList.paramarray[j][MACRLC_LOCAL_N_PORTD_IDX].iptr); + RC.nrmac[j]->eth_params_n.remote_portd = *(MacRLC_ParamList.paramarray[j][MACRLC_REMOTE_N_PORTD_IDX].iptr);; + RC.nrmac[j]->eth_params_n.transp_preference = ETH_UDP_MODE; + macrlc_has_f1 = 1; }else if (strcmp(*(MacRLC_ParamList.paramarray[j][MACRLC_TRANSPORT_N_PREFERENCE_IDX].strptr), "cudu") == 0) { RC.nrmac[j]->eth_params_n.local_if_name = strdup(*(MacRLC_ParamList.paramarray[j][MACRLC_LOCAL_N_IF_NAME_IDX].strptr)); RC.nrmac[j]->eth_params_n.my_addr = strdup(*(MacRLC_ParamList.paramarray[j][MACRLC_LOCAL_N_ADDRESS_IDX].strptr)); @@ -708,8 +735,6 @@ void RCconfig_nr_macrlc() { RC.nrmac[j]->eth_params_s.remote_portd = *(MacRLC_ParamList.paramarray[j][MACRLC_REMOTE_S_PORTD_IDX].iptr); RC.nrmac[j]->eth_params_s.transp_preference = ETH_UDP_MODE; - //sf_ahead = 2; // Cannot cope with 4 subframes between RX and TX - set it to 2 - printf("**************** vnf_port:%d\n", RC.nrmac[j]->eth_params_s.my_portc); configure_nr_nfapi_vnf(RC.nrmac[j]->eth_params_s.my_addr, RC.nrmac[j]->eth_params_s.my_portc); printf("**************** RETURNED FROM configure_nfapi_vnf() vnf_port:%d\n", RC.nrmac[j]->eth_params_s.my_portc); @@ -719,7 +744,8 @@ void RCconfig_nr_macrlc() { RC.nrmac[j]->ulsch_max_slots_inactivity = *(MacRLC_ParamList.paramarray[j][MACRLC_ULSCH_MAX_SLOTS_INACTIVITY].uptr); }// for (j=0;j 0 - AssertFatal (0,"No " CONFIG_STRING_MACRLC_LIST " configuration found"); + LOG_E(PHY,"No %s configuration found\n", CONFIG_STRING_MACRLC_LIST); + // AssertFatal (0,"No " CONFIG_STRING_MACRLC_LIST " configuration found"); } } @@ -824,7 +850,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { int num_gnbs = 0; char aprefix[MAX_OPTNAME_SIZE*2 + 8]; int32_t gnb_id = 0; - int k; + int k = 0; + paramdef_t GNBSParams[] = GNBSPARAMS_DESC; ////////// Identification parameters paramdef_t GNBParams[] = GNBPARAMS_DESC; @@ -940,6 +967,41 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { fix_scd(scd); printf("NRRRC %d: Southbound Transport %s\n",i,*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr)); + + if (strcmp(*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "f1") == 0) { + paramdef_t SCTPParams[] = GNBSCTPPARAMS_DESC; + char aprefix[MAX_OPTNAME_SIZE*2 + 8]; + sprintf(aprefix,"%s.[%u].%s",GNB_CONFIG_STRING_GNB_LIST,i,GNB_CONFIG_STRING_SCTP_CONFIG); + config_get(SCTPParams,sizeof(SCTPParams)/sizeof(paramdef_t),aprefix); + rrc->node_id = *(GNBParamList.paramarray[0][GNB_GNB_ID_IDX].uptr); + LOG_I(GNB_APP,"F1AP: gNB_CU_id[%d] %d\n",k,rrc->node_id); + rrc->node_name = strdup(*(GNBParamList.paramarray[0][GNB_GNB_NAME_IDX].strptr)); + LOG_I(GNB_APP,"F1AP: gNB_CU_name[%d] %s\n",k,rrc->node_name); + rrc->eth_params_s.local_if_name = strdup(*(GNBParamList.paramarray[i][GNB_LOCAL_S_IF_NAME_IDX].strptr)); + rrc->eth_params_s.my_addr = strdup(*(GNBParamList.paramarray[i][GNB_LOCAL_S_ADDRESS_IDX].strptr)); + rrc->eth_params_s.remote_addr = strdup(*(GNBParamList.paramarray[i][GNB_REMOTE_S_ADDRESS_IDX].strptr)); + rrc->eth_params_s.my_portc = *(GNBParamList.paramarray[i][GNB_LOCAL_S_PORTC_IDX].uptr); + rrc->eth_params_s.remote_portc = *(GNBParamList.paramarray[i][GNB_REMOTE_S_PORTC_IDX].uptr); + rrc->eth_params_s.my_portd = *(GNBParamList.paramarray[i][GNB_LOCAL_S_PORTD_IDX].uptr); + rrc->eth_params_s.remote_portd = *(GNBParamList.paramarray[i][GNB_REMOTE_S_PORTD_IDX].uptr); + rrc->eth_params_s.transp_preference = ETH_UDP_MODE; + rrc->node_type = ngran_gNB_CU; + rrc->sctp_in_streams = (uint16_t)*(SCTPParams[GNB_SCTP_INSTREAMS_IDX].uptr); + rrc->sctp_out_streams = (uint16_t)*(SCTPParams[GNB_SCTP_OUTSTREAMS_IDX].uptr); + } else { + // set to ngran_gNB for now, it will get set to ngran_gNB_DU if macrlc entity which uses F1 is present + // Note: we will have to handle the case of ngran_ng_gNB_DU + if (macrlc_has_f1 == 0) { + rrc->node_type = ngran_gNB; + LOG_I(NR_RRC,"Setting node_type to ngran_gNB\n"); + } else { + rrc->node_type = ngran_gNB_DU; + LOG_I(NR_RRC,"Setting node_type to ngran_gNB_DU\n"); + } + } + + rrc->nr_cellid = (uint64_t)*(GNBParamList.paramarray[i][GNB_NRCELLID_IDX].u64ptr); + if (strcmp(*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "local_mac") == 0) { } else if (strcmp(*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "cudu") == 0) { @@ -1006,16 +1068,13 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { NRRRC_CONFIGURATION_REQ (msg_p).pdsch_AntennaPorts = *GNBParamList.paramarray[i][GNB_PDSCH_ANTENNAPORTS_IDX].iptr; printf("pusch_AntennaPorts %d\n",*GNBParamList.paramarray[i][GNB_PUSCH_ANTENNAPORTS_IDX].iptr); NRRRC_CONFIGURATION_REQ (msg_p).pusch_AntennaPorts = *GNBParamList.paramarray[i][GNB_PUSCH_ANTENNAPORTS_IDX].iptr; - printf("pusch_TargetSNRx10 %d\n",*GNBParamList.paramarray[i][GNB_PUSCH_TARGETPOW_X10_IDX].iptr); - NRRRC_CONFIGURATION_REQ (msg_p).pusch_TargetSNRx10 = *GNBParamList.paramarray[i][GNB_PUSCH_TARGETPOW_X10_IDX].iptr; - printf("pucch_TargetSNRx10 %d\n",*GNBParamList.paramarray[i][GNB_PUCCH_TARGETPOW_X10_IDX].iptr); - NRRRC_CONFIGURATION_REQ (msg_p).pucch_TargetSNRx10 = *GNBParamList.paramarray[i][GNB_PUCCH_TARGETPOW_X10_IDX].iptr; NRRRC_CONFIGURATION_REQ (msg_p).scc = scc; NRRRC_CONFIGURATION_REQ (msg_p).scd = scd; }// }//End for (k=0; k configuration, &NRRRC_CONFIGURATION_REQ(msg_p), sizeof(NRRRC_CONFIGURATION_REQ(msg_p))); }//End if (num_gnbs>0) config_security(rrc); @@ -1077,15 +1136,17 @@ int RCconfig_nr_gtpu(void ) { IPV4_STR_ADDR_TO_INT_NWBO (address, GTPV1U_ENB_S1_REQ(message).enb_ip_address_for_S1u_S12_S4_up, "BAD IP ADDRESS FORMAT FOR eNB S1_U !\n" ); LOG_I(GTPU,"Configuring GTPu address : %s -> %x\n",address,GTPV1U_ENB_S1_REQ(message).enb_ip_address_for_S1u_S12_S4_up); GTPV1U_ENB_S1_REQ(message).enb_port_for_S1u_S12_S4_up = gnb_port_for_S1U; + strcpy(GTPV1U_ENB_S1_REQ(message).addrStr,address); + sprintf(GTPV1U_ENB_S1_REQ(message).portStr,"%d", gnb_port_for_NGU); } else {// TODO SA message = itti_alloc_new_message(TASK_GNB_APP, 0, GTPV1U_GNB_NG_REQ); AssertFatal(message!=NULL,""); IPV4_STR_ADDR_TO_INT_NWBO (address, GTPV1U_GNB_NG_REQ(message).gnb_ip_address_for_NGu_up, "BAD IP ADDRESS FORMAT FOR gNB NG_U !\n" ); LOG_I(GTPU,"Configuring GTPu address : %s -> %x\n",address,GTPV1U_GNB_NG_REQ(message).gnb_ip_address_for_NGu_up); GTPV1U_GNB_NG_REQ(message).gnb_port_for_NGu_up = gnb_port_for_NGU; + strcpy(GTPV1U_GNB_NG_REQ(message).addrStr,address); + sprintf(GTPV1U_GNB_NG_REQ(message).portStr,"%d", gnb_port_for_NGU); } - strcpy(GTPV1U_ENB_S1_REQ(message).addrStr,address); - sprintf(GTPV1U_ENB_S1_REQ(message).portStr,"%d", gnb_port_for_NGU); itti_send_msg_to_task (TASK_VARIABLE, 0, message); // data model is wrong: gtpu doesn't have enb_id (or module_id) } else LOG_E(GTPU,"invalid address for NGU\n"); @@ -1264,10 +1325,12 @@ int RCconfig_NR_NG(MessageDef *msg_p, uint32_t i) { NGAP_REGISTER_GNB_REQ (msg_p).amf_ip_address[j].ipv6 = 1; } + /* not in configuration yet ... if (NGParamList.paramarray[l][GNB_AMF_BROADCAST_PLMN_INDEX].iptr) NGAP_REGISTER_GNB_REQ(msg_p).broadcast_plmn_num[l] = NGParamList.paramarray[l][GNB_AMF_BROADCAST_PLMN_INDEX].numelt; else NGAP_REGISTER_GNB_REQ(msg_p).broadcast_plmn_num[l] = 0; + */ AssertFatal(NGAP_REGISTER_GNB_REQ(msg_p).broadcast_plmn_num[l] <= NGAP_REGISTER_GNB_REQ(msg_p).num_plmn, "List of broadcast PLMN to be sent to AMF can not be longer than actual " @@ -1374,7 +1437,7 @@ void NRRCConfig(void) { // Get num MACRLC instances config_getlist( &MACRLCParamList,NULL,0, NULL); - RC.nb_macrlc_inst = MACRLCParamList.numelt; + RC.nb_nr_macrlc_inst = MACRLCParamList.numelt; // Get num L1 instances config_getlist( &L1ParamList,NULL,0, NULL); RC.nb_nr_L1_inst = L1ParamList.numelt; @@ -1593,3 +1656,461 @@ int RCconfig_NR_X2(MessageDef *msg_p, uint32_t i) { return 0; } +int RCconfig_NR_DU_F1(MessageDef *msg_p, uint32_t i) { + int k; + paramdef_t GNBSParams[] = GNBSPARAMS_DESC; + paramdef_t GNBParams[] = GNBPARAMS_DESC; + paramlist_def_t GNBParamList = {GNB_CONFIG_STRING_GNB_LIST,NULL,0}; + config_get( GNBSParams,sizeof(GNBSParams)/sizeof(paramdef_t),NULL); + int num_gnbs = GNBSParams[GNB_ACTIVE_GNBS_IDX].numelt; + AssertFatal (i < num_gnbs, + "Failed to parse config file no %uth element in %s \n",i, GNB_CONFIG_STRING_ACTIVE_GNBS); + + if (num_gnbs > 0) { + // Output a list of all eNBs. + config_getlist( &GNBParamList,GNBParams,sizeof(GNBParams)/sizeof(paramdef_t),NULL); + AssertFatal(GNBParamList.paramarray[i][GNB_GNB_ID_IDX].uptr != NULL, + "gNB id %u is not defined in configuration file\n",i); + F1AP_SETUP_REQ (msg_p).num_cells_available = 0; + + for (k=0; k eth_params_n.remote_addr); + LOG_I(GNB_APP,"FIAP: CU_ip4_address in DU %p, strlen %d\n",F1AP_SETUP_REQ (msg_p).CU_f1_ip_address.ipv4_address,(int)strlen(RC.nrmac[k]->eth_params_n.remote_addr)); + F1AP_SETUP_REQ (msg_p).CU_f1_ip_address.ipv6 = 0; + F1AP_SETUP_REQ (msg_p).CU_f1_ip_address.ipv4 = 1; + //strcpy(F1AP_SETUP_REQ (msg_p).CU_f1_ip_address.ipv6_address, ""); + strcpy(F1AP_SETUP_REQ (msg_p).CU_f1_ip_address.ipv4_address, RC.nrmac[k]->eth_params_n.remote_addr); + LOG_I(GNB_APP,"F1AP: DU_ip4_address in DU %s\n",RC.nrmac[k]->eth_params_n.my_addr); + LOG_I(GNB_APP,"FIAP: DU_ip4_address in DU %p, strlen %d\n",F1AP_SETUP_REQ (msg_p).DU_f1_ip_address.ipv4_address,(int)strlen(RC.nrmac[k]->eth_params_n.my_addr)); + F1AP_SETUP_REQ (msg_p).DU_f1_ip_address.ipv6 = 0; + F1AP_SETUP_REQ (msg_p).DU_f1_ip_address.ipv4 = 1; + //strcpy(F1AP_SETUP_REQ (msg_p).DU_f1_ip_address.ipv6_address, ""); + strcpy(F1AP_SETUP_REQ (msg_p).DU_f1_ip_address.ipv4_address, RC.nrmac[k]->eth_params_n.my_addr); + //strcpy(F1AP_SETUP_REQ (msg_p).CU_ip_address[l].ipv6_address,*(F1ParamList.paramarray[l][ENB_CU_IPV6_ADDRESS_IDX].strptr)); + sprintf(aprefix,"%s.[%i].%s",GNB_CONFIG_STRING_GNB_LIST,k,GNB_CONFIG_STRING_SCTP_CONFIG); + config_get(SCTPParams,sizeof(SCTPParams)/sizeof(paramdef_t),aprefix); + F1AP_SETUP_REQ (msg_p).sctp_in_streams = (uint16_t)*(SCTPParams[GNB_SCTP_INSTREAMS_IDX].uptr); + F1AP_SETUP_REQ (msg_p).sctp_out_streams = (uint16_t)*(SCTPParams[GNB_SCTP_OUTSTREAMS_IDX].uptr); + gNB_RRC_INST *rrc = RC.nrrrc[k]; + // wait until RRC cell information is configured + int cell_info_configured = 0; + + do { + LOG_I(GNB_APP,"ngran_gNB_DU: Waiting for basic cell configuration\n"); + usleep(100000); + pthread_mutex_lock(&rrc->cell_info_mutex); + cell_info_configured = rrc->cell_info_configured; + pthread_mutex_unlock(&rrc->cell_info_mutex); + } while (cell_info_configured == 0); + + rrc->configuration.mcc[0] = F1AP_SETUP_REQ (msg_p).mcc[k]; + rrc->configuration.mnc[0] = F1AP_SETUP_REQ (msg_p).mnc[k]; + rrc->configuration.tac = F1AP_SETUP_REQ (msg_p).tac[k]; + rrc->nr_cellid = F1AP_SETUP_REQ (msg_p).nr_cellid[k]; + F1AP_SETUP_REQ (msg_p).nr_pci[k] = *rrc->configuration.scc->physCellId; + F1AP_SETUP_REQ (msg_p).num_ssi[k] = 0; + + if (rrc->configuration.scc->tdd_UL_DL_ConfigurationCommon) { + LOG_I(GNB_APP,"ngran_DU: Configuring Cell %d for TDD\n",k); + F1AP_SETUP_REQ (msg_p).fdd_flag = 0; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].tdd.nr_arfcn = rrc->configuration.scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].tdd.scs = rrc->configuration.scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].tdd.nrb = rrc->configuration.scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].tdd.num_frequency_bands = 1; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].tdd.nr_band[0] = *rrc->configuration.scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].tdd.sul_active = 0; + } else { + /***************** for test *****************/ + LOG_I(GNB_APP,"ngran_DU: Configuring Cell %d for FDD\n",k); + F1AP_SETUP_REQ (msg_p).fdd_flag = 1; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.dl_nr_arfcn = 26200UL; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_nr_arfcn = 26200UL; + // For LTE use scs field to carry prefix type and number of antennas + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.dl_scs = 0; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_scs = 0; + // use nrb field to hold LTE N_RB_DL (0...5) + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_nrb = 3; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_nrb = 3; + // RK: we need to check there value for FDD's frequency_bands DL/UL + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_num_frequency_bands = 1; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_nr_band[0] = 7; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.dl_num_frequency_bands = 1; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.dl_nr_band[0] = 7; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_num_sul_frequency_bands = 0; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.ul_nr_sul_band[0] = 7; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.dl_num_sul_frequency_bands = 0; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.dl_nr_sul_band[0] = 7; + F1AP_SETUP_REQ (msg_p).nr_mode_info[k].fdd.sul_active = 0; + /***************** for test *****************/ + } + + F1AP_SETUP_REQ (msg_p).measurement_timing_information[k] = "0"; + F1AP_SETUP_REQ (msg_p).ranac[k] = 0; + F1AP_SETUP_REQ (msg_p).mib[k] = rrc->carrier.MIB; + F1AP_SETUP_REQ (msg_p).sib1[k] = rrc->carrier.SIB1; + F1AP_SETUP_REQ (msg_p).mib_length[k] = rrc->carrier.sizeof_MIB; + F1AP_SETUP_REQ (msg_p).sib1_length[k] = rrc->carrier.sizeof_SIB1; + break; + } + } + } + return 0; +} + +int du_check_plmn_identity(rrc_gNB_carrier_data_t *carrier,uint16_t mcc,uint16_t mnc,uint8_t mnc_digit_length) { + NR_SIB1_t *sib1 = carrier->siblock1->message.choice.c1->choice.systemInformationBlockType1; + AssertFatal(sib1->cellAccessRelatedInfo.plmn_IdentityList.list.array[0]->plmn_IdentityList.list.count > 0, + "plmn info isn't there\n"); + AssertFatal(mnc_digit_length == 2 || mnc_digit_length == 3, + "impossible mnc_digit_length %d\n", mnc_digit_length); + NR_PLMN_Identity_t *plmn_Identity = sib1->cellAccessRelatedInfo.plmn_IdentityList.list.array[0] + ->plmn_IdentityList.list.array[0]; + + // check if mcc is different and return failure if so + if (mcc != + ((*plmn_Identity->mcc->list.array[0])*100)+ + ((*plmn_Identity->mcc->list.array[1])*10) + + (*plmn_Identity->mcc->list.array[2])) { + LOG_E(GNB_APP, "mcc in F1AP_SETUP_RESP message is different from mcc in DU \n"); + return(0); + } + + // check that mnc digit length is different and return failure if so + if (mnc_digit_length != plmn_Identity->mnc.list.count) { + LOG_E(GNB_APP, "mnc(length: %d) in F1AP_SETUP_RESP message is different from mnc(length: %d) in DU \n", + mnc_digit_length, plmn_Identity->mnc.list.count); + return 0; + } + + // check that 2 digit mnc is different and return failure if so + if (mnc_digit_length == 2 && + (mnc != + (*plmn_Identity->mnc.list.array[0]*10) + + (*plmn_Identity->mnc.list.array[1]))) { + LOG_E(GNB_APP, "mnc(%d) in F1AP_SETUP_RESP message is different from mnc(%ld%ld) in DU \n", + mnc, *plmn_Identity->mnc.list.array[0], *plmn_Identity->mnc.list.array[1]); + return(0); + } + else if (mnc_digit_length == 3 && + (mnc != + (*plmn_Identity->mnc.list.array[0]*100) + + (*plmn_Identity->mnc.list.array[1]*10) + + (*plmn_Identity->mnc.list.array[2]))) { + LOG_E(GNB_APP, "mnc(%d) in F1AP_SETUP_RESP message is different from mnc(%ld%ld%ld) in DU \n", + mnc, *plmn_Identity->mnc.list.array[0], *plmn_Identity->mnc.list.array[1], *plmn_Identity->mnc.list.array[2]); + return(0); + } + + // if we're here, the mcc/mnc match so return success + return(1); +} + +void du_extract_and_decode_SI(int inst, int si_ind, uint8_t *si_container, int si_container_length) { + gNB_RRC_INST *rrc = RC.nrrrc[inst]; + rrc_gNB_carrier_data_t *carrier = &rrc->carrier; + NR_BCCH_DL_SCH_Message_t *bcch_message ; + AssertFatal(si_ind == 0, "Can only handle a single SI block for now\n"); + LOG_I(GNB_APP, "rrc inst %d: Trying to decode SI block %d @ %p, length %d\n", inst, si_ind, si_container, si_container_length); + // point to first SI block + bcch_message = &carrier->systemInformation; + asn_dec_rval_t dec_rval = uper_decode_complete( NULL, + &asn_DEF_NR_BCCH_DL_SCH_Message, + (void **)&bcch_message, + (const void *)si_container, + si_container_length); + + if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { + AssertFatal(1==0, "[GNB_APP][NR_RRC inst %"PRIu8"] Failed to decode BCCH_DLSCH_MESSAGE (%zu bits)\n", + inst, + dec_rval.consumed ); + } + + if (bcch_message->message.present == NR_BCCH_DL_SCH_MessageType_PR_c1) { + switch (bcch_message->message.choice.c1->present) { + case NR_BCCH_DL_SCH_MessageType__c1_PR_systemInformationBlockType1: + AssertFatal(1 == 0, "Should have received SIB1 from CU\n"); + break; + + case NR_BCCH_DL_SCH_MessageType__c1_PR_systemInformation: + { + NR_SystemInformation_t *si = bcch_message->message.choice.c1->choice.systemInformation; + + if (si->criticalExtensions.present == NR_SystemInformation__criticalExtensions_PR_systemInformation) { + for (int i = 0; i < si->criticalExtensions.choice.systemInformation->sib_TypeAndInfo.list.count; i++) { + LOG_I(GNB_APP, "Extracting SI %d/%d\n", i, si->criticalExtensions.choice.systemInformation->sib_TypeAndInfo.list.count); + SystemInformation_IEs__sib_TypeAndInfo__Member *typeAndInfo; + typeAndInfo = si->criticalExtensions.choice.systemInformation->sib_TypeAndInfo.list.array[i]; + + switch(typeAndInfo->present) { + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_NOTHING: + AssertFatal(0, "Should have received SIB2 SIB3 from CU\n"); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib2: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB2 in CU F1AP_SETUP_RESP message\n", inst); + carrier->sib2 = typeAndInfo->choice.sib2; + carrier->SIB23 = (uint8_t *)malloc(64); + memcpy((void *)carrier->SIB23, (void *)si_container, si_container_length); + carrier->sizeof_SIB23 = si_container_length; + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib3: + carrier->sib3 = typeAndInfo->choice.sib3; + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB3 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib4: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB4 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib5: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB5 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib6: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB6 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib7: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB7 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib8: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB8 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib9: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB9 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib10_v1610: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB10 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib11_v1610: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB11 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib12_v1610: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB12 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib13_v1610: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB13 in CU F1AP_SETUP_RESP message\n", inst); + break; + case NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib14_v1610: + LOG_I(GNB_APP, "[NR_RRC %"PRIu8"] Found SIB14 in CU F1AP_SETUP_RESP message\n", inst); + break; + default: + AssertFatal(1 == 0,"Shouldn't have received this SI %d\n", typeAndInfo->present); + break; + } + } + } + + break; + } + + case NR_BCCH_DL_SCH_MessageType__c1_PR_NOTHING: + AssertFatal(0, "Should have received SIB1 from CU\n"); + break; + } + } else AssertFatal(1 == 0, "No SI messages\n"); +} + +void configure_gnb_du_mac(int inst) { + gNB_RRC_INST *rrc = RC.nrrrc[inst]; + // LOG_I(GNB_APP,"Configuring MAC/L1 %d, carrier->sib2 %p\n", inst, &carrier->sib2->radioResourceConfigCommon); + LOG_I(GNB_APP,"Configuring gNB DU MAC/L1 %d \n", inst); + rrc_mac_config_req_gNB(rrc->module_id, + rrc->configuration.ssb_SubcarrierOffset, + rrc->configuration.pdsch_AntennaPorts, + rrc->configuration.pusch_AntennaPorts, + rrc->configuration.scc, + 0, + 0, // rnti + (NR_CellGroupConfig_t *)NULL + ); +} + + +int gNB_app_handle_f1ap_setup_resp(f1ap_setup_resp_t *resp) { + int i, j, si_ind; + int ret=0; + LOG_I(GNB_APP, "cells_to_activate %d, RRC instances %d\n", + resp->num_cells_to_activate, RC.nb_nr_inst); + + for (j = 0; j < resp->num_cells_to_activate; j++) { + for (i = 0; i < RC.nb_nr_inst; i++) { + rrc_gNB_carrier_data_t *carrier = &RC.nrrrc[i]->carrier; + // identify local index of cell j by nr_cellid, plmn identity and physical cell ID + LOG_I(GNB_APP, "Checking cell %d, rrc inst %d : rrc->nr_cellid %lx, resp->nr_cellid %lx\n", + j, i, RC.nrrrc[i]->nr_cellid, resp->cells_to_activate[j].nr_cellid); + + if (RC.nrrrc[i]->nr_cellid == resp->cells_to_activate[j].nr_cellid && + (du_check_plmn_identity(carrier, resp->cells_to_activate[j].mcc, resp->cells_to_activate[j].mnc, resp->cells_to_activate[j].mnc_digit_length)>0 && + resp->cells_to_activate[j].nrpci == carrier->physCellId)) { + // copy system information and decode it + for (si_ind=0; si_indcells_to_activate[j].num_SI; si_ind++) { + + du_extract_and_decode_SI(i, + si_ind, + resp->cells_to_activate[j].SI_container[2+si_ind], + resp->cells_to_activate[j].SI_container_length[2+si_ind]); + } + + // perform MAC/L1 common configuration + configure_gnb_du_mac(i); + ret++; + } else { + LOG_E(GNB_APP, "F1 Setup Response not matching\n"); + } + } + } + return(ret); +} + +int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update) { + int i, j, si_ind, ret=0; + LOG_I(GNB_APP, "cells_to_activate %d, RRC instances %d\n", + gnb_cu_cfg_update->num_cells_to_activate, RC.nb_nr_inst); + + for (j = 0; j < gnb_cu_cfg_update->num_cells_to_activate; j++) { + for (i = 0; i < RC.nb_nr_inst; i++) { + rrc_gNB_carrier_data_t *carrier = &RC.nrrrc[i]->carrier; + // identify local index of cell j by nr_cellid, plmn identity and physical cell ID + LOG_I(GNB_APP, "Checking cell %d, rrc inst %d : rrc->nr_cellid %lx, gnb_cu_cfg_updatenr_cellid %lx\n", + j, i, RC.nrrrc[i]->nr_cellid, gnb_cu_cfg_update->cells_to_activate[j].nr_cellid); + + if (RC.nrrrc[i]->nr_cellid == gnb_cu_cfg_update->cells_to_activate[j].nr_cellid && + (du_check_plmn_identity(carrier, gnb_cu_cfg_update->cells_to_activate[j].mcc, gnb_cu_cfg_update->cells_to_activate[j].mnc, gnb_cu_cfg_update->cells_to_activate[j].mnc_digit_length)>0 && + gnb_cu_cfg_update->cells_to_activate[j].nrpci == carrier->physCellId)) { + // copy system information and decode it + for (si_ind=0; si_indcells_to_activate[j].num_SI; si_ind++) { + + du_extract_and_decode_SI(i, + si_ind, + gnb_cu_cfg_update->cells_to_activate[j].SI_container[2+si_ind], + gnb_cu_cfg_update->cells_to_activate[j].SI_container_length[2+si_ind]); + } + + // perform MAC/L1 common configuration + configure_gnb_du_mac(i); + ret++; + } else { + LOG_E(GNB_APP, "GNB_CU_CONFIGURATION_UPDATE not matching\n"); + } + } + } + MessageDef *msg_ack_p = NULL; + if (ret > 0) { + // generate gNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE + msg_ack_p = itti_alloc_new_message (TASK_GNB_APP, 0, F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE); + F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(msg_ack_p).num_cells_failed_to_be_activated = 0; + F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(msg_ack_p).have_criticality = 0; + F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(msg_ack_p).noofTNLAssociations_to_setup =0; + F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(msg_ack_p).noofTNLAssociations_failed = 0; + F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(msg_ack_p).noofDedicatedSIDeliveryNeededUEs = 0; + itti_send_msg_to_task (TASK_DU_F1, INSTANCE_DEFAULT, msg_ack_p); + + } + else { + // generate gNB_CU_CONFIGURATION_UPDATE_FAILURE + msg_ack_p = itti_alloc_new_message (TASK_GNB_APP, 0, F1AP_GNB_CU_CONFIGURATION_UPDATE_FAILURE); + F1AP_GNB_CU_CONFIGURATION_UPDATE_FAILURE(msg_ack_p).cause = F1AP_CauseRadioNetwork_cell_not_available; + + itti_send_msg_to_task (TASK_DU_F1, INSTANCE_DEFAULT, msg_ack_p); + + } + + return(ret); +} + +void set_node_type(void) { + int j; + paramdef_t MacRLC_Params[] = MACRLCPARAMS_DESC; + paramlist_def_t MacRLC_ParamList = {CONFIG_STRING_MACRLC_LIST,NULL,0}; + paramdef_t GNBParams[] = GNBPARAMS_DESC; + paramlist_def_t GNBParamList = {GNB_CONFIG_STRING_GNB_LIST,NULL,0}; + + config_getlist( &MacRLC_ParamList,MacRLC_Params,sizeof(MacRLC_Params)/sizeof(paramdef_t), NULL); + config_getlist( &GNBParamList,GNBParams,sizeof(GNBParams)/sizeof(paramdef_t),NULL); + + if ( MacRLC_ParamList.numelt > 0) { + RC.nb_nr_macrlc_inst = MacRLC_ParamList.numelt; + for (j=0;j0) AssertFatal(l1_north_init_gNB()==0,"could not initialize L1 north interface\n"); + + AssertFatal (gnb_nb <= RC.nb_nr_inst, + "Number of gNB is greater than gNB defined in configuration file (%d/%d)!", + gnb_nb, RC.nb_nr_inst); + + LOG_I(GNB_APP,"Allocating gNB_RRC_INST for %d instances\n",RC.nb_nr_inst); + + RC.nrrrc = (gNB_RRC_INST **)malloc(RC.nb_nr_inst*sizeof(gNB_RRC_INST *)); + LOG_I(PHY, "%s() RC.nb_nr_inst:%d RC.nrrrc:%p\n", __FUNCTION__, RC.nb_nr_inst, RC.nrrrc); + + for (gnb_id = 0; gnb_id < RC.nb_nr_inst ; gnb_id++) { + RC.nrrrc[gnb_id] = (gNB_RRC_INST*)malloc(sizeof(gNB_RRC_INST)); + LOG_I(PHY, "%s() Creating RRC instance RC.nrrrc[%d]:%p (%d of %d)\n", __FUNCTION__, gnb_id, RC.nrrrc[gnb_id], gnb_id+1, RC.nb_nr_inst); + memset((void *)RC.nrrrc[gnb_id],0,sizeof(gNB_RRC_INST)); + msg_p = itti_alloc_new_message (TASK_GNB_APP, 0, NRRRC_CONFIGURATION_REQ); + RCconfig_NRRRC(msg_p,gnb_id, RC.nrrrc[gnb_id]); + } + + if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { + pdcp_layer_init(); +// nr_DRB_preconfiguration(0x1234); + rrc_init_nr_global_param(); + } +} diff --git a/openair2/GNB_APP/gnb_config.h b/openair2/GNB_APP/gnb_config.h index 4f305b1f4d..46102e4488 100644 --- a/openair2/GNB_APP/gnb_config.h +++ b/openair2/GNB_APP/gnb_config.h @@ -40,6 +40,7 @@ #include "PHY/defs_eNB.h" #include "s1ap_messages_types.h" #include "ngap_messages_types.h" +#include "f1ap_messages_types.h" #ifdef CMAKER #include "rrc_messages_types.h" @@ -105,6 +106,10 @@ extern void NRRCConfig(void); void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc); int RCconfig_NR_NG(MessageDef *msg_p, uint32_t i); int RCconfig_NR_X2(MessageDef *msg_p, uint32_t i); +int RCconfig_NR_DU_F1(MessageDef *msg_p, uint32_t i); +int gNB_app_handle_f1ap_setup_resp(f1ap_setup_resp_t *resp); +int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update); +void nr_read_config_and_init(void); #endif /* GNB_CONFIG_H_ */ /** @} */ diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 8b326d58c9..6d9c9b8342 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -116,8 +116,7 @@ typedef enum { #define GNB_CONFIG_STRING_SSBSUBCARRIEROFFSET "ssb_SubcarrierOffset" #define GNB_CONFIG_STRING_PDSCHANTENNAPORTS "pdsch_AntennaPorts" #define GNB_CONFIG_STRING_PUSCHANTENNAPORTS "pusch_AntennaPorts" -#define GNB_CONFIG_STRING_PUSCHTARGETPOWX10 "pusch_TargetSNRx10" -#define GNB_CONFIG_STRING_PUCCHTARGETPOWX10 "pucch_TargetSNRx10" +#define GNB_CONFIG_STRING_NRCELLID "nr_cellid" /*-----------------------------------------------------------------------------------------------------------------------------------------*/ /* cell configuration parameters */ @@ -141,8 +140,7 @@ typedef enum { {GNB_CONFIG_STRING_SSBSUBCARRIEROFFSET, NULL, 0, iptr:NULL, defintval:31, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_PDSCHANTENNAPORTS, NULL, 0, iptr:NULL, defintval:1, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_PUSCHANTENNAPORTS, NULL, 0, iptr:NULL, defintval:1, TYPE_INT, 0}, \ -{GNB_CONFIG_STRING_PUSCHTARGETPOWX10, NULL, 0, iptr:NULL, defintval:200, TYPE_INT, 0}, \ -{GNB_CONFIG_STRING_PUCCHTARGETPOWX10, NULL, 0, iptr:NULL, defintval:200, TYPE_INT, 0}, \ +{GNB_CONFIG_STRING_NRCELLID , NULL, 0, u64ptr:NULL, defint64val:1, TYPE_UINT64, 0}, \ } #define GNB_GNB_ID_IDX 0 @@ -162,8 +160,7 @@ typedef enum { #define GNB_SSB_SUBCARRIEROFFSET_IDX 14 #define GNB_PDSCH_ANTENNAPORTS_IDX 15 #define GNB_PUSCH_ANTENNAPORTS_IDX 16 -#define GNB_PUSCH_TARGETPOW_X10_IDX 17 -#define GNB_PUCCH_TARGETPOW_X10_IDX 18 +#define GNB_NRCELLID_IDX 17 #define TRACKING_AREA_CODE_OKRANGE {0x0001,0xFFFD} #define GNBPARAMS_CHECK { \ diff --git a/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c b/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c index c83cc1d9d3..d4f486f761 100644 --- a/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c +++ b/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c @@ -303,7 +303,22 @@ rx_sdu(const module_id_t enb_mod_idP, first_rb = ra->msg3_first_rb; - if (sduP == NULL) { // we've got an error on Msg3 + bool no_sig = true; + if (sduP) { + for (int k = 0; k < sdu_lenP; k++) { + if(sduP[k]!=0) { + no_sig = false; + break; + } + } + } + + if (no_sig || sduP == NULL) { // we've got an error on Msg3 + + if(no_sig) { + LOG_D(MAC,"No signal in Msg3\n"); + } + LOG_D(MAC, "[eNB %d] CC_id %d, RA %d ULSCH in error in round %d/%d\n", enb_mod_idP, CC_idP, @@ -312,6 +327,20 @@ rx_sdu(const module_id_t enb_mod_idP, (int) mac->common_channels[CC_idP].radioResourceConfigCommon->rach_ConfigCommon.maxHARQ_Msg3Tx); if (ra->msg3_round >= mac->common_channels[CC_idP].radioResourceConfigCommon->rach_ConfigCommon.maxHARQ_Msg3Tx - 1) { + + // Release RNTI of LTE PHY when RA does not succeed + UE_free_list_t *free_list = NULL; + pthread_mutex_lock(&lock_ue_freelist); + free_list = &mac->UE_free_list; + free_list->UE_free_ctrl[free_list->tail_freelist].rnti = current_rnti; + free_list->UE_free_ctrl[free_list->tail_freelist].removeContextFlg = 1; + free_list->UE_free_ctrl[free_list->tail_freelist].raFlag = 1; + free_list->num_UEs++; + mac->UE_release_req.ue_release_request_body.ue_release_request_TLVs_list[mac->UE_release_req.ue_release_request_body.number_of_TLVs].rnti = current_rnti; + mac->UE_release_req.ue_release_request_body.number_of_TLVs++; + free_list->tail_freelist = (free_list->tail_freelist + 1) % (NUMBER_OF_UE_MAX+1); + pthread_mutex_unlock(&lock_ue_freelist); + cancel_ra_proc(enb_mod_idP, CC_idP, frameP, current_rnti); nfapi_hi_dci0_request_t *hi_dci0_req = NULL; uint8_t sf_ahead_dl = ul_subframe2_k_phich(&mac->common_channels[CC_idP], subframeP); @@ -690,6 +719,19 @@ rx_sdu(const module_id_t enb_mod_idP, break; } + bool no_sig = true; + for (int k = 0; k < sdu_lenP; k++) { + if(sduP[k]!=0) { + no_sig = false; + break; + } + } + + if(no_sig) { + LOG_D(MAC, "No signal\n"); + break; + } + LOG_D(MAC, "[eNB %d][RAPROC] CC_id %d Frame %d, Received CCCH: %x.%x.%x.%x.%x.%x, Terminating RA procedure for UE rnti %x\n", enb_mod_idP, CC_idP, diff --git a/openair2/LAYER2/MAC/mac.h b/openair2/LAYER2/MAC/mac.h index 062f613526..cb93c2feb8 100644 --- a/openair2/LAYER2/MAC/mac.h +++ b/openair2/LAYER2/MAC/mac.h @@ -1192,6 +1192,8 @@ typedef struct { rnti_t rnti; ///remove UE context flag boolean_t removeContextFlg; + ///remove RA flag + boolean_t raFlag; } UE_free_ctrl_t; /*! \brief REMOVE UE list used by eNB to order UEs/CC for deleting*/ typedef struct { diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c index 452ed45b3a..996c2fbe27 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c @@ -51,6 +51,8 @@ uint32_t nr_compute_tbs(uint16_t Qm, uint32_t Ninfo, Np_info, C; uint8_t n, scale; + LOG_D(MAC,"nb_symb_sch %d, nb_dmrs_prb %d, nb_rb_oh %d\n",nb_symb_sch,nb_dmrs_prb,nb_rb_oh); + nbp_re = NR_NB_SC_PER_RB * nb_symb_sch - nb_dmrs_prb - nb_rb_oh; nb_re = min(156, nbp_re) * nb_rb; scale = (R>1024)?11:10; diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 91f009da30..93c6c1dbc7 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -1179,12 +1179,12 @@ int get_format0(uint8_t index, return format; } -int64_t *get_prach_config_info(uint32_t pointa, +int64_t *get_prach_config_info(frequency_range_t freq_range, uint8_t index, uint8_t unpaired) { int64_t *prach_config_info_p; - if (pointa > 2016666) { //FR2 + if (freq_range == FR2) { //FR2 prach_config_info_p = table_6_3_3_2_4_prachConfig_Index[index]; } else { // FR1 @@ -2524,7 +2524,7 @@ uint8_t get_K_ptrs(uint16_t nrb0, uint16_t nrb1, uint16_t N_RB) { // Set the transform precoding status according to 6.1.3 of 3GPP TS 38.214 version 16.3.0 Release 16: // - "UE procedure for applying transform precoding on PUSCH" -uint8_t get_transformPrecoding(const NR_ServingCellConfigCommon_t *scc, +uint8_t get_transformPrecoding(const NR_BWP_UplinkCommon_t *initialUplinkBWP, const NR_PUSCH_Config_t *pusch_config, const NR_BWP_Uplink_t *ubwp, uint8_t *dci_format, @@ -2539,7 +2539,7 @@ uint8_t get_transformPrecoding(const NR_ServingCellConfigCommon_t *scc, } } - if (rnti_type != NR_RNTI_RA) { + if (rnti_type != NR_RNTI_RA && rnti_type != NR_RNTI_TC) { if (*dci_format != NR_UL_DCI_FORMAT_0_0) { if (pusch_config->transformPrecoder != NULL) { return *pusch_config->transformPrecoder; @@ -2547,7 +2547,7 @@ uint8_t get_transformPrecoding(const NR_ServingCellConfigCommon_t *scc, } } - if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) { + if (initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) { return 1; // Transformprecoding disabled } else { LOG_D(PHY, "MAC_COMMON: Transform Precodig enabled through msg3_transformPrecoder\n"); @@ -2558,8 +2558,8 @@ uint8_t get_transformPrecoding(const NR_ServingCellConfigCommon_t *scc, return -1; } -uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, - const NR_CellGroupConfig_t *secondaryCellGroup, +uint16_t nr_dci_size(const NR_BWP_UplinkCommon_t *initialUplinkBWP, + const NR_CellGroupConfig_t *cg, dci_pdu_rel15_t *dci_pdu, nr_dci_format_t format, nr_rnti_type_t rnti_type, @@ -2578,8 +2578,9 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, NR_PUSCH_Config_t *pusch_Config = NULL; NR_SRS_Config_t *srs_config = NULL; if(bwp_id > 0) { - bwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; - ubwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_id-1]; + AssertFatal(cg!=NULL,"Cellgroup is null and bwp_id!=0"); + bwp=cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; + ubwp=cg->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_id-1]; pdsch_config = bwp->bwp_Dedicated->pdsch_Config->choice.setup; pusch_Config = ubwp->bwp_Dedicated->pusch_Config->choice.setup; srs_config = ubwp->bwp_Dedicated->srs_Config->choice.setup; @@ -2591,7 +2592,7 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, /// fixed: Format identifier 1, Hop flag 1, MCS 5, NDI 1, RV 2, HARQ PID 4, PUSCH TPC 2 Time Domain assgnmt 4 --20 size += 20; size += (uint8_t)ceil( log2( (N_RB*(N_RB+1))>>1 ) ); // Freq domain assignment -- hopping scenario to be updated - size += nr_dci_size(scc,secondaryCellGroup,dci_pdu,NR_DL_DCI_FORMAT_1_0, rnti_type, N_RB, bwp_id) - size; // Padding to match 1_0 size + size += nr_dci_size(initialUplinkBWP,cg,dci_pdu,NR_DL_DCI_FORMAT_1_0, rnti_type, N_RB, bwp_id) - size; // Padding to match 1_0 size // UL/SUL indicator assumed to be 0 break; @@ -2599,24 +2600,24 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, /// fixed: Format identifier 1, MCS 5, NDI 1, RV 2, HARQ PID 4, PUSCH TPC 2, ULSCH indicator 1 --16 size += 16; // Carrier indicator - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) { + if (cg->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) { dci_pdu->carrier_indicator.nbits=3; size += dci_pdu->carrier_indicator.nbits; } // UL/SUL indicator - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->supplementaryUplink != NULL) { + if (cg->spCellConfig->spCellConfigDedicated->supplementaryUplink != NULL) { dci_pdu->carrier_indicator.nbits=1; size += dci_pdu->ul_sul_indicator.nbits; } // BWP Indicator - uint8_t n_ul_bwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; + uint8_t n_ul_bwp = cg->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; if (n_ul_bwp < 2) dci_pdu->bwp_indicator.nbits = n_ul_bwp; else dci_pdu->bwp_indicator.nbits = 2; size += dci_pdu->bwp_indicator.nbits; // Freq domain assignment - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pusch_Config->choice.setup->rbg_Size != NULL) + if (cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pusch_Config->choice.setup->rbg_Size != NULL) rbg_size_config = 1; else rbg_size_config = 0; @@ -2647,13 +2648,13 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, size += 1; } // 1st DAI - if (secondaryCellGroup->physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook==NR_PhysicalCellGroupConfig__pdsch_HARQ_ACK_Codebook_dynamic) + if (cg->physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook==NR_PhysicalCellGroupConfig__pdsch_HARQ_ACK_Codebook_dynamic) dci_pdu->dai[0].nbits = 2; else dci_pdu->dai[0].nbits = 1; size += dci_pdu->dai[0].nbits; // 2nd DAI - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { //TODO not sure about that + if (cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { //TODO not sure about that dci_pdu->dai[1].nbits = 2; size += dci_pdu->dai[1].nbits; } @@ -2673,9 +2674,9 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, else { int lmin,Lmax = 0; int lsum = 0; - if ( secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig != NULL) { - if ( secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers != NULL) - Lmax = *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers; + if ( cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig != NULL) { + if ( cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers != NULL) + Lmax = *cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers; else AssertFatal(1==0,"MIMO on PUSCH not supported, maxMIMO_Layers needs to be set to 1\n"); } @@ -2695,7 +2696,7 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, } } // Precoding info and number of layers - long transformPrecoder = get_transformPrecoding(scc, pusch_Config, ubwp, (uint8_t*)&format, rnti_type, 0); + long transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, ubwp, (uint8_t*)&format, rnti_type, 0); if (pusch_Config->txConfig != NULL){ if (*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook){ @@ -2739,21 +2740,21 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, dci_pdu->antenna_ports.nbits = xb; size += dci_pdu->antenna_ports.nbits; // SRS request - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->supplementaryUplink==NULL) + if (cg->spCellConfig->spCellConfigDedicated->supplementaryUplink==NULL) dci_pdu->srs_request.nbits = 2; else dci_pdu->srs_request.nbits = 3; size += dci_pdu->srs_request.nbits; // CSI request - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig != NULL) { - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup->reportTriggerSize != NULL) { - dci_pdu->csi_request.nbits = *secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup->reportTriggerSize; + if (cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig != NULL) { + if (cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup->reportTriggerSize != NULL) { + dci_pdu->csi_request.nbits = *cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup->reportTriggerSize; size += dci_pdu->csi_request.nbits; } } // CBGTI - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { - int num = secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->codeBlockGroupTransmission->choice.setup->maxCodeBlockGroupsPerTransportBlock; + if (cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { + int num = cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->codeBlockGroupTransmission->choice.setup->maxCodeBlockGroupsPerTransportBlock; dci_pdu->cbgti.nbits = 2 + (num<<1); size += dci_pdu->cbgti.nbits; } @@ -2786,7 +2787,6 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, dci_pdu->frequency_domain_assignment.nbits = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) ); dci_pdu->time_domain_assignment.nbits = 4; dci_pdu->vrb_to_prb_mapping.nbits = 1; - break; case NR_DL_DCI_FORMAT_1_1: @@ -2794,19 +2794,19 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, // Format identifier size = 1; // Carrier indicator - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) { + if (cg->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) { dci_pdu->carrier_indicator.nbits=3; size += dci_pdu->carrier_indicator.nbits; } // BWP Indicator - uint8_t n_dl_bwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; + uint8_t n_dl_bwp = cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; if (n_dl_bwp < 2) dci_pdu->bwp_indicator.nbits = n_dl_bwp; else dci_pdu->bwp_indicator.nbits = 2; size += dci_pdu->bwp_indicator.nbits; // Freq domain assignment - rbg_size_config = secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->rbg_Size; + rbg_size_config = cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->rbg_Size; numRBG = getNRBG(NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE), NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE), rbg_size_config); @@ -2862,7 +2862,7 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, // HARQ PID size += 4; // DAI - if (secondaryCellGroup->physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook == NR_PhysicalCellGroupConfig__pdsch_HARQ_ACK_Codebook_dynamic) { // FIXME in case of more than one serving cell + if (cg->physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook == NR_PhysicalCellGroupConfig__pdsch_HARQ_ACK_Codebook_dynamic) { // FIXME in case of more than one serving cell dci_pdu->dai[0].nbits = 2; size += dci_pdu->dai[0].nbits; } @@ -2887,20 +2887,20 @@ uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, size += dci_pdu->transmission_configuration_indication.nbits; } // SRS request - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->supplementaryUplink==NULL) + if (cg->spCellConfig->spCellConfigDedicated->supplementaryUplink==NULL) dci_pdu->srs_request.nbits = 2; else dci_pdu->srs_request.nbits = 3; size += dci_pdu->srs_request.nbits; // CBGTI - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { - uint8_t maxCBGperTB = (secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission->choice.setup->maxCodeBlockGroupsPerTransportBlock + 1) * 2; + if (cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) { + uint8_t maxCBGperTB = (cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission->choice.setup->maxCodeBlockGroupsPerTransportBlock + 1) * 2; long *maxCWperDCI_rrc = pdsch_config->maxNrofCodeWordsScheduledByDCI; uint8_t maxCW = (maxCWperDCI_rrc == NULL) ? 1 : *maxCWperDCI_rrc; dci_pdu->cbgti.nbits = maxCBGperTB * maxCW; size += dci_pdu->cbgti.nbits; // CBGFI - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission->choice.setup->codeBlockGroupFlushIndicator) { + if (cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission->choice.setup->codeBlockGroupFlushIndicator) { dci_pdu->cbgfi.nbits = 1; size += dci_pdu->cbgfi.nbits; } @@ -2951,64 +2951,64 @@ int ul_ant_bits(NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig, long transformPrec int tdd_period_to_num[8] = {500,625,1000,1250,2000,2500,5000,10000}; -int is_nr_DL_slot(NR_ServingCellConfigCommon_t *scc,slot_t slot) { +int is_nr_DL_slot(NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon,slot_t slot) { int period,period1,period2=0; - if (scc->tdd_UL_DL_ConfigurationCommon==NULL) return(1); + if (tdd_UL_DL_ConfigurationCommon==NULL) return(1); - if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1 && - scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530) - period1 = 3000+*scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; + if (tdd_UL_DL_ConfigurationCommon->pattern1.ext1 && + tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530) + period1 = 3000+*tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; else - period1 = tdd_period_to_num[scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity]; + period1 = tdd_period_to_num[tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity]; - if (scc->tdd_UL_DL_ConfigurationCommon->pattern2) { - if (scc->tdd_UL_DL_ConfigurationCommon->pattern2->ext1 && - scc->tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530) - period2 = 3000+*scc->tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530; + if (tdd_UL_DL_ConfigurationCommon->pattern2) { + if (tdd_UL_DL_ConfigurationCommon->pattern2->ext1 && + tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530) + period2 = 3000+*tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530; else - period2 = tdd_period_to_num[scc->tdd_UL_DL_ConfigurationCommon->pattern2->dl_UL_TransmissionPeriodicity]; + period2 = tdd_period_to_num[tdd_UL_DL_ConfigurationCommon->pattern2->dl_UL_TransmissionPeriodicity]; } period = period1+period2; - int scs=scc->tdd_UL_DL_ConfigurationCommon->referenceSubcarrierSpacing; + int scs=tdd_UL_DL_ConfigurationCommon->referenceSubcarrierSpacing; int slots=period*(1<tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots ? 1 : 0); - else return(slot_in_period <= slots1+scc->tdd_UL_DL_ConfigurationCommon->pattern2->nrofDownlinkSlots ? 1 : 0); + if (slot_in_period < slots1) return(slot_in_period <= tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots ? 1 : 0); + else return(slot_in_period <= slots1+tdd_UL_DL_ConfigurationCommon->pattern2->nrofDownlinkSlots ? 1 : 0); } -int is_nr_UL_slot(NR_ServingCellConfigCommon_t *scc, slot_t slot, lte_frame_type_t frame_type) { +int is_nr_UL_slot(NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon, slot_t slot, lte_frame_type_t frame_type) { int period,period1,period2=0; // Note: condition on frame_type // goal: the UL scheduler assumes mode is TDD therefore this hack is needed to make FDD work - if (scc->tdd_UL_DL_ConfigurationCommon == NULL || frame_type == FDD) { + if (tdd_UL_DL_ConfigurationCommon == NULL || frame_type == FDD) { return(1); } - if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1 && - scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530) - period1 = 3000+*scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; + if (tdd_UL_DL_ConfigurationCommon->pattern1.ext1 && + tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530) + period1 = 3000+*tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; else - period1 = tdd_period_to_num[scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity]; + period1 = tdd_period_to_num[tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity]; - if (scc->tdd_UL_DL_ConfigurationCommon->pattern2) { - if (scc->tdd_UL_DL_ConfigurationCommon->pattern2->ext1 && - scc->tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530) - period2 = 3000+*scc->tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530; + if (tdd_UL_DL_ConfigurationCommon->pattern2) { + if (tdd_UL_DL_ConfigurationCommon->pattern2->ext1 && + tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530) + period2 = 3000+*tdd_UL_DL_ConfigurationCommon->pattern2->ext1->dl_UL_TransmissionPeriodicity_v1530; else - period2 = tdd_period_to_num[scc->tdd_UL_DL_ConfigurationCommon->pattern2->dl_UL_TransmissionPeriodicity]; + period2 = tdd_period_to_num[tdd_UL_DL_ConfigurationCommon->pattern2->dl_UL_TransmissionPeriodicity]; } period = period1+period2; - int scs=scc->tdd_UL_DL_ConfigurationCommon->referenceSubcarrierSpacing; + int scs=tdd_UL_DL_ConfigurationCommon->referenceSubcarrierSpacing; int slots=period*(1<= scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots ? 1 : 0); - else return(slot_in_period >= slots1+scc->tdd_UL_DL_ConfigurationCommon->pattern2->nrofDownlinkSlots ? 1 : 0); + if (slot_in_period < slots1) return(slot_in_period >= tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots ? 1 : 0); + else return(slot_in_period >= slots1+tdd_UL_DL_ConfigurationCommon->pattern2->nrofDownlinkSlots ? 1 : 0); } int16_t fill_dmrs_mask(NR_PDSCH_Config_t *pdsch_Config,int dmrs_TypeA_Position,int NrOfSymbols, int startSymbol, int mappingtype_fromDCI) { diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index 9c24eea772..a6adb47c0a 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -49,16 +49,16 @@ uint32_t to_nrarfcn(int nr_bandP, uint64_t dl_CarrierFreq, uint8_t scs_index, ui int16_t fill_dmrs_mask(NR_PDSCH_Config_t *pdsch_Config,int dmrs_TypeA_Position,int NrOfSymbols,int startSymbol,int mappingtype_fromDCI); -int is_nr_DL_slot(NR_ServingCellConfigCommon_t *scc,slot_t slotP); +int is_nr_DL_slot(NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon,slot_t slotP); -int is_nr_UL_slot(NR_ServingCellConfigCommon_t *scc, slot_t slotP, lte_frame_type_t frame_type); +int is_nr_UL_slot(NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon, slot_t slotP, lte_frame_type_t frame_type); -uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc, - const NR_CellGroupConfig_t *secondaryCellGroup, +uint16_t nr_dci_size(const NR_BWP_UplinkCommon_t *initialULBWP, + const NR_CellGroupConfig_t *cg, dci_pdu_rel15_t *dci_pdu, nr_dci_format_t format, - nr_rnti_type_t rnti_type, - uint16_t N_RB, + nr_rnti_type_t rnti_type, + uint16_t N_RB, int bwp_id); void find_aggregation_candidates(uint8_t *aggregation_level, @@ -113,7 +113,7 @@ int ul_ant_bits(NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig,long transformPreco int get_format0(uint8_t index, uint8_t unpaired,frequency_range_t); -int64_t *get_prach_config_info(uint32_t pointa, +int64_t *get_prach_config_info(frequency_range_t freq_range, uint8_t index, uint8_t unpaired); @@ -162,7 +162,7 @@ uint8_t get_num_dmrs_symbols(NR_PDSCH_Config_t *pdsch_Config,int dmrs_TypeA_Posi @param rnti_type rnti type @param configuredGrant indicates whether a configured grant was received or not @returns transformPrecoding value */ -uint8_t get_transformPrecoding(const NR_ServingCellConfigCommon_t *scc, +uint8_t get_transformPrecoding(const NR_BWP_UplinkCommon_t *initialUplinkBWP, const NR_PUSCH_Config_t *pusch_config, const NR_BWP_Uplink_t *ubwp, uint8_t *dci_format, diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 111fee4883..bfa5d6a93f 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -35,7 +35,9 @@ #include "NR_MAC_UE/mac_proto.h" #include "NR_MAC-CellGroupConfig.h" #include "LAYER2/NR_MAC_COMMON/nr_mac_common.h" -#include "executables/softmodem-common.h" +#include + +extern uint32_t N_RB_DL; int set_tdd_config_nr_ue(fapi_nr_config_request_t *cfg, int mu, @@ -149,6 +151,7 @@ void config_common_ue(NR_UE_MAC_INST_t *mac, fapi_nr_config_request_t *cfg = &mac->phy_config.config_req; NR_ServingCellConfigCommon_t *scc = mac->scc; + NR_ServingCellConfigCommonSIB_t *scc_SIB = mac->scc_SIB; int i; mac->phy_config.Mod_id = module_id; @@ -157,77 +160,80 @@ void config_common_ue(NR_UE_MAC_INST_t *mac, // carrier config LOG_D(MAC, "Entering UE Config Common\n"); - cfg->carrier_config.dl_bandwidth = config_bandwidth(scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth, - *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]); + AssertFatal(scc==NULL || scc_SIB==NULL,"Both scc and scc_SIB cannot be null\n"); - cfg->carrier_config.dl_frequency = from_nrarfcn(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], - *scc->ssbSubcarrierSpacing, - scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA)/1000; // freq in kHz - - for (i=0; i<5; i++) { - if (i==scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) { - cfg->carrier_config.dl_grid_size[i] = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; - cfg->carrier_config.dl_k0[i] = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->offsetToCarrier; + if (scc) { + cfg->carrier_config.dl_bandwidth = config_bandwidth(scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth, + *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]); + + cfg->carrier_config.dl_frequency = from_nrarfcn(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], + *scc->ssbSubcarrierSpacing, + scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA)/1000; // freq in kHz + + for (i=0; i<5; i++) { + if (i==scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) { + cfg->carrier_config.dl_grid_size[i] = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + cfg->carrier_config.dl_k0[i] = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->offsetToCarrier; + } + else { + cfg->carrier_config.dl_grid_size[i] = 0; + cfg->carrier_config.dl_k0[i] = 0; + } } - else { - cfg->carrier_config.dl_grid_size[i] = 0; - cfg->carrier_config.dl_k0[i] = 0; + + cfg->carrier_config.uplink_bandwidth = config_bandwidth(scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth, + *scc->uplinkConfigCommon->frequencyInfoUL->frequencyBandList->list.array[0]); + + int UL_pointA; + if (scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA == NULL) + UL_pointA = scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA; + else + UL_pointA = *scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA; + + cfg->carrier_config.uplink_frequency = from_nrarfcn(*scc->uplinkConfigCommon->frequencyInfoUL->frequencyBandList->list.array[0], + *scc->ssbSubcarrierSpacing, + UL_pointA)/1000; // freq in kHz + + + for (i=0; i<5; i++) { + if (i==scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) { + cfg->carrier_config.ul_grid_size[i] = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + cfg->carrier_config.ul_k0[i] = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->offsetToCarrier; + } + else { + cfg->carrier_config.ul_grid_size[i] = 0; + cfg->carrier_config.ul_k0[i] = 0; + } } - } + + uint32_t band = *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]; + mac->frequency_range = band<100?FR1:FR2; + + lte_frame_type_t frame_type = get_frame_type(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); - cfg->carrier_config.uplink_bandwidth = config_bandwidth(scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth, - *scc->uplinkConfigCommon->frequencyInfoUL->frequencyBandList->list.array[0]); + // cell config + + cfg->cell_config.phy_cell_id = *scc->physCellId; + cfg->cell_config.frame_duplex_type = frame_type; + + // SSB config + cfg->ssb_config.ss_pbch_power = scc->ss_PBCH_BlockPower; + cfg->ssb_config.scs_common = *scc->ssbSubcarrierSpacing; + + // SSB Table config + int scs_scaling = 1<<(cfg->ssb_config.scs_common); + if (scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA < 600000) + scs_scaling = scs_scaling*3; + if (scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA > 2016666) + scs_scaling = scs_scaling>>2; + uint32_t absolute_diff = (*scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB - scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA); + cfg->ssb_table.ssb_offset_point_a = absolute_diff/(12*scs_scaling) - 10; + cfg->ssb_table.ssb_period = *scc->ssb_periodicityServingCell; + cfg->ssb_table.ssb_subcarrier_offset = 0; // TODO currently not in RRC? - int UL_pointA; - if (scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA == NULL) - UL_pointA = scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA; - else - UL_pointA = *scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA; - - cfg->carrier_config.uplink_frequency = from_nrarfcn(*scc->uplinkConfigCommon->frequencyInfoUL->frequencyBandList->list.array[0], - *scc->ssbSubcarrierSpacing, - UL_pointA)/1000; // freq in kHz - - - for (i=0; i<5; i++) { - if (i==scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) { - cfg->carrier_config.ul_grid_size[i] = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; - cfg->carrier_config.ul_k0[i] = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->offsetToCarrier; - } - else { - cfg->carrier_config.ul_grid_size[i] = 0; - cfg->carrier_config.ul_k0[i] = 0; - } - } - - uint32_t band = *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]; - frequency_range_t frequency_range = band<100?FR1:FR2; - - mac->frame_type = get_frame_type(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); - - // cell config - - cfg->cell_config.phy_cell_id = *scc->physCellId; - cfg->cell_config.frame_duplex_type = mac->frame_type; - - // SSB config - cfg->ssb_config.ss_pbch_power = scc->ss_PBCH_BlockPower; - cfg->ssb_config.scs_common = *scc->ssbSubcarrierSpacing; - - // SSB Table config - int scs_scaling = 1<<(cfg->ssb_config.scs_common); - if (scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA < 600000) - scs_scaling = scs_scaling*3; - if (scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA > 2016666) - scs_scaling = scs_scaling>>2; - uint32_t absolute_diff = (*scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB - scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA); - cfg->ssb_table.ssb_offset_point_a = absolute_diff/(12*scs_scaling) - 10; - cfg->ssb_table.ssb_period = *scc->ssb_periodicityServingCell; - cfg->ssb_table.ssb_subcarrier_offset = 0; // TODO currently not in RRC? - - switch (scc->ssb_PositionsInBurst->present) { + switch (scc->ssb_PositionsInBurst->present) { case 1 : cfg->ssb_table.ssb_mask_list[0].ssb_mask = scc->ssb_PositionsInBurst->choice.shortBitmap.buf[0]<<24; cfg->ssb_table.ssb_mask_list[1].ssb_mask = 0; @@ -246,48 +252,48 @@ void config_common_ue(NR_UE_MAC_INST_t *mac, break; default: AssertFatal(1==0,"SSB bitmap size value %d undefined (allowed values 1,2,3) \n", scc->ssb_PositionsInBurst->present); - } - - // TDD Table Configuration - if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1 == NULL) - cfg->tdd_table.tdd_period = scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity; - else { - AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 != NULL, - "scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 is null\n"); - cfg->tdd_table.tdd_period = *scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; - } - if(cfg->cell_config.frame_duplex_type == TDD){ - LOG_I(MAC,"Setting TDD configuration period to %d\n", cfg->tdd_table.tdd_period); - int return_tdd = set_tdd_config_nr_ue(cfg, - scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots, - scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSymbols, - scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots, - scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols - ); - - if (return_tdd !=0) - LOG_E(PHY,"TDD configuration can not be done\n"); - else - LOG_I(PHY,"TDD has been properly configurated\n"); - } - - // PRACH configuration - - uint8_t nb_preambles = 64; - if(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles != NULL) - nb_preambles = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles; - - cfg->prach_config.prach_sequence_length = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.present-1; - - if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing) - cfg->prach_config.prach_sub_c_spacing = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing; - else - cfg->prach_config.prach_sub_c_spacing = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - - cfg->prach_config.restricted_set_config = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->restrictedSetConfig; - - switch (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FDM) { + } + + // TDD Table Configuration + if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1 == NULL) + cfg->tdd_table.tdd_period = scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity; + else { + AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 != NULL, + "scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 is null\n"); + cfg->tdd_table.tdd_period = *scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; + } + if(cfg->cell_config.frame_duplex_type == TDD){ + LOG_I(MAC,"Setting TDD configuration period to %d\n", cfg->tdd_table.tdd_period); + int return_tdd = set_tdd_config_nr_ue(cfg, + scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots, + scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSymbols, + scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots, + scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols + ); + + if (return_tdd !=0) + LOG_E(PHY,"TDD configuration can not be done\n"); + else + LOG_I(PHY,"TDD has been properly configurated\n"); + } + + // PRACH configuration + + uint8_t nb_preambles = 64; + if(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles != NULL) + nb_preambles = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles; + + cfg->prach_config.prach_sequence_length = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.present-1; + + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing) + cfg->prach_config.prach_sub_c_spacing = *scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing; + else + cfg->prach_config.prach_sub_c_spacing = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + + cfg->prach_config.restricted_set_config = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->restrictedSetConfig; + + switch (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FDM) { case 0 : cfg->prach_config.num_prach_fd_occasions = 1; break; @@ -302,36 +308,187 @@ void config_common_ue(NR_UE_MAC_INST_t *mac, break; default: AssertFatal(1==0,"msg1 FDM identifier %ld undefined (0,1,2,3) \n", scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FDM); + } + + cfg->prach_config.num_prach_fd_occasions_list = (fapi_nr_num_prach_fd_occasions_t *) malloc(cfg->prach_config.num_prach_fd_occasions*sizeof(fapi_nr_num_prach_fd_occasions_t)); + for (i=0; iprach_config.num_prach_fd_occasions; i++) { + cfg->prach_config.num_prach_fd_occasions_list[i].num_prach_fd_occasions = i; + if (cfg->prach_config.prach_sequence_length) + cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l139; + else + cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l839; + + cfg->prach_config.num_prach_fd_occasions_list[i].k1 = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FrequencyStart; + cfg->prach_config.num_prach_fd_occasions_list[i].prach_zero_corr_conf = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.zeroCorrelationZoneConfig; + cfg->prach_config.num_prach_fd_occasions_list[i].num_root_sequences = compute_nr_root_seq(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup, + nb_preambles, frame_type,mac->frequency_range); + //cfg->prach_config.num_prach_fd_occasions_list[i].num_unused_root_sequences = ??? + } + + cfg->prach_config.ssb_per_rach = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present-1; + + } // scc + + else { // scc_SIB + + cfg->carrier_config.dl_bandwidth = config_bandwidth(scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->carrierBandwidth, + *scc_SIB->downlinkConfigCommon.frequencyInfoDL.frequencyBandList.list.array[0]->freqBandIndicatorNR); + + cfg->carrier_config.dl_frequency = downlink_frequency[0][0] - (10+scc_SIB->downlinkConfigCommon.frequencyInfoDL.offsetToPointA)*(15<downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing); + + for (i=0; i<5; i++) { + if (i==scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) { + cfg->carrier_config.dl_grid_size[i] = scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + cfg->carrier_config.dl_k0[i] = scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->offsetToCarrier; + } + else { + cfg->carrier_config.dl_grid_size[i] = 0; + cfg->carrier_config.dl_k0[i] = 0; + } + } + + cfg->carrier_config.uplink_bandwidth = config_bandwidth(scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->carrierBandwidth, + scc_SIB->uplinkConfigCommon->frequencyInfoUL.frequencyBandList==NULL ? *scc_SIB->downlinkConfigCommon.frequencyInfoDL.frequencyBandList.list.array[0]->freqBandIndicatorNR : *scc_SIB->uplinkConfigCommon->frequencyInfoUL.frequencyBandList->list.array[0]->freqBandIndicatorNR); + + + if (scc_SIB->uplinkConfigCommon->frequencyInfoUL.absoluteFrequencyPointA == NULL) + cfg->carrier_config.uplink_frequency = cfg->carrier_config.dl_frequency; + else + cfg->carrier_config.uplink_frequency = from_nrarfcn(*scc_SIB->uplinkConfigCommon->frequencyInfoUL.frequencyBandList->list.array[0]->freqBandIndicatorNR, + scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + *scc_SIB->uplinkConfigCommon->frequencyInfoUL.absoluteFrequencyPointA)/1000; // freq in kHz + + + for (i=0; i<5; i++) { + if (i==scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing) { + cfg->carrier_config.ul_grid_size[i] = scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + cfg->carrier_config.ul_k0[i] = scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->offsetToCarrier; + } + else { + cfg->carrier_config.ul_grid_size[i] = 0; + cfg->carrier_config.ul_k0[i] = 0; + } + } + + uint32_t band = *scc_SIB->downlinkConfigCommon.frequencyInfoDL.frequencyBandList.list.array[0]->freqBandIndicatorNR; + frequency_range_t frequency_range = band<100?FR1:FR2; + + lte_frame_type_t frame_type = get_frame_type(band, get_softmodem_params()->numerology); + + // cell config + + cfg->cell_config.phy_cell_id = mac->physCellId; + cfg->cell_config.frame_duplex_type = frame_type; + + // SSB config + cfg->ssb_config.ss_pbch_power = scc_SIB->ss_PBCH_BlockPower; + cfg->ssb_config.scs_common = get_softmodem_params()->numerology; + + // SSB Table config + + cfg->ssb_table.ssb_offset_point_a = scc_SIB->downlinkConfigCommon.frequencyInfoDL.offsetToPointA; + cfg->ssb_table.ssb_period = scc_SIB->ssb_PeriodicityServingCell; + cfg->ssb_table.ssb_subcarrier_offset = 0; // TODO currently not in RRC? + + AssertFatal(scc_SIB->ssb_PositionsInBurst.groupPresence==NULL, "Cannot handle more than 8 SSBs for now (%x.%x.%x.%x.%x.%x.%x.%x)\n", + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[0], + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[1], + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[2], + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[3], + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[4], + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[5], + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[6], + scc_SIB->ssb_PositionsInBurst.groupPresence->buf[7]); + cfg->ssb_table.ssb_mask_list[0].ssb_mask = scc_SIB->ssb_PositionsInBurst.inOneGroup.buf[0]<<24; + cfg->ssb_table.ssb_mask_list[1].ssb_mask = 0; + + + // TDD Table Configuration + if (scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.ext1 == NULL) + cfg->tdd_table.tdd_period = scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity; + else { + AssertFatal(scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 != NULL, + "scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530 is null\n"); + cfg->tdd_table.tdd_period = *scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; + } + if(cfg->cell_config.frame_duplex_type == TDD){ + LOG_I(MAC,"Setting TDD configuration period to %d\n", cfg->tdd_table.tdd_period); + int return_tdd = set_tdd_config_nr_ue(cfg, + scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots, + scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSymbols, + scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots, + scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols + ); + + if (return_tdd !=0) + LOG_E(PHY,"TDD configuration can not be done\n"); + else + LOG_I(PHY,"TDD has been properly configurated\n"); + } + + // PRACH configuration + + uint8_t nb_preambles = 64; + if(scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles != NULL) + nb_preambles = *scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->totalNumberOfRA_Preambles; + + cfg->prach_config.prach_sequence_length = scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.present-1; + + if (scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing) + cfg->prach_config.prach_sub_c_spacing = *scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->msg1_SubcarrierSpacing; + else + cfg->prach_config.prach_sub_c_spacing = scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + + cfg->prach_config.restricted_set_config = scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->restrictedSetConfig; + + switch (scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FDM) { + case 0 : + cfg->prach_config.num_prach_fd_occasions = 1; + break; + case 1 : + cfg->prach_config.num_prach_fd_occasions = 2; + break; + case 2 : + cfg->prach_config.num_prach_fd_occasions = 4; + break; + case 3 : + cfg->prach_config.num_prach_fd_occasions = 8; + break; + default: + AssertFatal(1==0,"msg1 FDM identifier %ld undefined (0,1,2,3) \n", scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FDM); } cfg->prach_config.num_prach_fd_occasions_list = (fapi_nr_num_prach_fd_occasions_t *) malloc(cfg->prach_config.num_prach_fd_occasions*sizeof(fapi_nr_num_prach_fd_occasions_t)); for (i=0; iprach_config.num_prach_fd_occasions; i++) { cfg->prach_config.num_prach_fd_occasions_list[i].num_prach_fd_occasions = i; if (cfg->prach_config.prach_sequence_length) - cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l139; + cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index = scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l139; else - cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l839; - if (get_softmodem_params()->sa) { - cfg->prach_config.num_prach_fd_occasions_list[i].k1 = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) + scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FrequencyStart + (get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing, scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing ) * i); - } else { - cfg->prach_config.num_prach_fd_occasions_list[i].k1 = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FrequencyStart + (get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing, scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing ) * i); - } - cfg->prach_config.num_prach_fd_occasions_list[i].prach_zero_corr_conf = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.zeroCorrelationZoneConfig; - cfg->prach_config.num_prach_fd_occasions_list[i].num_root_sequences = compute_nr_root_seq(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup, nb_preambles, mac->frame_type, frequency_range); + cfg->prach_config.num_prach_fd_occasions_list[i].prach_root_sequence_index = scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.choice.l839; + cfg->prach_config.num_prach_fd_occasions_list[i].k1 = NRRIV2PRBOFFSET(scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE) + scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->rach_ConfigGeneric.msg1_FrequencyStart + (get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing, scc_SIB->uplinkConfigCommon->frequencyInfoUL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing ) * i); + cfg->prach_config.num_prach_fd_occasions_list[i].prach_zero_corr_conf = scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->rach_ConfigGeneric.zeroCorrelationZoneConfig; + cfg->prach_config.num_prach_fd_occasions_list[i].num_root_sequences = compute_nr_root_seq(scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup, nb_preambles, mac->frame_type, frequency_range); //cfg->prach_config.num_prach_fd_occasions_list[i].num_unused_root_sequences = ??? } - cfg->prach_config.ssb_per_rach = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present-1; + cfg->prach_config.ssb_per_rach = scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present-1; + + } + } /** \brief This function performs some configuration routines according to clause 12 "Bandwidth part operation" 3GPP TS 38.213 version 16.3.0 Release 16 @param NR_UE_MAC_INST_t mac: pointer to local MAC instance @returns void */ + void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format){ - NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; + NR_ServingCellConfig_t *scd = mac->cg->spCellConfig->spCellConfigDedicated; if (bwp_ind && dci_format){ @@ -366,6 +523,70 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format } + +void fill_default_coreset_zero(NR_UE_MAC_INST_t *mac) { + + // Search space zero + + if(mac->search_space_zero == NULL) mac->search_space_zero=calloc(1,sizeof(*mac->search_space_zero)); + if(mac->search_space_zero->controlResourceSetId == NULL) mac->search_space_zero->controlResourceSetId=calloc(1,sizeof(*mac->search_space_zero->controlResourceSetId)); + if(mac->search_space_zero->monitoringSymbolsWithinSlot == NULL) mac->search_space_zero->monitoringSymbolsWithinSlot = calloc(1,sizeof(*mac->search_space_zero->monitoringSymbolsWithinSlot)); + if(mac->search_space_zero->monitoringSymbolsWithinSlot->buf == NULL) mac->search_space_zero->monitoringSymbolsWithinSlot->buf = calloc(1,2); + if(mac->search_space_zero->nrofCandidates == NULL) mac->search_space_zero->nrofCandidates = calloc(1,sizeof(*mac->search_space_zero->nrofCandidates)); + if(mac->search_space_zero->searchSpaceType == NULL) mac->search_space_zero->searchSpaceType = calloc(1,sizeof(*mac->search_space_zero->searchSpaceType)); + if(mac->search_space_zero->searchSpaceType->choice.common == NULL) mac->search_space_zero->searchSpaceType->choice.common=calloc(1,sizeof(*mac->search_space_zero->searchSpaceType->choice.common)); + if(mac->search_space_zero->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0 == NULL) mac->search_space_zero->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0 = calloc(1,sizeof(*mac->search_space_zero->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0)); + mac->search_space_zero->searchSpaceId = 0; + *mac->search_space_zero->controlResourceSetId = 0; + mac->search_space_zero->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*mac->search_space_zero->monitoringSlotPeriodicityAndOffset)); + mac->search_space_zero->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; + mac->search_space_zero->duration=NULL; + // should be '1100 0000 0000 00'B (LSB first!), first two symbols in slot, adjust if needed + mac->search_space_zero->monitoringSymbolsWithinSlot->buf[1] = 0; + mac->search_space_zero->monitoringSymbolsWithinSlot->buf[0] = (1<<7); + mac->search_space_zero->monitoringSymbolsWithinSlot->size = 2; + mac->search_space_zero->monitoringSymbolsWithinSlot->bits_unused = 2; + + // FIXME: update values from TS38.213 Section 10.1 Table 10.1-1: CCE aggregation levels and maximum number of PDCCH candidates per CCE aggregation level for CSS sets configured by searchSpaceSIB1 + mac->search_space_zero->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; + mac->search_space_zero->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; + mac->search_space_zero->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n2; + mac->search_space_zero->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; + mac->search_space_zero->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; + mac->search_space_zero->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_common; + + // Coreset0 + if(mac->coreset0 == NULL) mac->coreset0 = calloc(1,sizeof(*mac->coreset0)); + mac->coreset0->controlResourceSetId = 0; + // frequencyDomainResources '11111111 00000000 00000000 00000000 00000000 00000'B, + if(mac->coreset0->frequencyDomainResources.buf == NULL) mac->coreset0->frequencyDomainResources.buf = calloc(1,6); + mac->coreset0->frequencyDomainResources.buf[0] = 0xff; + mac->coreset0->frequencyDomainResources.buf[1] = 0; + mac->coreset0->frequencyDomainResources.buf[2] = 0; + mac->coreset0->frequencyDomainResources.buf[3] = 0; + mac->coreset0->frequencyDomainResources.buf[4] = 0; + mac->coreset0->frequencyDomainResources.buf[5] = 0; + mac->coreset0->frequencyDomainResources.size = 6; + mac->coreset0->frequencyDomainResources.bits_unused = 3; + mac->coreset0->duration = 1; + mac->coreset0->cce_REG_MappingType.present=NR_ControlResourceSet__cce_REG_MappingType_PR_interleaved; + mac->coreset0->cce_REG_MappingType.choice.interleaved=calloc(1,sizeof(*mac->coreset0->cce_REG_MappingType.choice.interleaved)); + mac->coreset0->cce_REG_MappingType.choice.interleaved->reg_BundleSize = NR_ControlResourceSet__cce_REG_MappingType__interleaved__reg_BundleSize_n6; + mac->coreset0->cce_REG_MappingType.choice.interleaved->interleaverSize = NR_ControlResourceSet__cce_REG_MappingType__interleaved__interleaverSize_n2; + mac->coreset0->cce_REG_MappingType.choice.interleaved->shiftIndex = NULL; + mac->coreset0->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; + if(mac->coreset0->tci_StatesPDCCH_ToAddList == NULL) mac->coreset0->tci_StatesPDCCH_ToAddList = calloc(1,sizeof(*mac->coreset0->tci_StatesPDCCH_ToAddList)); + NR_TCI_StateId_t *tci[8]; + for (int i=0;i<8;i++) { + tci[i]=calloc(1,sizeof(*tci[i])); + *tci[i] = i; + ASN_SEQUENCE_ADD(&mac->coreset0->tci_StatesPDCCH_ToAddList->list,tci[i]); + } + mac->coreset0->tci_StatesPDCCH_ToReleaseList = NULL; + mac->coreset0->tci_PresentInDCI = NULL; + mac->coreset0->pdcch_DMRS_ScramblingID = NULL; +} + /** \brief This function is relavant for the UE procedures for control. It loads the search spaces, the BWPs and the CORESETs into the MAC instance and \brief performs assert checks on the relevant RRC configuration. @param NR_UE_MAC_INST_t mac: pointer to local MAC instance @@ -375,7 +596,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ uint8_t coreset_id = 1, ss_id; - NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; + NR_ServingCellConfig_t *scd = mac->cg->spCellConfig->spCellConfigDedicated; AssertFatal(scd->downlinkBWP_ToAddModList != NULL, "downlinkBWP_ToAddModList is null\n"); AssertFatal(scd->downlinkBWP_ToAddModList->list.count <= 4, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); @@ -455,65 +676,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ ss_id++; } - // TODO: Merge this code in a single function as fill_default_searchSpaceZero() in rrc_gNB_reconfig.c - // Search space zero - if(mac->search_space_zero == NULL) mac->search_space_zero=calloc(1,sizeof(*mac->search_space_zero)); - if(mac->search_space_zero->controlResourceSetId == NULL) mac->search_space_zero->controlResourceSetId=calloc(1,sizeof(*mac->search_space_zero->controlResourceSetId)); - if(mac->search_space_zero->monitoringSymbolsWithinSlot == NULL) mac->search_space_zero->monitoringSymbolsWithinSlot = calloc(1,sizeof(*mac->search_space_zero->monitoringSymbolsWithinSlot)); - if(mac->search_space_zero->monitoringSymbolsWithinSlot->buf == NULL) mac->search_space_zero->monitoringSymbolsWithinSlot->buf = calloc(1,2); - if(mac->search_space_zero->nrofCandidates == NULL) mac->search_space_zero->nrofCandidates = calloc(1,sizeof(*mac->search_space_zero->nrofCandidates)); - if(mac->search_space_zero->searchSpaceType == NULL) mac->search_space_zero->searchSpaceType = calloc(1,sizeof(*mac->search_space_zero->searchSpaceType)); - if(mac->search_space_zero->searchSpaceType->choice.common == NULL) mac->search_space_zero->searchSpaceType->choice.common=calloc(1,sizeof(*mac->search_space_zero->searchSpaceType->choice.common)); - if(mac->search_space_zero->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0 == NULL) mac->search_space_zero->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0 = calloc(1,sizeof(*mac->search_space_zero->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0)); - mac->search_space_zero->searchSpaceId = 0; - *mac->search_space_zero->controlResourceSetId = 0; - mac->search_space_zero->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*mac->search_space_zero->monitoringSlotPeriodicityAndOffset)); - mac->search_space_zero->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; - mac->search_space_zero->duration=NULL; - // should be '1100 0000 0000 00'B (LSB first!), first two symbols in slot, adjust if needed - mac->search_space_zero->monitoringSymbolsWithinSlot->buf[1] = 0; - mac->search_space_zero->monitoringSymbolsWithinSlot->buf[0] = (1<<7); - mac->search_space_zero->monitoringSymbolsWithinSlot->size = 2; - mac->search_space_zero->monitoringSymbolsWithinSlot->bits_unused = 2; - - // FIXME: update values from TS38.213 Section 10.1 Table 10.1-1: CCE aggregation levels and maximum number of PDCCH candidates per CCE aggregation level for CSS sets configured by searchSpaceSIB1 - mac->search_space_zero->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; - mac->search_space_zero->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; - mac->search_space_zero->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n2; - mac->search_space_zero->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; - mac->search_space_zero->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; - mac->search_space_zero->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_common; - - // Coreset0 - if(mac->coreset0 == NULL) mac->coreset0 = calloc(1,sizeof(*mac->coreset0)); - mac->coreset0->controlResourceSetId = 0; - // frequencyDomainResources '11111111 00000000 00000000 00000000 00000000 00000'B, - if(mac->coreset0->frequencyDomainResources.buf == NULL) mac->coreset0->frequencyDomainResources.buf = calloc(1,6); - mac->coreset0->frequencyDomainResources.buf[0] = 0xff; - mac->coreset0->frequencyDomainResources.buf[1] = 0; - mac->coreset0->frequencyDomainResources.buf[2] = 0; - mac->coreset0->frequencyDomainResources.buf[3] = 0; - mac->coreset0->frequencyDomainResources.buf[4] = 0; - mac->coreset0->frequencyDomainResources.buf[5] = 0; - mac->coreset0->frequencyDomainResources.size = 6; - mac->coreset0->frequencyDomainResources.bits_unused = 3; - mac->coreset0->duration = 1; - mac->coreset0->cce_REG_MappingType.present=NR_ControlResourceSet__cce_REG_MappingType_PR_interleaved; - mac->coreset0->cce_REG_MappingType.choice.interleaved=calloc(1,sizeof(*mac->coreset0->cce_REG_MappingType.choice.interleaved)); - mac->coreset0->cce_REG_MappingType.choice.interleaved->reg_BundleSize = NR_ControlResourceSet__cce_REG_MappingType__interleaved__reg_BundleSize_n6; - mac->coreset0->cce_REG_MappingType.choice.interleaved->interleaverSize = NR_ControlResourceSet__cce_REG_MappingType__interleaved__interleaverSize_n2; - mac->coreset0->cce_REG_MappingType.choice.interleaved->shiftIndex = NULL; - mac->coreset0->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; - if(mac->coreset0->tci_StatesPDCCH_ToAddList == NULL) mac->coreset0->tci_StatesPDCCH_ToAddList = calloc(1,sizeof(*mac->coreset0->tci_StatesPDCCH_ToAddList)); - NR_TCI_StateId_t *tci[8]; - for (int i=0;i<8;i++) { - tci[i]=calloc(1,sizeof(*tci[i])); - *tci[i] = i; - ASN_SEQUENCE_ADD(&mac->coreset0->tci_StatesPDCCH_ToAddList->list,tci[i]); - } - mac->coreset0->tci_StatesPDCCH_ToReleaseList = NULL; - mac->coreset0->tci_PresentInDCI = NULL; - mac->coreset0->pdcch_DMRS_ScramblingID = NULL; + fill_default_coreset_zero(mac); } @@ -522,10 +685,11 @@ int nr_rrc_mac_config_req_ue( int cc_idP, uint8_t gNB_index, NR_MIB_t *mibP, - // NR_ServingCellConfigCommon_t *sccP, + NR_ServingCellConfigCommonSIB_t *sccP, // NR_MAC_CellGroupConfig_t *mac_cell_group_configP, // NR_PhysicalCellGroupConfig_t *phy_cell_group_configP, - NR_CellGroupConfig_t *cell_group_config ){ + NR_CellGroupConfig_t *cell_group_config, + NR_CellGroupConfig_t *scell_group_config){ NR_UE_MAC_INST_t *mac = get_mac_inst(module_id); RA_config_t *ra = &mac->ra; @@ -533,25 +697,71 @@ int nr_rrc_mac_config_req_ue( // TODO do something FAPI-like P5 L1/L2 config interface in config_si, config_mib, etc. if(mibP != NULL){ - mac->mib = mibP; // update by every reception - } - if(cell_group_config != NULL ){ - mac->scg = cell_group_config; - mac->servCellIndex = *cell_group_config->spCellConfig->servCellIndex; + // if this is the first MIB intialize coreset0 for SA + if (mac->mib == NULL) fill_default_coreset_zero(mac); + + mac->mib = mibP; // update by every reception + mac->phy_config.Mod_id = module_id; + mac->phy_config.CC_id = cc_idP; + mac->phy_config.config_req.ssb_table.ssb_subcarrier_offset = 0; // TODO currently not in RRC? + mac->phy_config.config_req.tdd_table.tdd_period_in_slots=5<numerology; + mac->phy_config.config_req.ssb_table.ssb_offset_point_a = (N_RB_DL-20)>>1; + } + AssertFatal(scell_group_config == NULL || cell_group_config == NULL, + "both scell_group_config and cell_group_config cannot be non-NULL\n"); + + if (sccP != NULL) { + + mac->scc_SIB=sccP; + LOG_I(MAC,"Keeping ServingCellConfigCommonSIB\n"); + config_common_ue(mac,module_id,cc_idP); + int num_slots_ul = mac->scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots; + if (mac->scc_SIB->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols>0) num_slots_ul++; + LOG_I(MAC, "Initializing ul_config_request. num_slots_ul = %d\n", num_slots_ul); + mac->ul_config_request = (fapi_nr_ul_config_request_t *)calloc(num_slots_ul, sizeof(fapi_nr_ul_config_request_t)); + // Setup the SSB to Rach Occasions mapping according to the config + build_ssb_to_ro_map(mac);//->scc, mac->phy_config.config_req.cell_config.frame_duplex_type); + mac->if_module->phy_config_request(&mac->phy_config); + mac->common_configuration_complete = 1; + } + if(scell_group_config != NULL ){ + mac->cg = scell_group_config; + mac->servCellIndex = *scell_group_config->spCellConfig->servCellIndex; config_control_ue(mac); - if (cell_group_config->spCellConfig->reconfigurationWithSync) { - if (cell_group_config->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated) { - ra->rach_ConfigDedicated = cell_group_config->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink; + if (scell_group_config->spCellConfig->reconfigurationWithSync) { + if (scell_group_config->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated) { + ra->rach_ConfigDedicated = scell_group_config->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink; } - mac->scc = cell_group_config->spCellConfig->reconfigurationWithSync->spCellConfigCommon; + mac->scc = scell_group_config->spCellConfig->reconfigurationWithSync->spCellConfigCommon; + mac->physCellId = *mac->scc->physCellId; config_common_ue(mac,module_id,cc_idP); - mac->crnti = cell_group_config->spCellConfig->reconfigurationWithSync->newUE_Identity; + mac->crnti = scell_group_config->spCellConfig->reconfigurationWithSync->newUE_Identity; LOG_I(MAC,"Configuring CRNTI %x\n",mac->crnti); } // Setup the SSB to Rach Occasions mapping according to the config - build_ssb_to_ro_map(mac->scc, mac->phy_config.config_req.cell_config.frame_duplex_type); + build_ssb_to_ro_map(mac); + } + else if (cell_group_config != NULL){ + LOG_I(MAC,"Applying CellGroupConfig from gNodeB\n"); + mac->cg = cell_group_config; + mac->servCellIndex = cell_group_config->spCellConfig->servCellIndex ? *cell_group_config->spCellConfig->servCellIndex : 0; + if(get_softmodem_params()->phy_test==1 || get_softmodem_params()->do_ra==1) { + config_control_ue(mac); + if (cell_group_config->spCellConfig->reconfigurationWithSync) { + if (cell_group_config->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated) { + ra->rach_ConfigDedicated = cell_group_config->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink; + } + mac->scc = cell_group_config->spCellConfig->reconfigurationWithSync->spCellConfigCommon; + config_common_ue(mac,module_id,cc_idP); + mac->crnti = cell_group_config->spCellConfig->reconfigurationWithSync->newUE_Identity; + LOG_I(MAC,"Configuring CRNTI %x\n",mac->crnti); + } + + // Setup the SSB to Rach Occasions mapping according to the config + build_ssb_to_ro_map(mac); + } /* if(mac_cell_group_configP != NULL){ diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index c33173cf38..3347ddf19c 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -315,10 +315,13 @@ typedef struct { typedef struct { NR_ServingCellConfigCommon_t *scc; - NR_CellGroupConfig_t *scg; + NR_ServingCellConfigCommonSIB_t *scc_SIB; + NR_CellGroupConfig_t *cg; int servCellIndex; NR_CSI_ReportConfig_t *csirc; + long physCellId; //// MAC config + int common_configuration_complete; NR_DRX_Config_t *drx_Config; NR_SchedulingRequestConfig_t *schedulingRequestConfig; NR_BSR_Config_t *bsr_Config; @@ -361,7 +364,8 @@ typedef struct { uint8_t mib_ssb; /// Last NDI of UL HARQ processes uint8_t UL_ndi[NR_MAX_HARQ_PROCESSES]; - + /// first ULTX of UL HARQ processes + int first_ul_tx[NR_MAX_HARQ_PROCESSES]; //// FAPI-like interface message fapi_nr_ul_config_request_t *ul_config_request; fapi_nr_dl_config_request_t dl_config_request; @@ -380,6 +384,7 @@ typedef struct { NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config; NR_SearchSpace_t *search_space_zero; NR_ControlResourceSet_t *coreset0; + frequency_range_t frequency_range; dci_pdu_rel15_t def_dci_pdu_rel15[8]; @@ -502,5 +507,8 @@ typedef struct ssb_list_info { uint8_t nb_tx_ssb; } ssb_list_info_t; +void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, fapi_nr_dl_config_request_t *dl_config, int rnti_type, int ss_id); +void fill_dci_search_candidates(NR_SearchSpace_t *ss,fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15); + /*@}*/ #endif /*__LAYER2_MAC_DEFS_H__ */ diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 394ab58fc7..2d00aa0003 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -53,7 +53,8 @@ int8_t nr_ue_decode_mib( uint8_t extra_bits, uint32_t ssb_length, uint32_t ssb_index, - void *pduP, + void *pduP, + uint16_t ssb_start_subcarrier, uint16_t cell_id ); /**\brief decode SIB1 and other SIs pdus in NR_UE, from if_module dl_ind @@ -82,8 +83,10 @@ int nr_rrc_mac_config_req_ue( int cc_idP, uint8_t gNB_index, NR_MIB_t *mibP, - //NR_ServingCellConfigCommon_t *sccP, - NR_CellGroupConfig_t *cell_group_config); + NR_ServingCellConfigCommonSIB_t *sccP, + NR_CellGroupConfig_t *cell_group_config, + NR_CellGroupConfig_t *scell_group_config +); /**\brief initialization NR UE MAC instance(s), total number of MAC instance based on NB_NR_UE_MAC_INST*/ NR_UE_MAC_INST_t * nr_l2_init_ue(NR_UE_RRC_INST_t* rrc_inst); @@ -166,6 +169,10 @@ uint16_t nr_generate_ulsch_pdu(uint8_t *sdus_payload, unsigned short post_padding, uint16_t buflen); +void fill_dci_search_candidates(NR_SearchSpace_t *ss,fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15); + +void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, fapi_nr_dl_config_request_t *dl_config, int rnti_type, int ss_id); + void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl_config, frame_t frame, int slot); uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, @@ -301,7 +308,7 @@ void get_num_re_dmrs(nfapi_nr_ue_pusch_pdu_t *pusch_pdu, uint8_t *nb_dmrs_re_per_rb, uint16_t *number_dmrs_symbols); -void build_ssb_to_ro_map(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired); +void build_ssb_to_ro_map(NR_UE_MAC_INST_t *mac); void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format); diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index bbb44ba47a..768f65bf63 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -39,6 +39,7 @@ #include "PHY/defs_UE.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h" #include "executables/softmodem-common.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp.h" static NR_UE_MAC_INST_t *nr_ue_mac_inst; @@ -50,12 +51,26 @@ NR_UE_MAC_INST_t * nr_l2_init_ue(NR_UE_RRC_INST_t* rrc_inst) //init mac here nr_ue_mac_inst = (NR_UE_MAC_INST_t *)calloc(sizeof(NR_UE_MAC_INST_t),NB_NR_UE_MAC_INST); - if (rrc_inst) { - nr_rrc_mac_config_req_ue(0,0,0,NULL,rrc_inst->cell_group_config); - if (IS_SOFTMODEM_NOS1){ + for (int j=0;jscell_group_config || rrc_inst->cell_group_config)) { + + if(rrc_inst->scell_group_config) { + nr_rrc_mac_config_req_ue(0,0,0,NULL,NULL,NULL,rrc_inst->scell_group_config); + //if (IS_SOFTMODEM_NOS1){ + // AssertFatal(rlc_module_init(0) == 0, "%s: Could not initialize RLC layer\n", __FUNCTION__); + // nr_pdcp_layer_init(); + // nr_DRB_preconfiguration(nr_ue_mac_inst->crnti); + //} + } else if (rrc_inst->cell_group_config) { + nr_rrc_mac_config_req_ue(0,0,0,NULL,NULL,rrc_inst->cell_group_config,NULL); AssertFatal(rlc_module_init(0) == 0, "%s: Could not initialize RLC layer\n", __FUNCTION__); - pdcp_layer_init(); - nr_DRB_preconfiguration(nr_ue_mac_inst->crnti); + if (IS_SOFTMODEM_NOS1){ + pdcp_layer_init(); + nr_DRB_preconfiguration(nr_ue_mac_inst->crnti); + } } // Allocate memory for ul_config_request in the mac instance. This is now a pointer and will @@ -68,8 +83,14 @@ NR_UE_MAC_INST_t * nr_l2_init_ue(NR_UE_RRC_INST_t* rrc_inst) LOG_D(MAC, "Initializing ul_config_request. num_slots_ul = %d\n", num_slots_ul); nr_ue_mac_inst->ul_config_request = (fapi_nr_ul_config_request_t *)calloc(num_slots_ul, sizeof(fapi_nr_ul_config_request_t)); } + + } else { + LOG_I(MAC,"Running without CellGroupConfig\n"); + nr_rrc_mac_config_req_ue(0,0,0,NULL,NULL,NULL,NULL); + if(get_softmodem_params()->sa == 1) { + AssertFatal(rlc_module_init(0) == 0, "%s: Could not initialize RLC layer\n", __FUNCTION__); + } } - else LOG_I(MAC,"Running without RRC instance\n"); return (nr_ue_mac_inst); } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_l1_helpers.c b/openair2/LAYER2/NR_MAC_UE/nr_l1_helpers.c index 2f4697c25d..7f1011fb28 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_l1_helpers.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_l1_helpers.c @@ -41,10 +41,11 @@ int8_t nr_get_DELTA_PREAMBLE(module_id_t mod_id, int CC_id, uint16_t prach_format){ NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); - NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = (mac->scc!=NULL) ? + mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup: + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; NR_SubcarrierSpacing_t scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing; - int prach_sequence_length = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.present - 1; + int prach_sequence_length = (mac->scc!=NULL)?(mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.present - 1) : (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup->prach_RootSequenceIndex.present-1); uint8_t prachConfigIndex, mu; AssertFatal(CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); @@ -145,11 +146,10 @@ int8_t nr_get_DELTA_PREAMBLE(module_id_t mod_id, int CC_id, uint16_t prach_forma int nr_get_Po_NOMINAL_PUSCH(NR_PRACH_RESOURCES_t *prach_resources, module_id_t mod_id, uint8_t CC_id){ NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); - NR_ServingCellConfigCommon_t *scc = mac->scc; int8_t receivedTargerPower; int8_t delta_preamble; - NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = (mac->scc != NULL) ? mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup: mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; long preambleReceivedTargetPower = nr_rach_ConfigCommon->rach_ConfigGeneric.preambleReceivedTargetPower; delta_preamble = nr_get_DELTA_PREAMBLE(mod_id, CC_id, prach_resources->prach_format); @@ -205,7 +205,8 @@ void get_num_re_dmrs(nfapi_nr_ue_pusch_pdu_t *pusch_pdu, long nr_get_Pcmax(module_id_t mod_id){ NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); - uint32_t band = *mac->scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]; + uint32_t band = (mac->scc!=NULL) ? *mac->scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0] : + *mac->scc_SIB->downlinkConfigCommon.frequencyInfoDL.frequencyBandList.list.array[0]->freqBandIndicatorNR; NR_P_Max_t p_max = 0; uint8_t P_powerclass = 23; uint8_t delta_P_powerclass = 0; @@ -224,17 +225,18 @@ long nr_get_Pcmax(module_id_t mod_id){ delta_MPR_c = 0.5; } - if (mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->ext1){ - if (*mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->ext1->powerBoostPi2BPSK == 1){ + if (mac->cg && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->ext1){ + if (*mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->ext1->powerBoostPi2BPSK == 1){ // TbD: assuming power class 3 capable UE operating in TDD bands n40, n41, n77, n78, and n79 with Pi/2 BPSK modulation delta_P_powerclass = -3; p_max += 3; } } - if (mac->scc->uplinkConfigCommon->frequencyInfoUL->p_Max){ + NR_P_Max_t *p_Max = (mac->scc!=NULL) ? mac->scc->uplinkConfigCommon->frequencyInfoUL->p_Max : mac->scc_SIB->uplinkConfigCommon->frequencyInfoUL.p_Max; + if (p_Max){ - p_max += *mac->scc->uplinkConfigCommon->frequencyInfoUL->p_Max; + p_max += *p_Max; LOG_D(MAC, "In %s maximum UL transmission power p_max is %ld dBm \n", __FUNCTION__, p_max); @@ -256,4 +258,4 @@ long nr_get_Pcmax(module_id_t mod_id){ return P_cmax; -} \ No newline at end of file +} diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index 7f4d536e3f..7c54c85632 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -246,16 +246,18 @@ void ra_preambles_config(NR_PRACH_RESOURCES_t *prach_resources, NR_UE_MAC_INST_t long deltaPreamble_Msg3 = 0; uint8_t noGroupB = 0; RA_config_t *ra = &mac->ra; - NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; - NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &nr_rach_ConfigCommon->rach_ConfigGeneric; + NR_RACH_ConfigCommon_t *setup; + if (mac->scc) setup = mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + else setup = mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; + NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric; - if (scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->msg3_DeltaPreamble){ - deltaPreamble_Msg3 = (*scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->msg3_DeltaPreamble) * 2; // dB + NR_BWP_UplinkCommon_t *initialUplinkBWP = (mac->scc) ? mac->scc->uplinkConfigCommon->initialUplinkBWP : &mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP; + if (initialUplinkBWP->pusch_ConfigCommon->choice.setup->msg3_DeltaPreamble){ + deltaPreamble_Msg3 = (*initialUplinkBWP->pusch_ConfigCommon->choice.setup->msg3_DeltaPreamble) * 2; // dB LOG_D(MAC, "In %s: deltaPreamble_Msg3 set to %ld\n", __FUNCTION__, deltaPreamble_Msg3); } - if (!nr_rach_ConfigCommon->groupBconfigured) { + if (!setup->groupBconfigured) { noGroupB = 1; LOG_D(MAC, "In %s:%d: preambles group B is not configured...\n", __FUNCTION__, __LINE__); } else { @@ -263,8 +265,8 @@ void ra_preambles_config(NR_PRACH_RESOURCES_t *prach_resources, NR_UE_MAC_INST_t // - Random Access Preambles group B is configured for 4-step RA type // - Defining the number of RA preambles in RA Preamble Group A for each SSB LOG_D(MAC, "In %s:%d: preambles group B is configured...\n", __FUNCTION__, __LINE__); - sizeOfRA_PreamblesGroupA = nr_rach_ConfigCommon->groupBconfigured->numberOfRA_PreamblesGroupA; - switch (nr_rach_ConfigCommon->groupBconfigured->ra_Msg3SizeGroupA){ + sizeOfRA_PreamblesGroupA = setup->groupBconfigured->numberOfRA_PreamblesGroupA; + switch (setup->groupBconfigured->ra_Msg3SizeGroupA){ /* - Threshold to determine the groups of RA preambles */ case 0: messageSizeGroupA = 56; @@ -297,13 +299,13 @@ void ra_preambles_config(NR_PRACH_RESOURCES_t *prach_resources, NR_UE_MAC_INST_t messageSizeGroupA = 72; break; default: - AssertFatal(1 == 0, "Unknown ra_Msg3SizeGroupA %lu\n", nr_rach_ConfigCommon->groupBconfigured->ra_Msg3SizeGroupA); + AssertFatal(1 == 0, "Unknown ra_Msg3SizeGroupA %lu\n", setup->groupBconfigured->ra_Msg3SizeGroupA); /* todo cases 10 -15*/ } /* Power offset for preamble selection in dB */ messagePowerOffsetGroupB = -9999; - switch (nr_rach_ConfigCommon->groupBconfigured->messagePowerOffsetGroupB){ + switch (setup->groupBconfigured->messagePowerOffsetGroupB){ case 0: messagePowerOffsetGroupB = -9999; break; @@ -329,7 +331,7 @@ void ra_preambles_config(NR_PRACH_RESOURCES_t *prach_resources, NR_UE_MAC_INST_t messagePowerOffsetGroupB = 18; break; default: - AssertFatal(1 == 0,"Unknown messagePowerOffsetGroupB %lu\n", nr_rach_ConfigCommon->groupBconfigured->messagePowerOffsetGroupB); + AssertFatal(1 == 0,"Unknown messagePowerOffsetGroupB %lu\n", setup->groupBconfigured->messagePowerOffsetGroupB); } PLThreshold = prach_resources->RA_PCMAX - rach_ConfigGeneric->preambleReceivedTargetPower - deltaPreamble_Msg3 - messagePowerOffsetGroupB; @@ -420,7 +422,10 @@ void nr_get_prach_resources(module_id_t mod_id, NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); RA_config_t *ra = &mac->ra; - NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + + NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = (mac->scc)? + mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup : + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; LOG_D(PHY, "In %s: getting PRACH resources frame (first_Msg3 %d)\n", __FUNCTION__, ra->first_Msg3); @@ -455,7 +460,9 @@ void nr_Msg1_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint8_t gNB_id){ NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); - NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = (mac->scc) ? + mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup: + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; RA_config_t *ra = &mac->ra; LOG_D(MAC,"In %s: [UE %d] Frame %d, CB-RA: starting contention resolution timer\n", __FUNCTION__, mod_id, frameP); @@ -499,9 +506,9 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, uint8_t *payload; uint16_t size_sdu = 0; unsigned short post_padding; - NR_ServingCellConfigCommon_t *scc = mac->scc; - AssertFatal(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup != NULL, "In %s: FATAL! nr_rach_ConfigCommon is NULL...\n", __FUNCTION__); - NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_RACH_ConfigCommon_t *setup; + if (mac->scc) setup = mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + else setup = mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; AssertFatal(&setup->rach_ConfigGeneric != NULL, "In %s: FATAL! rach_ConfigGeneric is NULL...\n", __FUNCTION__); NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric; NR_RACH_ConfigDedicated_t *rach_ConfigDedicated = ra->rach_ConfigDedicated; @@ -513,19 +520,23 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, // Delay init RA procedure to allow the convergence of the IIR filter on PRACH noise measurements at gNB side if (!prach_resources->init_msg1) { - if (((MAX_FRAME_NUMBER + frame - prach_resources->sync_frame) % MAX_FRAME_NUMBER) > 150){ + if ( (mac->common_configuration_complete>0 || get_softmodem_params()->do_ra==1) && ((MAX_FRAME_NUMBER+frame-prach_resources->sync_frame)%MAX_FRAME_NUMBER)>150 ){ prach_resources->init_msg1 = 1; } else { + LOG_D(NR_MAC,"PRACH Condition not met: frame %d, prach_resources->sync_frame %d\n",frame,prach_resources->sync_frame); return 0; } } + LOG_D(NR_MAC,"frame %d prach_resources->init_msg1 %d, ra->ra_state %d, ra->RA_active %d\n", + frame,prach_resources->init_msg1,ra->ra_state,ra->RA_active); + if (prach_resources->init_msg1 && ra->ra_state != RA_SUCCEEDED) { if (ra->RA_active == 0) { /* RA not active - checking if RRC is ready to initiate the RA procedure */ - LOG_D(MAC, "RA not active. Checking for data to transmit from upper layers...\n"); + LOG_D(NR_MAC, "RA not active. Checking for data to transmit from upper layers...\n"); uint8_t TBS_max = 8 + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); payload = (uint8_t*) mac->CCCH_pdu.payload; @@ -546,7 +557,7 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, sdu_lengths[0] = size_sdu; - LOG_D(MAC,"[UE %d] Frame %d: Requested RRCConnectionRequest, got %d bytes\n", mod_id, frame, size_sdu); + LOG_D(NR_MAC,"[UE %d] Frame %d: Requested RRCConnectionRequest, got %d bytes\n", mod_id, frame, size_sdu); if (size_sdu > 0) { @@ -555,7 +566,7 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, // Random Access Procedure has been successful after reception of Msg4 memcpy(ra->cont_res_id, mac_sdus, sizeof(uint8_t) * 6); - LOG_D(MAC, "[UE %d][%d.%d]: starting initialisation Random Access Procedure...\n", mod_id, frame, nr_slot_tx); + LOG_D(NR_MAC, "[UE %d][%d.%d]: starting initialisation Random Access Procedure...\n", mod_id, frame, nr_slot_tx); ra->Msg3_size = size_sdu + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); @@ -564,9 +575,9 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, nr_get_RA_window(mac); // Fill in preamble and PRACH resources - if (ra->generate_nr_prach == GENERATE_PREAMBLE) + if (ra->generate_nr_prach == GENERATE_PREAMBLE) { nr_get_prach_resources(mod_id, CC_id, gNB_id, prach_resources, prach_pdu, rach_ConfigDedicated); - + } offset = nr_generate_ulsch_pdu((uint8_t *) mac_sdus, // sdus buffer (uint8_t *) payload, // UL MAC pdu pointer num_sdus, // num sdus @@ -632,15 +643,16 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, // Fill in preamble and PRACH resources ra->RA_window_cnt--; - nr_get_prach_resources(mod_id, CC_id, gNB_id, prach_resources, prach_pdu, rach_ConfigDedicated); - + if (ra->generate_nr_prach == GENERATE_PREAMBLE) { + nr_get_prach_resources(mod_id, CC_id, gNB_id, prach_resources, prach_pdu, rach_ConfigDedicated); + } } else if (ra->RA_backoff_cnt > 0) { LOG_D(MAC, "[UE %d][%d.%d]: RAR not received yet (RA backoff count %d) \n", mod_id, frame, nr_slot_tx, ra->RA_backoff_cnt); ra->RA_backoff_cnt--; - if ((ra->RA_backoff_cnt > 0 && ra->generate_nr_prach == GENERATE_PREAMBLE) || ra->RA_backoff_cnt == 0){ + if ((ra->RA_backoff_cnt > 0 && ra->generate_nr_prach == GENERATE_PREAMBLE) || ra->RA_backoff_cnt == 0) { nr_get_prach_resources(mod_id, CC_id, gNB_id, prach_resources, prach_pdu, rach_ConfigDedicated); } @@ -652,6 +664,7 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, nr_ue_contention_resolution(mod_id, CC_id, frame, nr_slot_tx, prach_resources); } + LOG_D(MAC,"ra->generate_nr_prach %d ra->ra_state %d (GENERATE_IDLE %d)\n",ra->generate_nr_prach,ra->ra_state,GENERATE_IDLE); if(ra->generate_nr_prach != GENERATE_IDLE) { return ra->generate_nr_prach; } else { @@ -664,17 +677,21 @@ void nr_get_RA_window(NR_UE_MAC_INST_t *mac){ uint8_t mu, ra_ResponseWindow; RA_config_t *ra = &mac->ra; - NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_RACH_ConfigCommon_t *setup; + if (mac->scc) setup = mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + else setup = mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; + AssertFatal(&setup->rach_ConfigGeneric != NULL, "In %s: FATAL! rach_ConfigGeneric is NULL...\n", __FUNCTION__); NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric; - NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; - + long scs = (mac->scc) ? + mac->scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing : + mac->scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + ra_ResponseWindow = rach_ConfigGeneric->ra_ResponseWindow; if (setup->msg1_SubcarrierSpacing) mu = *setup->msg1_SubcarrierSpacing; else - mu = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + mu = scs; ra->RA_window_cnt = ra->RA_offset*nr_slots_per_frame[mu]; // taking into account the 2 frames gap introduced by OAI gNB diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index cea476354f..037287e875 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -47,7 +47,7 @@ #endif #define LOG_DCI_PARM(a...) LOG_D(PHY,"\t<-NR_PDCCH_DCI_TOOLS_DEBUG (nr_generate_ue_ul_dlsch_params_from_dci)" a) -// #define DEBUG_DCI +//#define DEBUG_DCI void fill_dci_search_candidates(NR_SearchSpace_t *ss,fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15) { @@ -70,11 +70,22 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t uint16_t monitoringSymbolsWithinSlot = 0; uint8_t coreset_id = 1; int sps = 0; - AssertFatal(mac->scc != NULL, "scc is null\n"); + + AssertFatal(mac->scc == NULL || mac->scc_SIB == NULL, "both scc and scc_SIB cannot be non-null\n"); + NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_BWP_DownlinkCommon_t *bwp_Common = mac->DLbwp[bwp_id - 1]->bwp_Common; - NR_BWP_DownlinkCommon_t *initialDownlinkBWP = scc->downlinkConfigCommon->initialDownlinkBWP; + NR_ServingCellConfigCommonSIB_t *scc_SIB = mac->scc_SIB; + NR_BWP_DownlinkCommon_t *bwp_Common=NULL; + NR_BWP_DownlinkCommon_t *initialDownlinkBWP=NULL; + NR_BWP_UplinkCommon_t *initialUplinkBWP=NULL; + + if (scc!=NULL || scc_SIB != NULL) { + initialDownlinkBWP = scc!=NULL ? scc->downlinkConfigCommon->initialDownlinkBWP : &scc_SIB->downlinkConfigCommon.initialDownlinkBWP; + initialUplinkBWP = scc!=NULL ? scc->uplinkConfigCommon->initialUplinkBWP : &scc_SIB->uplinkConfigCommon->initialUplinkBWP; + + bwp_Common = bwp_id>0 ? mac->DLbwp[bwp_id-1]->bwp_Common : NULL; + } NR_SearchSpace_t *ss; NR_ControlResourceSet_t *coreset; @@ -101,8 +112,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t struct NR_ControlResourceSet__cce_REG_MappingType__interleaved *interleaved = coreset->cce_REG_MappingType.choice.interleaved; rel15->coreset.RegBundleSize = (interleaved->reg_BundleSize == NR_ControlResourceSet__cce_REG_MappingType__interleaved__reg_BundleSize_n6) ? 6 : (2 + interleaved->reg_BundleSize); rel15->coreset.InterleaverSize = (interleaved->interleaverSize == NR_ControlResourceSet__cce_REG_MappingType__interleaved__interleaverSize_n6) ? 6 : (2 + interleaved->interleaverSize); - AssertFatal(scc->physCellId != NULL, "mac->scc->physCellId is null\n"); - rel15->coreset.ShiftIndex = interleaved->shiftIndex != NULL ? *interleaved->shiftIndex : *scc->physCellId; + rel15->coreset.ShiftIndex = interleaved->shiftIndex != NULL ? *interleaved->shiftIndex : mac->physCellId; } else { rel15->coreset.RegBundleSize = 0; rel15->coreset.InterleaverSize = 0; @@ -116,7 +126,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->coreset.pdcch_dmrs_scrambling_id = *coreset->pdcch_DMRS_ScramblingID; rel15->coreset.scrambling_rnti = mac->crnti; } else { - rel15->coreset.pdcch_dmrs_scrambling_id = *scc->physCellId; + rel15->coreset.pdcch_dmrs_scrambling_id = mac->physCellId; rel15->coreset.scrambling_rnti = 0; } @@ -128,15 +138,23 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t switch(rnti_type) { case NR_RNTI_C: // we use DL BWP dedicated - sps = bwp_Common->genericParameters.cyclicPrefix == NULL ? 14 : 12; + sps = bwp_Common ? + (bwp_Common->genericParameters.cyclicPrefix ? 12 : 14) : + initialDownlinkBWP->genericParameters.cyclicPrefix ? 12 : 14; // for SPS=14 8 MSBs in positions 13 down to 6 monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps)); rel15->rnti = mac->crnti; - rel15->BWPSize = NRRIV2BW(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - rel15->BWPStart = NRRIV2PRBOFFSET(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - rel15->SubcarrierSpacing = bwp_Common->genericParameters.subcarrierSpacing; + if (!bwp_Common) { + rel15->BWPSize = NRRIV2BW(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + rel15->BWPStart = NRRIV2PRBOFFSET(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } + else { + rel15->BWPSize = NRRIV2BW(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + rel15->BWPStart = NRRIV2PRBOFFSET(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + rel15->SubcarrierSpacing = bwp_Common->genericParameters.subcarrierSpacing; + } for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(scc, mac->scg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, bwp_id); + rel15->dci_length_options[i] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, bwp_id); } break; case NR_RNTI_RA: @@ -151,7 +169,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPStart = NRRIV2PRBOFFSET(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; - rel15->dci_length_options[0] = nr_dci_size(scc, mac->scg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, bwp_id); + rel15->dci_length_options[0] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, bwp_id); break; case NR_RNTI_P: break; @@ -165,13 +183,14 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPSize = NRRIV2BW(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); rel15->BWPStart = NRRIV2PRBOFFSET(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; - rel15->dci_length_options[0] = nr_dci_size(scc, mac->scg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_TC, rel15->BWPSize, bwp_id); + rel15->dci_length_options[0] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_TC, rel15->BWPSize, bwp_id); break; case NR_RNTI_SP_CSI: break; case NR_RNTI_SI: // we use DL BWP dedicated - sps = bwp_Common->genericParameters.cyclicPrefix == NULL ? 14 : 12; + if (bwp_Common) sps = bwp_Common->genericParameters.cyclicPrefix == NULL ? 14 : 12; + else sps=14; // note: normally this would be found with SSS detection // for SPS=14 8 MSBs in positions 13 down to 6 monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps)); @@ -182,7 +201,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon; for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(scc, mac->scg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_SI, rel15->BWPSize, 0); + rel15->dci_length_options[i] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_SI, rel15->BWPSize, 0); } break; case NR_RNTI_SFI: @@ -224,168 +243,175 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - uint8_t bwp_id = mac->DL_BWP_Id; - uint8_t coreset_id = 1; + + uint8_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0, coreset_id = (mac->cg) ? 1 : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; - NR_BWP_Downlink_t *bwp = mac->DLbwp[bwp_id - 1]; + NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id - 1] : NULL; - #ifdef DEBUG_DCI - LOG_D(MAC, "[DCI_CONFIG] ra_rnti %p (%x) crnti %p (%x) t_crnti %p (%x)\n", &ra->ra_rnti, ra->ra_rnti, &mac->crnti, mac->crnti, &ra->t_crnti, ra->t_crnti); - #endif + LOG_D(MAC, "[DCI_CONFIG] ra_rnti %p (%x) crnti %p (%x) t_crnti %p (%x)\n", &ra->ra_rnti, ra->ra_rnti, &mac->crnti, mac->crnti, &ra->t_crnti, ra->t_crnti); - // loop over all available SS for BWP ID 1, CORESET ID 1 - for (ss_id = 0; ss_id < FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id - 1][coreset_id - 1][ss_id] != NULL; ss_id++){ - NR_SearchSpace_t *ss = mac->SSpace[bwp_id - 1][coreset_id - 1][ss_id]; - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; - NR_BWP_DownlinkCommon_t *bwp_Common = bwp->bwp_Common; - NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; - struct NR_PhysicalCellGroupConfig *phy_cgc = mac->scg->physicalCellGroupConfig; - switch (ss->searchSpaceType->present){ - case NR_SearchSpace__searchSpaceType_PR_common: - // this is for CSSs, we use BWP common and pdcch_ConfigCommon + if (mac->cg) { // do this only after we have a Master or Secondary Cell group + // loop over all available SS for BWP ID 1, CORESET ID 1 + if (bwp) { + for (ss_id = 0; ss_id < FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id - 1][coreset_id - 1][ss_id] != NULL; ss_id++){ + LOG_D(MAC, "[DCI_CONFIG] ss_id %d\n",ss_id); + NR_SearchSpace_t *ss = mac->SSpace[bwp_id - 1][coreset_id - 1][ss_id]; + fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; + NR_BWP_DownlinkCommon_t *bwp_Common = bwp->bwp_Common; + NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; + struct NR_PhysicalCellGroupConfig *phy_cgc = mac->cg->physicalCellGroupConfig; + switch (ss->searchSpaceType->present){ + case NR_SearchSpace__searchSpaceType_PR_common: + // this is for CSSs, we use BWP common and pdcch_ConfigCommon - // Fetch configuration for searchSpaceZero - // note: The search space with the SearchSpaceId = 0 identifies the search space configured via PBCH (MIB) and in ServingCellConfigCommon (searchSpaceZero). - if (pdcch_ConfigCommon->choice.setup->searchSpaceZero){ - if (pdcch_ConfigCommon->choice.setup->searchSpaceSIB1 == NULL){ - pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=calloc(1,sizeof(*pdcch_ConfigCommon->choice.setup->searchSpaceSIB1)); - } - *pdcch_ConfigCommon->choice.setup->searchSpaceSIB1 = 0; - LOG_D(MAC, "[DCI_CONFIG] Configure SearchSpace#0 of the initial BWP\n"); - } - if (ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0){ - // check available SS IDs - if (pdcch_ConfigCommon->choice.setup->ra_SearchSpace){ - if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->ra_SearchSpace){ - switch(ra->ra_state){ - case WAIT_RAR: - LOG_D(NR_MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type1-PDCCH common random access search space (RA-Msg2)\n"); - rel15->num_dci_options = 1; - rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; - if (get_softmodem_params()->sa) { - config_dci_pdu(mac, rel15, dl_config, NR_RNTI_RA, -1); - } else { - config_dci_pdu(mac, rel15, dl_config, NR_RNTI_RA, ss_id); - } - fill_dci_search_candidates(ss, rel15); - break; - case WAIT_CONTENTION_RESOLUTION: - LOG_D(NR_MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type1-PDCCH common random access search space (RA-Msg4)\n"); - rel15->num_dci_options = 1; - rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; - config_dci_pdu(mac, rel15, dl_config, NR_RNTI_TC, -1); - fill_dci_search_candidates(ss, rel15); - break; - default: - break; - } - } - } - if (pdcch_ConfigCommon->choice.setup->searchSpaceSIB1){ - if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->searchSpaceSIB1){ - // Configure monitoring of PDCCH candidates in Type0-PDCCH common search space on the MCG - LOG_W(MAC, "[DCI_CONFIG] This seach space should not be configured yet..."); - } - } - if (pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation){ - if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation){ - // Configure monitoring of PDCCH candidates in Type0-PDCCH common search space on the MCG - LOG_W(MAC, "[DCI_CONFIG] This seach space should not be configured yet..."); - } - } - if (pdcch_ConfigCommon->choice.setup->pagingSearchSpace){ - if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->pagingSearchSpace){ - // Configure monitoring of PDCCH candidates in Type2-PDCCH common search space on the MCG - LOG_W(MAC, "[DCI_CONFIG] This seach space should not be configured yet..."); - } - } - if (phy_cgc){ - if (phy_cgc->cs_RNTI){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by CS-RNTI...\n"); - LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); - } - if (phy_cgc->ext1){ - if (phy_cgc->ext1->mcs_C_RNTI){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by MCS-C-RNTI...\n"); - LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); - } - } - } - } // end DCI 00 and 01 - // DCI 2_0 - if (ss->searchSpaceType->choice.common->dci_Format2_0){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_0 with CRC scrambled by SFI-RNTI \n"); - LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); - } - // DCI 2_1 - if (ss->searchSpaceType->choice.common->dci_Format2_1){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_1 with CRC scrambled by INT-RNTI \n"); - LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); - } - // DCI 2_2 - if (ss->searchSpaceType->choice.common->dci_Format2_2){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_2 with CRC scrambled by TPC-RNTI \n"); - LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); - } - // DCI 2_3 - if (ss->searchSpaceType->choice.common->dci_Format2_3){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_3 with CRC scrambled by TPC-SRS-RNTI \n"); - LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); - } + // Fetch configuration for searchSpaceZero + // note: The search space with the SearchSpaceId = 0 identifies the search space configured via PBCH (MIB) and in ServingCellConfigCommon (searchSpaceZero). + if (pdcch_ConfigCommon->choice.setup->searchSpaceZero){ + if (pdcch_ConfigCommon->choice.setup->searchSpaceSIB1 == NULL){ + pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=calloc(1,sizeof(*pdcch_ConfigCommon->choice.setup->searchSpaceSIB1)); + } + *pdcch_ConfigCommon->choice.setup->searchSpaceSIB1 = 0; + LOG_D(MAC, "[DCI_CONFIG] Configure SearchSpace#0 of the initial BWP\n"); + } + if (ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0){ + // check available SS IDs + if (pdcch_ConfigCommon->choice.setup->ra_SearchSpace){ + if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->ra_SearchSpace){ + switch(ra->ra_state){ + case WAIT_RAR: + LOG_D(NR_MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type1-PDCCH common random access search space (RA-Msg2)\n"); + rel15->num_dci_options = 1; + rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; + if (get_softmodem_params()->sa) { + config_dci_pdu(mac, rel15, dl_config, NR_RNTI_RA, -1); + } else { + config_dci_pdu(mac, rel15, dl_config, NR_RNTI_RA, ss_id); + } + fill_dci_search_candidates(ss, rel15); + break; + case WAIT_CONTENTION_RESOLUTION: + LOG_D(NR_MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type1-PDCCH common random access search space (RA-Msg4)\n"); + rel15->num_dci_options = 1; + rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; + config_dci_pdu(mac, rel15, dl_config, NR_RNTI_TC, -1); + fill_dci_search_candidates(ss, rel15); + break; + default: + break; + } + } + } + if (pdcch_ConfigCommon->choice.setup->searchSpaceSIB1){ + if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->searchSpaceSIB1){ + // Configure monitoring of PDCCH candidates in Type0-PDCCH common search space on the MCG + LOG_W(MAC, "[DCI_CONFIG] This seach space should not be configured yet..."); + } + } + if (pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation){ + if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation){ + // Configure monitoring of PDCCH candidates in Type0-PDCCH common search space on the MCG + LOG_W(MAC, "[DCI_CONFIG] This seach space should not be configured yet..."); + } + } + if (pdcch_ConfigCommon->choice.setup->pagingSearchSpace){ + if (ss->searchSpaceId == *pdcch_ConfigCommon->choice.setup->pagingSearchSpace){ + // Configure monitoring of PDCCH candidates in Type2-PDCCH common search space on the MCG + LOG_W(MAC, "[DCI_CONFIG] This seach space should not be configured yet..."); + } + } + if (phy_cgc){ + if (phy_cgc->cs_RNTI){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by CS-RNTI...\n"); + LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); + } + if (phy_cgc->ext1){ + if (phy_cgc->ext1->mcs_C_RNTI){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by MCS-C-RNTI...\n"); + LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); + } + } + } + } // end DCI 00 and 01 + // DCI 2_0 + if (ss->searchSpaceType->choice.common->dci_Format2_0){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_0 with CRC scrambled by SFI-RNTI \n"); + LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); + } + // DCI 2_1 + if (ss->searchSpaceType->choice.common->dci_Format2_1){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_1 with CRC scrambled by INT-RNTI \n"); + LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); + } + // DCI 2_2 + if (ss->searchSpaceType->choice.common->dci_Format2_2){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_2 with CRC scrambled by TPC-RNTI \n"); + LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); + } + // DCI 2_3 + if (ss->searchSpaceType->choice.common->dci_Format2_3){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in Type3-PDCCH common search space for DCI format 2_3 with CRC scrambled by TPC-SRS-RNTI \n"); + LOG_W(MAC, "[DCI_CONFIG] This format should not be configured yet..."); + } - break; - case NR_SearchSpace__searchSpaceType_PR_ue_Specific: - // this is an USS - if (ss->searchSpaceType->choice.ue_Specific){ - if(ss->searchSpaceType->choice.ue_Specific->dci_Formats == NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_1_And_1_1){ - // Monitors DCI 01 and 11 scrambled with C-RNTI, or CS-RNTI(s), or SP-CSI-RNTI - if ((ra->ra_state == RA_SUCCEEDED || get_softmodem_params()->phy_test) && mac->crnti > 0) { - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in the user specific search space\n"); - rel15->num_dci_options = 2; - rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_1; - rel15->dci_format_options[1] = NR_UL_DCI_FORMAT_0_1; - config_dci_pdu(mac, rel15, dl_config, NR_RNTI_C, ss_id); - fill_dci_search_candidates(ss, rel15); + break; + case NR_SearchSpace__searchSpaceType_PR_ue_Specific: + // this is an USS + if (ss->searchSpaceType->choice.ue_Specific){ + if(ss->searchSpaceType->choice.ue_Specific->dci_Formats == NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_1_And_1_1){ + // Monitors DCI 01 and 11 scrambled with C-RNTI, or CS-RNTI(s), or SP-CSI-RNTI + if ((ra->ra_state == RA_SUCCEEDED || get_softmodem_params()->phy_test) && mac->crnti > 0) { + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in the user specific search space\n"); + rel15->num_dci_options = 2; + rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_1; + rel15->dci_format_options[1] = NR_UL_DCI_FORMAT_0_1; + config_dci_pdu(mac, rel15, dl_config, NR_RNTI_C, ss_id); + fill_dci_search_candidates(ss, rel15); - #ifdef DEBUG_DCI - LOG_D(MAC, "[DCI_CONFIG] ss %d ue_Specific %p searchSpaceType->present %d dci_Formats %d\n", - ss_id, - ss->searchSpaceType->choice.ue_Specific, - ss->searchSpaceType->present, - ss->searchSpaceType->choice.ue_Specific->dci_Formats); - #endif - } - if (phy_cgc){ - if (phy_cgc->cs_RNTI){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by CS-RNTI...\n"); - LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); - } - if (phy_cgc->sp_CSI_RNTI){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by SP-CSI-RNTI...\n"); - LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); - } - if (phy_cgc->ext1){ - if (phy_cgc->ext1->mcs_C_RNTI){ - LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by MCS-C-RNTI...\n"); - LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); - } - } - } - } +#ifdef DEBUG_DCI + LOG_D(MAC, "[DCI_CONFIG] ss %d ue_Specific %p searchSpaceType->present %d dci_Formats %d\n", + ss_id, + ss->searchSpaceType->choice.ue_Specific, + ss->searchSpaceType->present, + ss->searchSpaceType->choice.ue_Specific->dci_Formats); +#endif + } + if (phy_cgc){ + if (phy_cgc->cs_RNTI){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by CS-RNTI...\n"); + LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); + } + if (phy_cgc->sp_CSI_RNTI){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by SP-CSI-RNTI...\n"); + LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); + } + if (phy_cgc->ext1){ + if (phy_cgc->ext1->mcs_C_RNTI){ + LOG_D(MAC, "[DCI_CONFIG] Configure monitoring of PDCCH candidates in user specific search space for dci_Format0_0_AndFormat1_0 with CRC scrambled by MCS-C-RNTI...\n"); + LOG_W(MAC, "[DCI_CONFIG] This RNTI should not be configured yet..."); + } + } + } + } + } + break; + default: + AssertFatal(1 == 0, "[DCI_CONFIG] Unrecognized search space type..."); + break; + } } - break; - default: - AssertFatal(1 == 0, "[DCI_CONFIG] Unrecognized search space type..."); - break; } } + else { + AssertFatal(1==0,"Handle DCI searching when CellGroup without dedicated BWP\n"); + } // Search space 0, CORESET ID 0 - NR_BWP_DownlinkCommon_t *bwp_Common = bwp->bwp_Common; - NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; + NR_BWP_DownlinkCommon_t *bwp_Common = bwp ? bwp->bwp_Common : NULL; + NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp?bwp_Common->pdcch_ConfigCommon:NULL; - if (pdcch_ConfigCommon->choice.setup->searchSpaceSIB1){ + if (pdcch_ConfigCommon && + pdcch_ConfigCommon->choice.setup->searchSpaceSIB1){ NR_SearchSpace_t *ss0 = mac->search_space_zero; fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; @@ -399,5 +425,13 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl } } } - + else { // use coreset0/ss0 + NR_SearchSpace_t *ss0 = mac->search_space_zero; + fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[0].dci_config_pdu.dci_config_rel15; + rel15->num_dci_options = 1; + rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; + config_dci_pdu(mac, rel15, dl_config, NR_RNTI_C , -1); + fill_dci_search_candidates(ss0, rel15); + dl_config->number_pdus = 1; + } } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index bdf7daa9ee..3497e8aef3 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -62,10 +62,13 @@ #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" +//#define DEBUG_MIB //#define ENABLE_MAC_PAYLOAD_DEBUG 1 //#define DEBUG_EXTRACT_DCI //#define DEBUG_RAR +extern uint32_t N_RB_DL; + int get_rnti_type(NR_UE_MAC_INST_t *mac, uint16_t rnti){ RA_config_t *ra = &mac->ra; @@ -99,47 +102,54 @@ int8_t nr_ue_decode_mib(module_id_t module_id, uint32_t ssb_length, uint32_t ssb_index, void *pduP, + uint16_t ssb_start_subcarrier, uint16_t cell_id) { LOG_D(MAC,"[L2][MAC] decode mib\n"); NR_UE_MAC_INST_t *mac = get_mac_inst(module_id); - NR_ServingCellConfigCommon_t *scc = mac->scc; - frequency_range_t frequency_range; + mac->physCellId = cell_id; + nr_mac_rrc_data_ind_ue( module_id, cc_id, gNB_index, 0, 0, 0, NR_BCCH_BCH, (uint8_t *) pduP, 3 ); // fixed 3 bytes MIB PDU AssertFatal(mac->mib != NULL, "nr_ue_decode_mib() mac->mib == NULL\n"); //if(mac->mib != NULL){ uint16_t frame = (mac->mib->systemFrameNumber.buf[0] >> mac->mib->systemFrameNumber.bits_unused); uint16_t frame_number_4lsb = 0; + for (int i=0; i<4; i++) frame_number_4lsb |= ((extra_bits>>i)&1)<<(3-i); - uint8_t half_frame_bit = ( extra_bits >> 4 ) & 0x1; // extra bits[4] + uint8_t ssb_subcarrier_offset_msb = ( extra_bits >> 5 ) & 0x1; // extra bits[5] uint8_t ssb_subcarrier_offset = (uint8_t)mac->mib->ssb_SubcarrierOffset; frame = frame << 4; frame = frame | frame_number_4lsb; if(ssb_length == 64){ - frequency_range = FR2; + mac->frequency_range = FR2; for (int i=0; i<3; i++) ssb_index += (((extra_bits>>(7-i))&0x01)<<(3+i)); }else{ - frequency_range = FR1; + mac->frequency_range = FR1; if(ssb_subcarrier_offset_msb){ ssb_subcarrier_offset = ssb_subcarrier_offset | 0x10; } } - LOG_D(MAC,"system frame number(6 MSB bits): %d\n", mac->mib->systemFrameNumber.buf[0]); - LOG_D(MAC,"system frame number(with LSB): %d\n", (int)frame); - LOG_D(MAC,"subcarrier spacing (0=15or60, 1=30or120): %d\n", (int)mac->mib->subCarrierSpacingCommon); - LOG_D(MAC,"ssb carrier offset(with MSB): %d\n", (int)ssb_subcarrier_offset); - LOG_D(MAC,"dmrs type A position (0=pos2,1=pos3): %d\n", (int)mac->mib->dmrs_TypeA_Position); - LOG_D(MAC,"cell barred (0=barred,1=notBarred): %d\n", (int)mac->mib->cellBarred); - LOG_D(MAC,"intra frequency reselection (0=allowed,1=notAllowed): %d\n", (int)mac->mib->intraFreqReselection); - LOG_D(MAC,"half frame bit(extra bits): %d\n", (int)half_frame_bit); - LOG_D(MAC,"ssb index(extra bits): %d\n", (int)ssb_index); +#ifdef DEBUG_MIB + uint8_t half_frame_bit = ( extra_bits >> 4 ) & 0x1; // extra bits[4] + LOG_I(MAC,"system frame number(6 MSB bits): %d\n", mac->mib->systemFrameNumber.buf[0]); + LOG_I(MAC,"system frame number(with LSB): %d\n", (int)frame); + LOG_I(MAC,"subcarrier spacing (0=15or60, 1=30or120): %d\n", (int)mac->mib->subCarrierSpacingCommon); + LOG_I(MAC,"ssb carrier offset(with MSB): %d\n", (int)ssb_subcarrier_offset); + LOG_I(MAC,"dmrs type A position (0=pos2,1=pos3): %d\n", (int)mac->mib->dmrs_TypeA_Position); + LOG_I(MAC,"controlResourceSetZero: %d\n", (int)mac->mib->pdcch_ConfigSIB1.controlResourceSetZero); + LOG_I(MAC,"searchSpaceZero: %d\n", (int)mac->mib->pdcch_ConfigSIB1.searchSpaceZero); + LOG_I(MAC,"cell barred (0=barred,1=notBarred): %d\n", (int)mac->mib->cellBarred); + LOG_I(MAC,"intra frequency reselection (0=allowed,1=notAllowed): %d\n", (int)mac->mib->intraFreqReselection); + LOG_I(MAC,"half frame bit(extra bits): %d\n", (int)half_frame_bit); + LOG_I(MAC,"ssb index(extra bits): %d\n", (int)ssb_index); +#endif //storing ssb index in the mac structure mac->mib_ssb = ssb_index; @@ -147,16 +157,10 @@ int8_t nr_ue_decode_mib(module_id_t module_id, if (get_softmodem_params()->sa == 1) { // TODO these values shouldn't be taken from SCC in SA - uint8_t scs_ssb = *scc->ssbSubcarrierSpacing; - uint32_t band = *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]; - int scs_scaling = 1<downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA < 600000) - scs_scaling = scs_scaling*3; - if (scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA > 2016666) - scs_scaling = scs_scaling>>2; - uint32_t absolute_diff = (*scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB - scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA); - + uint8_t scs_ssb = get_softmodem_params()->numerology; + uint32_t band = get_softmodem_params()->band; uint16_t ssb_start_symbol = get_ssb_start_symbol(band,scs_ssb,ssb_index); + uint16_t ssb_offset_point_a = (ssb_start_subcarrier - ssb_subcarrier_offset)/12; get_type0_PDCCH_CSS_config_parameters(&mac->type0_PDCCH_CSS_config, frame, @@ -165,9 +169,9 @@ int8_t nr_ue_decode_mib(module_id_t module_id, ssb_subcarrier_offset, ssb_start_symbol, scs_ssb, - frequency_range, + mac->frequency_range, ssb_index, - absolute_diff/(12*scs_scaling)-10); + ssb_offset_point_a); mac->type0_pdcch_ss_mux_pattern = mac->type0_PDCCH_CSS_config.type0_pdcch_ss_mux_pattern; @@ -230,10 +234,10 @@ int8_t nr_ue_process_dci_freq_dom_resource_assignment(nfapi_nr_ue_pusch_pdu_t *p return -1; } - LOG_D(MAC,"riv = %i\n", riv); - LOG_D(MAC,"n_RB_DLBWP = %i\n", n_RB_DLBWP); - LOG_D(MAC,"number_rbs = %i\n", dlsch_config_pdu->number_rbs); - LOG_D(MAC,"start_rb = %i\n", dlsch_config_pdu->start_rb); + LOG_D(MAC,"DLSCH riv = %i\n", riv); + LOG_D(MAC,"DLSCH n_RB_DLBWP = %i\n", n_RB_DLBWP); + LOG_D(MAC,"DLSCH number_rbs = %i\n", dlsch_config_pdu->number_rbs); + LOG_D(MAC,"DLSCH start_rb = %i\n", dlsch_config_pdu->start_rb); } if(pusch_config_pdu != NULL){ @@ -256,7 +260,10 @@ int8_t nr_ue_process_dci_freq_dom_resource_assignment(nfapi_nr_ue_pusch_pdu_t *p LOG_W(MAC, "Frequency domain assignment values are invalid! #RBs: %d, Start RB: %d, n_RB_ULBWP: %d \n",pusch_config_pdu->rb_size, pusch_config_pdu->rb_start, n_RB_ULBWP); return -1; } - + LOG_D(MAC,"ULSCH riv = %i\n", riv); + LOG_D(MAC,"ULSCH n_RB_DLBWP = %i\n", n_RB_ULBWP); + LOG_D(MAC,"ULSCH number_rbs = %i\n", pusch_config_pdu->rb_size); + LOG_D(MAC,"ULSCH start_rb = %i\n", pusch_config_pdu->rb_start); } return 0; } @@ -265,9 +272,10 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu, uint8_t time_domain_ind, - bool use_default){ + bool use_default + ){ + int dmrs_typeA_pos = (mac->scc != NULL) ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position; - int dmrs_typeA_pos = mac->scc->dmrs_TypeA_Position; // uint8_t k_offset=0; uint8_t sliv_S=0; uint8_t sliv_L=0; @@ -319,10 +327,16 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(dlsch_config_pdu != NULL){ NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) + pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (pdsch_TimeDomainAllocationList && use_default==false) { if (time_domain_ind >= pdsch_TimeDomainAllocationList->list.count) { @@ -372,13 +386,22 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(pusch_config_pdu != NULL){ NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { + if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + else if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Common && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } - + else pusch_TimeDomainAllocationList = mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + if (pusch_TimeDomainAllocationList && use_default==false) { if (time_domain_ind >= pusch_TimeDomainAllocationList->list.count) { LOG_E(MAC, "time_domain_ind %d >= pusch->TimeDomainAllocationList->list.count %d\n", @@ -388,6 +411,7 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, return -1; } + LOG_D(NR_MAC,"Filling Time-Domain Allocation from pusch_TimeDomainAllocationList\n"); int startSymbolAndLength = pusch_TimeDomainAllocationList->list.array[time_domain_ind]->startSymbolAndLength; int S,L; SLIV2SL(startSymbolAndLength,&S,&L); @@ -395,6 +419,7 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, pusch_config_pdu->nr_of_symbols=L; } else { + LOG_D(NR_MAC,"Filling Time-Domain Allocation from tables\n"); // k_offset = table_6_1_2_1_1_2_time_dom_res_alloc_A[time_domain_ind-1][0]; sliv_S = table_6_1_2_1_1_2_time_dom_res_alloc_A[time_domain_ind][1]; sliv_L = table_6_1_2_1_1_2_time_dom_res_alloc_A[time_domain_ind][2]; @@ -404,6 +429,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, pusch_config_pdu->nr_of_symbols = sliv_L; pusch_config_pdu->start_symbol_index = sliv_S; } + LOG_D(NR_MAC,"start_symbol = %i\n", pusch_config_pdu->start_symbol_index); + LOG_D(NR_MAC,"number_symbols = %i\n", pusch_config_pdu->nr_of_symbols); } return 0; } @@ -411,13 +438,14 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, int nr_ue_process_dci_indication_pdu(module_id_t module_id,int cc_id, int gNB_index, frame_t frame, int slot, fapi_nr_dci_indication_pdu_t *dci) { NR_UE_MAC_INST_t *mac = get_mac_inst(module_id); + dci_pdu_rel15_t *def_dci_pdu_rel15 = &mac->def_dci_pdu_rel15[dci->dci_format]; LOG_D(MAC,"Received dci indication (rnti %x,dci format %d,n_CCE %d,payloadSize %d,payload %llx)\n", dci->rnti,dci->dci_format,dci->n_CCE,dci->payloadSize,*(unsigned long long*)dci->payloadBits); - - if (nr_extract_dci_info(mac, dci->dci_format, dci->payloadSize, dci->rnti, (uint64_t *)dci->payloadBits, &mac->def_dci_pdu_rel15[dci->dci_format])) - return -1; - return (nr_ue_process_dci(module_id, cc_id, gNB_index, frame, slot, &mac->def_dci_pdu_rel15[dci->dci_format], dci->rnti, dci->dci_format)); + int8_t ret = nr_extract_dci_info(mac, dci->dci_format, dci->payloadSize, dci->rnti, (uint64_t *)dci->payloadBits, def_dci_pdu_rel15); + if ((ret&1) == 1) return -1; + else if (ret == 2) dci->dci_format = NR_UL_DCI_FORMAT_0_0; + return (nr_ue_process_dci(module_id, cc_id, gNB_index, frame, slot, def_dci_pdu_rel15, dci->rnti, dci->dci_format)); } int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, frame_t frame, int slot, dci_pdu_rel15_t *dci, uint16_t rnti, uint8_t dci_format){ @@ -434,11 +462,10 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; - //const uint16_t n_RB_DLBWP = dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP; //make sure this has been set - AssertFatal(mac->DLbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); - AssertFatal(mac->ULbwp[0]!=NULL,"DLbwp[0] should not be zero here!\n"); - - const uint16_t n_RB_DLBWP = (ra->ra_state == WAIT_RAR || ra->ra_state == WAIT_CONTENTION_RESOLUTION) ? NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + uint16_t n_RB_DLBWP; + if (mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + else if (mac->scc_SIB) n_RB_DLBWP = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + else n_RB_DLBWP = mac->type0_PDCCH_CSS_config.num_rbs; LOG_D(MAC, "In %s: Processing received DCI format %s (DL BWP %d)\n", __FUNCTION__, dci_formats[dci_format], n_RB_DLBWP); @@ -477,6 +504,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr return -1; } + AssertFatal(ul_config->number_pdusnumber_pdus %d out of bounds\n",ul_config->number_pdus); nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu = &ul_config->ul_config_list[ul_config->number_pdus].pusch_config_pdu; fill_ul_config(ul_config, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH); @@ -601,14 +629,15 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti = rnti; fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_0 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - NR_PDSCH_Config_t *pdsch_config = NULL; - uint16_t BWPSize = 0; + NR_PDSCH_Config_t *pdsch_config= (mac->DLbwp[dl_bwp_id -1]) ? mac->DLbwp[dl_bwp_id -1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + uint16_t BWPSize = n_RB_DLBWP; if(rnti == SI_RNTI) { dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH; dlsch_config_pdu_1_0->BWPSize = mac->type0_PDCCH_CSS_config.num_rbs; dlsch_config_pdu_1_0->BWPStart = mac->type0_PDCCH_CSS_config.cset_start_rb; dlsch_config_pdu_1_0->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon; + if (pdsch_config) pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NULL; // For PDSCH with mapping type A, the UE shall assume dmrs-AdditionalPosition='pos2' BWPSize = dlsch_config_pdu_1_0->BWPSize; } else { if (ra->RA_window_cnt >= 0 && rnti == ra->ra_rnti){ @@ -617,20 +646,30 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH; } if( (ra->RA_window_cnt >= 0 && rnti == ra->ra_rnti) || (rnti == ra->t_crnti) ) { - dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - if (get_softmodem_params()->sa) { + if (mac->scc == NULL) { + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } + else { + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - } else { // NSA mode is not using the Initial BWP + } + if (!get_softmodem_params()->sa) { // NSA mode is not using the Initial BWP dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; + BWPSize = dlsch_config_pdu_1_0->BWPSize; } - } else { + } else if (mac->DLbwp[dl_bwp_id-1]) { dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; + } else if (mac->scc_SIB) { + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->SubcarrierSpacing = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.subcarrierSpacing; + pdsch_config = NULL; } - BWPSize = n_RB_DLBWP; } /* IDENTIFIER_DCI_FORMATS */ @@ -646,24 +685,25 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) + pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; - int mappingtype = pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; + int mappingtype = pdsch_TimeDomainAllocationList ? pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType : ((dlsch_config_pdu_1_0->start_symbol <= 3)? typeA: typeB); /* dmrs symbol positions*/ - dlsch_config_pdu_1_0->dlDmrsSymbPos = fill_dmrs_mask(NULL, - mac->scc->dmrs_TypeA_Position, - dlsch_config_pdu_1_0->number_symbols, - dlsch_config_pdu_1_0->start_symbol, - mappingtype); - - // For DCI 1_0, dmrs type is type1. referred to section 5.1.6.2 of 3GPP TS 38.214 - dlsch_config_pdu_1_0->dmrsConfigType = 0; - LOG_D(MAC, "DCI 1_0 start_sym:%d NR Symb:%d, DMRS_symb_pos:%x\n", dlsch_config_pdu_1_0->start_symbol, dlsch_config_pdu_1_0->number_symbols, dlsch_config_pdu_1_0->dlDmrsSymbPos); - + dlsch_config_pdu_1_0->dlDmrsSymbPos = fill_dmrs_mask(pdsch_config, + mac->mib->dmrs_TypeA_Position, + dlsch_config_pdu_1_0->number_symbols, + dlsch_config_pdu_1_0->start_symbol, + mappingtype); + dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id-1] != NULL) ? (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; /* number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214 version 15.9.0 Release 15 */ if (dlsch_config_pdu_1_0->number_symbols == 2) dlsch_config_pdu_1_0->n_dmrs_cdm_groups = 1; @@ -712,20 +752,42 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; - for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { - valid = 1; - break; + if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup&& + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList) { + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + for (int id = 0; id < pucch_res_set_cnt; id++) { + if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + valid = 1; + break; + } } } + else if (mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList){ + pucch_res_set_cnt = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + for (int id = 0; id < pucch_res_set_cnt; id++) { + if (dlsch_config_pdu_1_0->pucch_resource_id < mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + valid = 1; + break; + } + } + } else valid=1; if (!valid) { LOG_W(MAC, "[%d.%d] pucch_resource_indicator value %d is out of bounds. Possibly due to false DCI. Ignoring DCI!\n", frame, slot, dlsch_config_pdu_1_0->pucch_resource_id); return -1; } /* PDSCH_TO_HARQ_FEEDBACK_TIME_IND (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI)*/ - dlsch_config_pdu_1_0->pdsch_to_harq_feedback_time_ind = mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Dedicated->pucch_Config->choice.setup->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; + dlsch_config_pdu_1_0->pdsch_to_harq_feedback_time_ind = 1+dci->pdsch_to_harq_feedback_timing_indicator.val; LOG_D(MAC,"(nr_ue_procedures.c) rnti = %x dl_config->number_pdus = %d\n", dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti, @@ -820,12 +882,17 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) + pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; - int mappingtype = pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; + int mappingtype = pdsch_TimeDomainAllocationList ? pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType : ((dlsch_config_pdu_1_1->start_symbol <= 3)? typeA: typeB); /* dmrs symbol positions*/ dlsch_config_pdu_1_1->dlDmrsSymbPos = fill_dmrs_mask(pdsch_config, @@ -1135,24 +1202,34 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, uint64_t *dci_pdu, dci_pdu_rel15_t *dci_pdu_rel15) { + int N_RB = 0; + int pos = 0; + int fsize = 0; + int rnti_type = get_rnti_type(mac, rnti); - - AssertFatal(mac->DLbwp[0] != NULL, "DLbwp[0] shouldn't be null here!\n"); - AssertFatal(mac->ULbwp[0] != NULL, "ULbwp[0] shouldn't be null here!\n"); - int N_RB = get_n_rb(mac, rnti_type); NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; - int N_RB_UL = (mac->scg != NULL) ? - NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) : - NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - int pos=0; - int fsize=0; + int N_RB_UL = 0; + if(mac->scc_SIB) { + N_RB_UL = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else if(mac->ULbwp[ul_bwp_id-1]) { + N_RB_UL = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else if(mac->scc) { + N_RB_UL = NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } + LOG_D(MAC,"nr_extract_dci_info : dci_pdu %lx, size %d\n",*dci_pdu,dci_size); switch(dci_format) { case NR_DL_DCI_FORMAT_1_0: switch(rnti_type) { case NR_RNTI_RA: + if(mac->scc_SIB) { + N_RB = NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else { + N_RB = get_n_rb(mac, rnti_type); + } // Freq domain assignment fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) ); pos=fsize; @@ -1192,11 +1269,19 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, //Identifier for DCI formats pos++; dci_pdu_rel15->format_indicator = (*dci_pdu>>(dci_size-pos))&1; + + //switch to DCI_0_0 if (dci_pdu_rel15->format_indicator == 0) - return 1; // discard dci, format indicator not corresponding to dci_format + return 2+nr_extract_dci_info(mac, NR_UL_DCI_FORMAT_0_0, dci_size, rnti, dci_pdu, dci_pdu_rel15); + #ifdef DEBUG_EXTRACT_DCI LOG_D(MAC,"Format indicator %d (%d bits) N_RB_BWP %d => %d (0x%lx)\n",dci_pdu_rel15->format_indicator,1,N_RB,dci_size-pos,*dci_pdu); #endif + + // check BWP id + if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + // Freq domain assignment (275rb >> fsize = 16) fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) ); pos+=fsize; @@ -1336,46 +1421,50 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, break; case NR_RNTI_SI: + N_RB = mac->type0_PDCCH_CSS_config.num_rbs; + // Freq domain assignment 0-16 bit + fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) ); + pos+=fsize; + dci_pdu_rel15->frequency_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&((1<>1 ) ); - pos+=fsize; - dci_pdu_rel15->frequency_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&((1<time_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&0xf; - // Time domain assignment 4 bit - pos+=4; - dci_pdu_rel15->time_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&0xf; + // VRB to PRB mapping 1 bit + pos++; + dci_pdu_rel15->vrb_to_prb_mapping.val = (*dci_pdu>>(dci_size-pos))&0x1; - // VRB to PRB mapping 1 bit - pos++; - dci_pdu_rel15->vrb_to_prb_mapping.val = (*dci_pdu>>(dci_size-pos))&0x1; + // MCS 5bit //bit over 32, so dci_pdu ++ + pos+=5; + dci_pdu_rel15->mcs = (*dci_pdu>>(dci_size-pos))&0x1f; - // MCS 5bit //bit over 32, so dci_pdu ++ - pos+=5; - dci_pdu_rel15->mcs = (*dci_pdu>>(dci_size-pos))&0x1f; + // Redundancy version 2 bit + pos+=2; + dci_pdu_rel15->rv = (*dci_pdu>>(dci_size-pos))&3; - // Redundancy version 2 bit - pos+=2; - dci_pdu_rel15->rv = (*dci_pdu>>(dci_size-pos))&3; + // System information indicator 1 bit + pos++; + dci_pdu_rel15->system_info_indicator = (*dci_pdu>>(dci_size-pos))&0x1; - // System information indicator 1 bit - pos++; - dci_pdu_rel15->system_info_indicator = (*dci_pdu>>(dci_size-pos))&0x1; - - LOG_D(MAC,"N_RB = %i\n", N_RB); - LOG_D(MAC,"dci_size = %i\n", dci_size); - LOG_D(MAC,"fsize = %i\n", fsize); - LOG_D(MAC,"dci_pdu_rel15->frequency_domain_assignment.val = %i\n", dci_pdu_rel15->frequency_domain_assignment.val); - LOG_D(MAC,"dci_pdu_rel15->time_domain_assignment.val = %i\n", dci_pdu_rel15->time_domain_assignment.val); - LOG_D(MAC,"dci_pdu_rel15->vrb_to_prb_mapping.val = %i\n", dci_pdu_rel15->vrb_to_prb_mapping.val); - LOG_D(MAC,"dci_pdu_rel15->mcs = %i\n", dci_pdu_rel15->mcs); - LOG_D(MAC,"dci_pdu_rel15->rv = %i\n", dci_pdu_rel15->rv); - LOG_D(MAC,"dci_pdu_rel15->system_info_indicator = %i\n", dci_pdu_rel15->system_info_indicator); + LOG_D(MAC,"N_RB = %i\n", N_RB); + LOG_D(MAC,"dci_size = %i\n", dci_size); + LOG_D(MAC,"fsize = %i\n", fsize); + LOG_D(MAC,"dci_pdu_rel15->frequency_domain_assignment.val = %i\n", dci_pdu_rel15->frequency_domain_assignment.val); + LOG_D(MAC,"dci_pdu_rel15->time_domain_assignment.val = %i\n", dci_pdu_rel15->time_domain_assignment.val); + LOG_D(MAC,"dci_pdu_rel15->vrb_to_prb_mapping.val = %i\n", dci_pdu_rel15->vrb_to_prb_mapping.val); + LOG_D(MAC,"dci_pdu_rel15->mcs = %i\n", dci_pdu_rel15->mcs); + LOG_D(MAC,"dci_pdu_rel15->rv = %i\n", dci_pdu_rel15->rv); + LOG_D(MAC,"dci_pdu_rel15->system_info_indicator = %i\n", dci_pdu_rel15->system_info_indicator); break; case NR_RNTI_TC: + // check BWP id + if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + // indicating a DL DCI format - 1 bit pos++; dci_pdu_rel15->format_indicator = (*dci_pdu>>(dci_size-pos))&1; @@ -1448,38 +1537,68 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, break; case NR_UL_DCI_FORMAT_0_0: + if (mac->ULbwp[ul_bwp_id-1]) N_RB_UL=NRRIV2BW(mac->ULbwp[ul_bwp_id -1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + else N_RB_UL=NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + switch(rnti_type) { case NR_RNTI_C: //Identifier for DCI formats pos++; dci_pdu_rel15->format_indicator = (*dci_pdu>>(dci_size-pos))&1; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"Format indicator %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->format_indicator,1,dci_size-pos,*dci_pdu); +#endif if (dci_pdu_rel15->format_indicator == 1) return 1; // discard dci, format indicator not corresponding to dci_format fsize = (int)ceil( log2( (N_RB_UL*(N_RB_UL+1))>>1 ) ); pos+=fsize; dci_pdu_rel15->frequency_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&((1< %d (0x%lx)\n",dci_pdu_rel15->frequency_domain_assignment.val,fsize,dci_size-pos,*dci_pdu); +#endif // Time domain assignment 4bit pos+=4; dci_pdu_rel15->time_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&0xf; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"time-domain assignment %d (4 bits)=> %d (0x%lx)\n",dci_pdu_rel15->time_domain_assignment.val,dci_size-pos,*dci_pdu); +#endif // Frequency hopping flag  E1 bit pos++; dci_pdu_rel15->frequency_hopping_flag.val= (*dci_pdu>>(dci_size-pos))&1; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"frequency_hopping %d (1 bit)=> %d (0x%lx)\n",dci_pdu_rel15->frequency_hopping_flag.val,dci_size-pos,*dci_pdu); +#endif // MCS 5 bit pos+=5; dci_pdu_rel15->mcs= (*dci_pdu>>(dci_size-pos))&0x1f; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"mcs %d (5 bits)=> %d (0x%lx)\n",dci_pdu_rel15->mcs,dci_size-pos,*dci_pdu); +#endif // New data indicator 1bit pos++; dci_pdu_rel15->ndi= (*dci_pdu>>(dci_size-pos))&1; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"NDI %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->ndi,1,dci_size-pos,*dci_pdu); +#endif // Redundancy version 2bit pos+=2; dci_pdu_rel15->rv= (*dci_pdu>>(dci_size-pos))&3; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"RV %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->rv,2,dci_size-pos,*dci_pdu); +#endif // HARQ process number 4bit pos+=4; dci_pdu_rel15->harq_pid = (*dci_pdu>>(dci_size-pos))&0xf; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"HARQ_PID %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->harq_pid,4,dci_size-pos,*dci_pdu); +#endif // TPC command for scheduled PUSCH  E2 bits pos+=2; dci_pdu_rel15->tpc = (*dci_pdu>>(dci_size-pos))&3; +#ifdef DEBUG_EXTRACT_DCI + LOG_D(MAC,"TPC %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->tpc,2,dci_size-pos,*dci_pdu); +#endif // UL/SUL indicator  E1 bit /* commented for now (RK): need to get this from BWP descriptor if (cfg->pucch_config.pucch_GroupHopping.value) @@ -1640,10 +1759,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, // Freq domain assignment max 16 bit fsize = (int)ceil( log2( (N_RB_UL*(N_RB_UL+1))>>1 ) ); - //pos+=dci_pdu_rel15->frequency_domain_assignment.nbits; pos+=fsize; - - //pos+=dci_pdu_rel15->frequency_domain_assignment.nbits; dci_pdu_rel15->frequency_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&((1<frame; int slot = dl_info->slot; uint8_t *pduP = (dl_info->rx_ind->rx_indication_body + pdu_id)->pdsch_pdu.pdu; - int32_t pdu_len = (dl_info->rx_ind->rx_indication_body + pdu_id)->pdsch_pdu.pdu_length; + int16_t pdu_len = (int16_t)(dl_info->rx_ind->rx_indication_body + pdu_id)->pdsch_pdu.pdu_length; uint8_t gNB_index = dl_info->gNB_index; uint8_t CC_id = dl_info->cc_id; uint8_t done = 0; @@ -1797,9 +1913,6 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, mac_subheader_len = 0x0001; // default to fixed-length subheader = 1-oct mac_sdu_len = 0x0000; rx_lcid = ((NR_MAC_SUBHEADER_FIXED *)pduP)->LCID; - //#ifdef DEBUG_HEADER_PARSING - LOG_D(MAC, "[UE] LCID %d, PDU length %d\n", ((NR_MAC_SUBHEADER_FIXED *)pduP)->LCID, pdu_len); - //#endif LOG_D(MAC, "[UE] LCID %d, PDU length %d\n", rx_lcid, pdu_len); switch(rx_lcid){ @@ -2012,8 +2125,8 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, LOG_T(MAC, "\n"); #endif - if (IS_SOFTMODEM_NOS1){ - if (rx_lcid < NB_RB_MAX && rx_lcid >= DL_SCH_LCID_DTCH) { +// if (IS_SOFTMODEM_NOS1){ + if (rx_lcid < NB_RB_MAX && rx_lcid >= DL_SCH_LCID_DCCH) { mac_rlc_data_ind(module_idP, mac->crnti, @@ -2029,7 +2142,7 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, } else { LOG_E(MAC, "[UE %d] Frame %d : unknown LCID %d (gNB %d)\n", module_idP, frameP, rx_lcid, gNB_index); } - } +// } break; } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index ecf1ce1a83..cf83136a72 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -63,7 +63,7 @@ void fill_ul_config(fapi_nr_ul_config_request_t *ul_config, frame_t frame_tx, in ul_config->sfn = frame_tx; ul_config->number_pdus++; - LOG_D(MAC, "In %s: Set config request for UL transmission in [%d.%d], number of UL PDUs: %d\n", __FUNCTION__, ul_config->sfn, ul_config->slot, ul_config->number_pdus); + LOG_D(NR_MAC, "In %s: Set config request for UL transmission in [%d.%d], number of UL PDUs: %d\n", __FUNCTION__, ul_config->sfn, ul_config->slot, ul_config->number_pdus); } @@ -96,17 +96,25 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { long k2 = -1; NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Get K2 from RRC configuration - NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id-1] ? mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup : NULL; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (pusch_config->pusch_TimeDomainAllocationList) { + if (pusch_config && pusch_config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + else if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Common&& + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon&& + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } + else if (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) + pusch_TimeDomainAllocationList=mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else AssertFatal(1==0,"need to fall back to default PUSCH time-domain allocations\n"); + if (pusch_TimeDomainAllocationList) { if (time_domain_ind >= pusch_TimeDomainAllocationList->list.count) { - LOG_E(MAC, "time_domain_ind %d >= pusch->TimeDomainAllocationList->list.count %d\n", + LOG_E(NR_MAC, "time_domain_ind %d >= pusch->TimeDomainAllocationList->list.count %d\n", time_domain_ind, pusch_TimeDomainAllocationList->list.count); return -1; } @@ -117,7 +125,7 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { "Slot offset K2 (%ld) cannot be less than DURATION_RX_TO_TX (%d)\n", k2,DURATION_RX_TO_TX); - LOG_D(MAC, "get_k2(): k2 is %ld\n", k2); + LOG_D(NR_MAC, "get_k2(): k2 is %ld\n", k2); return k2; } @@ -128,22 +136,26 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) { NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_TDD_UL_DL_ConfigCommon_t *tdd_config = mac->scc==NULL ? mac->scc_SIB->tdd_UL_DL_ConfigurationCommon : mac->scc->tdd_UL_DL_ConfigurationCommon; + //Check if request to access ul_config is for a UL slot - if (is_nr_UL_slot(mac->scc, slot, mac->frame_type) == 0) { - LOG_W(MAC, "Slot %d is not a UL slot. %s called for wrong slot!!!\n", slot, __FUNCTION__); + if (is_nr_UL_slot(tdd_config, slot, mac->frame_type) == 0) { + LOG_W(NR_MAC, "Slot %d is not a UL slot. %s called for wrong slot!!!\n", slot, __FUNCTION__); return NULL; } // Calculate the index of the UL slot in mac->ul_config_request list. This is // based on the TDD pattern (slot configuration period) and number of UL+mixed // slots in the period. TS 38.213 Sec 11.1 - int mu = mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; - NR_TDD_UL_DL_Pattern_t *tdd_pattern = &mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1; + int mu = mac->ULbwp[ul_bwp_id-1] ? + mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing : + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.subcarrierSpacing; + NR_TDD_UL_DL_Pattern_t *tdd_pattern = &tdd_config->pattern1; const int num_slots_per_tdd = nr_slots_per_frame[mu] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity); const int num_slots_ul = tdd_pattern->nrofUplinkSlots + (tdd_pattern->nrofUplinkSymbols!=0); - int index = (slot + num_slots_ul - num_slots_per_tdd) % num_slots_per_tdd; + int index = slot % num_slots_ul; - LOG_D(MAC, "In %s slots per tdd %d, num_slots_ul %d, index %d\n", __FUNCTION__, + LOG_D(NR_MAC, "In %s slots per tdd %d, num_slots_ul %d, index %d\n", __FUNCTION__, num_slots_per_tdd, num_slots_ul, index); @@ -499,7 +511,6 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, uint16_t number_dmrs_symbols = 0; int N_PRB_oh = 0; - NR_ServingCellConfigCommon_t *scc = mac->scc; int rnti_type = get_rnti_type(mac, rnti); NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; @@ -508,6 +519,9 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; pusch_config_pdu->nrOfLayers = 1; pusch_config_pdu->rnti = rnti; + NR_BWP_UplinkCommon_t *initialUplinkBWP; + if (mac->scc) initialUplinkBWP = mac->scc->uplinkConfigCommon->initialUplinkBWP; + else initialUplinkBWP = &mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP; pusch_dmrs_AdditionalPosition_t add_pos = pusch_dmrs_pos2; pusch_maxLength_t dmrslength = pusch_len1; @@ -515,23 +529,39 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (rar_grant) { // Note: for Msg3 or MsgA PUSCH transmission the N_PRB_oh is always set to 0 - NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id-1]; - NR_BWP_UplinkDedicated_t *ibwp = mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP; - NR_PUSCH_Config_t *pusch_Config = ibwp->pusch_Config->choice.setup; - int startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->startSymbolAndLength; - int mappingtype = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->mappingType; + NR_BWP_UplinkDedicated_t *ibwp; + int scs,abwp_start,abwp_size,startSymbolAndLength,mappingtype; + NR_PUSCH_Config_t *pusch_Config=NULL; + if (mac->cg && ubwp && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP) { + ibwp = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP; + pusch_Config = ibwp->pusch_Config->choice.setup; + startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->startSymbolAndLength; + mappingtype = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->mappingType; - // active BWP start - int abwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - int abwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + // active BWP start + abwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + abwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + scs = ubwp->bwp_Common->genericParameters.subcarrierSpacing; + } + else { + startSymbolAndLength = initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->startSymbolAndLength; + mappingtype = initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->mappingType; - // initial BWP start - int ibwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - int ibwp_size = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + // active BWP start + abwp_start = NRRIV2PRBOFFSET(initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + abwp_size = NRRIV2BW(initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + scs = initialUplinkBWP->genericParameters.subcarrierSpacing; + } + int ibwp_start = NRRIV2PRBOFFSET(initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + int ibwp_size = NRRIV2BW(initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - // BWP start selection according to 8.3 of TS 38.213 + // BWP start selection according to 8.3 of TS 38.213 if ((ibwp_start < abwp_start) || (ibwp_size > abwp_size)) { pusch_config_pdu->bwp_start = abwp_start; pusch_config_pdu->bwp_size = abwp_size; @@ -559,11 +589,11 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->start_symbol_index = StartSymbolIndex; pusch_config_pdu->nr_of_symbols = NrOfSymbols; - l_prime_mask = get_l_prime(NrOfSymbols, mappingtype, add_pos, dmrslength, StartSymbolIndex, scc->dmrs_TypeA_Position); + l_prime_mask = get_l_prime(NrOfSymbols, mappingtype, add_pos, dmrslength, StartSymbolIndex, mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); LOG_D(MAC, "MSG3 start_sym:%d NR Symb:%d mappingtype:%d , DMRS_MASK:%x\n", pusch_config_pdu->start_symbol_index, pusch_config_pdu->nr_of_symbols, mappingtype, l_prime_mask); #ifdef DEBUG_MSG3 - LOG_D(MAC, "In %s BWP assignment (BWP (start %d, size %d) \n", __FUNCTION__, pusch_config_pdu->bwp_start, pusch_config_pdu->bwp_size); + LOG_D(NR_MAC, "In %s BWP assignment (BWP (start %d, size %d) \n", __FUNCTION__, pusch_config_pdu->bwp_start, pusch_config_pdu->bwp_size); #endif // MCS @@ -580,17 +610,17 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->scid = 0; // Transform precoding according to 6.1.3 UE procedure for applying transform precoding on PUSCH in 38.214 - pusch_config_pdu->transform_precoding = get_transformPrecoding(scc, pusch_Config, NULL, NULL, NR_RNTI_RA, 0); // TBR fix rnti and take out + pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out // Resource allocation in frequency domain according to 6.1.2.2 in TS 38.214 - pusch_config_pdu->resource_alloc = pusch_Config->resourceAllocation; + pusch_config_pdu->resource_alloc = (mac->cg) ? pusch_Config->resourceAllocation : 1; //// Completing PUSCH PDU pusch_config_pdu->mcs_table = 0; pusch_config_pdu->cyclic_prefix = 0; - pusch_config_pdu->data_scrambling_id = *scc->physCellId; - pusch_config_pdu->ul_dmrs_scrambling_id = *scc->physCellId; - pusch_config_pdu->subcarrier_spacing = ubwp->bwp_Common->genericParameters.subcarrierSpacing; + pusch_config_pdu->data_scrambling_id = mac->physCellId; + pusch_config_pdu->ul_dmrs_scrambling_id = mac->physCellId; + pusch_config_pdu->subcarrier_spacing = scs; pusch_config_pdu->vrb_to_prb_mapping = 0; pusch_config_pdu->uplink_frequency_shift_7p5khz = 0; //Optional Data only included if indicated in pduBitmap @@ -603,18 +633,29 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int target_ss; bool valid_ptrs_setup = 0; - uint16_t n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; + uint16_t n_RB_ULBWP; + if (mac->ULbwp[ul_bwp_id-1] && mac->ULbwp[ul_bwp_id-1]->bwp_Common) { + n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } + else { + pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + n_RB_ULBWP = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } + + pusch_config_pdu->bwp_size = n_RB_ULBWP; + + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1] ? mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup : NULL; // Basic sanity check for MCS value to check for a false or erroneous DCI if (dci->mcs > 28) { - LOG_W(MAC, "MCS value %d out of bounds! Possibly due to false DCI. Ignoring DCI!\n", dci->mcs); + LOG_W(NR_MAC, "MCS value %d out of bounds! Possibly due to false DCI. Ignoring DCI!\n", dci->mcs); return -1; } /* Transform precoding */ if (rnti_type != NR_RNTI_CS || (rnti_type == NR_RNTI_CS && dci->ndi == 1)) { - pusch_config_pdu->transform_precoding = get_transformPrecoding(scc, pusch_Config, NULL, dci_format, rnti_type, 0); + pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); } /*DCI format-related configuration*/ @@ -633,24 +674,33 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } else { - LOG_E(MAC, "In %s: UL grant from DCI format %d is not handled...\n", __FUNCTION__, *dci_format); + LOG_E(NR_MAC, "In %s: UL grant from DCI format %d is not handled...\n", __FUNCTION__, *dci_format); return -1; } NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (pusch_Config->pusch_TimeDomainAllocationList) { + if (pusch_Config && pusch_Config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_Config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + else if (mac->ULbwp[mac->UL_BWP_Id-1] && + mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common&& + mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon&& + mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } + else if (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) + pusch_TimeDomainAllocationList=mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else AssertFatal(1==0,"need to fall back to default PUSCH time-domain allocations\n"); int mappingtype = pusch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType; NR_DMRS_UplinkConfig_t *NR_DMRS_ulconfig = NULL; - 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) { + NR_DMRS_ulconfig = (mappingtype == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA) + ? pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup : pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + } /* TRANSFORM PRECODING ------------------------------------------------------------------------------------------*/ @@ -674,7 +724,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, else AssertFatal(1==0,"SequenceGroupHopping or sequenceHopping are NOT Supported\n"); - LOG_D(MAC,"TRANSFORM PRECODING IS ENABLED. CDM groups: %d, U: %d \n", pusch_config_pdu->num_dmrs_cdm_grps_no_data, + LOG_D(NR_MAC,"TRANSFORM PRECODING IS ENABLED. CDM groups: %d, U: %d \n", pusch_config_pdu->num_dmrs_cdm_grps_no_data, pusch_config_pdu->dfts_ofdm.low_papr_group_number); } @@ -691,7 +741,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } /* FREQ_HOPPING_FLAG */ - if ((pusch_Config->frequencyHopping!=NULL) && (pusch_Config->resourceAllocation != NR_PUSCH_Config__resourceAllocation_resourceAllocationType0)){ + if ((pusch_Config!=NULL) && (pusch_Config->frequencyHopping!=NULL) && (pusch_Config->resourceAllocation != NR_PUSCH_Config__resourceAllocation_resourceAllocationType0)){ pusch_config_pdu->frequency_hopping = dci->frequency_hopping_flag.val; } @@ -700,9 +750,9 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* MCS TABLE */ if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { - pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config->mcs_Table, 0, *dci_format, rnti_type, target_ss, false); + pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_Table : NULL, 0, *dci_format, rnti_type, target_ss, false); } else { - pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config->mcs_TableTransformPrecoder, 1, *dci_format, rnti_type, target_ss, false); + pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_TableTransformPrecoder : NULL, 1, *dci_format, rnti_type, target_ss, false); } /* NDI */ @@ -732,19 +782,30 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } /* DMRS */ - l_prime_mask = get_l_prime(pusch_config_pdu->nr_of_symbols, mappingtype, add_pos, dmrslength, pusch_config_pdu->start_symbol_index, scc->dmrs_TypeA_Position); - if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) + l_prime_mask = get_l_prime(pusch_config_pdu->nr_of_symbols, mappingtype, add_pos, dmrslength, pusch_config_pdu->start_symbol_index, mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); + if ((mac->ULbwp[ul_bwp_id -1] && pusch_config_pdu->transform_precoding == transform_precoder_disabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; + else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[ul_bwp_id -1] && pusch_config_pdu->transform_precoding == transform_precoder_enabled)) + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; // Num PRB Overhead from PUSCH-ServingCellConfig - if (mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead == NULL) { - N_PRB_oh = 0; - } else { - N_PRB_oh = *mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead; - } + if (mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead) + N_PRB_oh = *mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead; + + else N_PRB_oh = 0; /* PTRS */ - if (mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS != NULL) { + if (mac->ULbwp[mac->UL_BWP_Id -1] && + mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated && + mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config && + mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && + mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; @@ -756,13 +817,13 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if(valid_ptrs_setup==true) { pusch_config_pdu->pdu_bit_map |= PUSCH_PDU_BITMAP_PUSCH_PTRS; } - LOG_D(MAC, "UL PTRS values: PTRS time den: %d, PTRS freq den: %d\n", pusch_config_pdu->pusch_ptrs.ptrs_time_density, pusch_config_pdu->pusch_ptrs.ptrs_freq_density); + LOG_D(NR_MAC, "UL PTRS values: PTRS time den: %d, PTRS freq den: %d\n", pusch_config_pdu->pusch_ptrs.ptrs_time_density, pusch_config_pdu->pusch_ptrs.ptrs_freq_density); } } } - LOG_D(MAC, "In %s: received UL grant (rb_start %d, rb_size %d, start_symbol_index %d, nr_of_symbols %d) for RNTI type %s \n", + LOG_D(NR_MAC, "In %s: received UL grant (rb_start %d, rb_size %d, start_symbol_index %d, nr_of_symbols %d) for RNTI type %s \n", __FUNCTION__, pusch_config_pdu->rb_start, pusch_config_pdu->rb_size, @@ -775,7 +836,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->qam_mod_order = nr_get_Qm_ul(pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table); if (pusch_config_pdu->target_code_rate == 0 || pusch_config_pdu->qam_mod_order == 0) { - LOG_W(MAC, "In %s: Invalid code rate or Mod order, likely due to unexpected UL DCI. Ignoring DCI! \n", __FUNCTION__); + LOG_W(NR_MAC, "In %s: Invalid code rate or Mod order, likely due to unexpected UL DCI. Ignoring DCI! \n", __FUNCTION__); return -1; } @@ -818,8 +879,8 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in // check type0 from 38.213 13 if we have no CellGroupConfig // TODO: implementation to be completed - if (mac->scg == NULL) { - + LOG_D(NR_MAC,"nr_ue_scheduler(): mac->cg %p\n",mac->cg); + if (mac->cg == NULL) { if(dl_info->ssb_index != -1){ if(mac->type0_pdcch_ss_mux_pattern == 1){ @@ -860,17 +921,39 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.rnti = 0xaaaa; // to be set dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP = 106; // to be set - LOG_I(MAC,"nr_ue_scheduler Type0 PDCCH with rnti %x, BWP %d\n", + LOG_I(NR_MAC,"nr_ue_scheduler Type0 PDCCH with rnti %x, BWP %d\n", dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.rnti, dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP); */ - dl_config->number_pdus = dl_config->number_pdus + 1; + NR_SearchSpace_t *ss0 = mac->search_space_zero; + fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; - fill_scheduled_response(&scheduled_response, dl_config, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); - if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) - mac->if_module->scheduled_response(&scheduled_response); + + if( mac->scc == NULL && mac->scc_SIB == NULL && (rx_frame%2 == mac->type0_PDCCH_CSS_config.sfn_c) && (rx_slot == mac->type0_PDCCH_CSS_config.n_0) ){ + rel15->num_dci_options = 1; + rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; + config_dci_pdu(mac, rel15, dl_config, NR_RNTI_SI, -1); + fill_dci_search_candidates(ss0, rel15); + dl_config->number_pdus = 1; + LOG_D(NR_MAC,"Calling fill_scheduled_response, type0_pdcch, num_pdus %d\n",dl_config->number_pdus); + fill_scheduled_response(&scheduled_response, dl_config, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); + if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) + mac->if_module->scheduled_response(&scheduled_response); + } + // this is for Msg2/Msg4 + if (mac->ra.ra_state >= WAIT_RAR) { + rel15->num_dci_options = 1; + rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; + config_dci_pdu(mac, rel15, dl_config, mac->ra.ra_state == WAIT_RAR ? NR_RNTI_RA : NR_RNTI_TC , -1); + fill_dci_search_candidates(ss0, rel15); + dl_config->number_pdus = 1; + LOG_D(NR_MAC,"mac->cg %p: Calling fill_scheduled_response rnti %x, type0_pdcch, num_pdus %d\n",mac->cg,rel15->rnti,dl_config->number_pdus); + fill_scheduled_response(&scheduled_response, dl_config, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); + if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) + mac->if_module->scheduled_response(&scheduled_response); + } } - } else { // we have an scg + } else { // we have a Master or Secondary CellGroupConfig dcireq.module_id = mod_id; dcireq.gNB_index = gNB_index; @@ -916,9 +999,9 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in // Schedule ULSCH only if the current frame and slot match those in ul_config_req // AND if a UL grant (UL DCI or Msg3) has been received (as indicated by num_pdus) - if ((ul_info->slot_tx == ul_config->slot && ul_info->frame_tx == ul_config->sfn) && ul_config->number_pdus > 0){ + if (ul_config && (ul_info->slot_tx == ul_config->slot && ul_info->frame_tx == ul_config->sfn) && ul_config->number_pdus > 0){ - LOG_D(MAC, "In %s:[%d.%d]: number of UL PDUs: %d with UL transmission in [%d.%d]\n", __FUNCTION__, frame_tx, slot_tx, ul_config->number_pdus, ul_config->sfn, ul_config->slot); + LOG_D(NR_MAC, "In %s:[%d.%d]: number of UL PDUs: %d with UL transmission in [%d.%d]\n", __FUNCTION__, frame_tx, slot_tx, ul_config->number_pdus, ul_config->sfn, ul_config->slot); uint8_t ulsch_input_buffer[MAX_ULSCH_PAYLOAD_BYTES]; uint8_t data_existing = 0; @@ -932,15 +1015,26 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in if (ulcfg_pdu->pdu_type == FAPI_NR_UL_CONFIG_TYPE_PUSCH) { uint16_t TBS_bytes = ulcfg_pdu->pusch_config_pdu.pusch_data.tb_size; - + LOG_D(NR_MAC,"harq_id %d, NDI %d NDI_DCI %d, TBS_bytes %d (ra_state %d\n", + ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id, + mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id], + ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator, + TBS_bytes, + ra->ra_state); if (ra->ra_state == WAIT_RAR && !ra->cfra){ memcpy(ulsch_input_buffer, mac->ulsch_pdu.payload, TBS_bytes); LOG_D(NR_MAC,"[RAPROC] Msg3 to be transmitted:\n"); for (int k = 0; k < TBS_bytes; k++) { LOG_D(NR_MAC,"(%i): 0x%x\n",k,mac->ulsch_pdu.payload[k]); } + LOG_D(NR_MAC,"Flipping NDI for harq_id %d (Msg3)\n",ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator); + mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] = ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator; + mac->first_ul_tx[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] = 0; } else { - if (IS_SOFTMODEM_NOS1 && (mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] != ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator)){ + + if ( (mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] != ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator || + mac->first_ul_tx[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id]==1)){ + // Getting IP traffic to be transmitted data_existing = nr_ue_get_sdu(mod_id, cc_id, @@ -952,15 +1046,19 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in &access_mode); } + LOG_D(NR_MAC,"Flipping NDI for harq_id %d\n",ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator); mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] = ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator; + mac->first_ul_tx[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] = 0; + //Random traffic to be transmitted if there is no IP traffic available for this Tx opportunity - if (!IS_SOFTMODEM_NOS1 || !data_existing) { + if (!data_existing) { //Use zeros for the header bytes in noS1 mode, in order to make sure that the LCID is not valid //and block this traffic from being forwarded to the upper layers at the gNB LOG_D(PHY, "In %s: Random data to be transmitted: TBS_bytes %d \n", __FUNCTION__, TBS_bytes); - // Make the first byte padding so that gNB ignores the PHY random - // data in the TB for the PHY at the MAC layer + //Give the first byte a dummy value (a value not corresponding to any valid LCID based on 38.321, Table 6.2.1-2) + //in order to distinguish the PHY random packets at the MAC layer of the gNB receiver from the normal packets that should + //have a valid LCID (nr_process_mac_pdu function) ulsch_input_buffer[0] = UL_SCH_LCID_PADDING; for (int i = 1; i < TBS_bytes; i++) { @@ -1022,9 +1120,15 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, int delta = 0; NR_BWP_Uplink_t *ubwp = mac->ULbwp[mac->UL_BWP_Id-1]; + // Get the numerology to calculate the Tx frame and slot - int mu = ubwp->bwp_Common->genericParameters.subcarrierSpacing; - struct NR_PUSCH_TimeDomainResourceAllocationList *pusch_TimeDomainAllocationList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + int mu = ubwp ? + ubwp->bwp_Common->genericParameters.subcarrierSpacing : + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.subcarrierSpacing; + + NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = ubwp ? + ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList: + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; // k2 as per 3GPP TS 38.214 version 15.9.0 Release 15 ch 6.1.2.1.1 // PUSCH time domain resource allocation is higher layer configured from uschTimeDomainAllocationList in either pusch-ConfigCommon int k2; @@ -1072,14 +1176,14 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, } - LOG_D(MAC, "In %s: currently at [%d.%d] UL transmission in [%d.%d] (k2 %d delta %d)\n", __FUNCTION__, current_frame, current_slot, *frame_tx, *slot_tx, k2, delta); + LOG_D(NR_MAC, "In %s: currently at [%d.%d] UL transmission in [%d.%d] (k2 %d delta %d)\n", __FUNCTION__, current_frame, current_slot, *frame_tx, *slot_tx, k2, delta); return 0; } // Build the list of all the valid RACH occasions in the maximum association pattern period according to the PRACH config -static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { +static void build_ro_list(NR_UE_MAC_INST_t *mac) { int x,y; // PRACH Configuration Index table variables used to compute the valid frame numbers int y2; // PRACH Configuration Index table additional variable used to compute the valid frame numbers @@ -1097,7 +1201,6 @@ static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { int nb_fdm; uint8_t config_index, mu; - uint32_t pointa; int msg1_FDM; uint8_t prach_conf_period_idx; @@ -1105,18 +1208,21 @@ static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { uint8_t prach_conf_period_frame_idx; int64_t *prach_config_info_p; - NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; - NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; + NR_RACH_ConfigCommon_t *setup = (mac->scc) ? + mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup: + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric; config_index = rach_ConfigGeneric->prach_ConfigurationIndex; - if (setup->msg1_SubcarrierSpacing) + if (setup->msg1_SubcarrierSpacing) { mu = *setup->msg1_SubcarrierSpacing; - else - mu = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + } else if(mac->scc) { + mu = mac->scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + } else { + mu = get_softmodem_params()->numerology; + } - pointa = frequencyInfoDL->absoluteFrequencyPointA; msg1_FDM = rach_ConfigGeneric->msg1_FDM; switch (msg1_FDM){ @@ -1134,12 +1240,13 @@ static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { // ============================== // WIP: For now assume no rejected PRACH occasions because of conflict with SSB or TDD_UL_DL_ConfigurationCommon schedule + int unpaired = mac->phy_config.config_req.cell_config.frame_duplex_type; // Identify the proper PRACH Configuration Index table according to the operating frequency - LOG_D(MAC,"Pointa %u, mu = %u, PRACH config index = %u, unpaired = %u\n", pointa, mu, config_index, unpaired); + LOG_D(NR_MAC,"mu = %u, PRACH config index = %u, unpaired = %u\n", mu, config_index, unpaired); - prach_config_info_p = get_prach_config_info(pointa, config_index, unpaired); + prach_config_info_p = get_prach_config_info(mac->frequency_range, config_index, unpaired); - if (pointa > 2016666) { //FR2 + if (mac->frequency_range == FR2) { //FR2 x = prach_config_info_p[2]; y = prach_config_info_p[3]; @@ -1170,6 +1277,7 @@ static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { prach_conf_start_symbol = prach_config_info_p[5]; N_t_slot = prach_config_info_p[7]; N_dur = prach_config_info_p[8]; + LOG_D(NR_MAC,"N_t_slot %d, N_dur %d\n",N_t_slot,N_dur); if (prach_config_info_p[1] != -1) format2 = (uint8_t) prach_config_info_p[1]; format = ((uint8_t) prach_config_info_p[0]) | (format2<<8); @@ -1185,7 +1293,7 @@ static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { prach_assoc_pattern.nb_of_prach_conf_period_in_max_period = MAX_NB_PRACH_CONF_PERIOD_IN_ASSOCIATION_PATTERN_PERIOD / x; nb_of_frames_per_prach_conf_period = x; - LOG_D(MAC,"nb_of_prach_conf_period_in_max_period %d\n", prach_assoc_pattern.nb_of_prach_conf_period_in_max_period); + LOG_D(NR_MAC,"nb_of_prach_conf_period_in_max_period %d\n", prach_assoc_pattern.nb_of_prach_conf_period_in_max_period); // Fill in the PRACH occasions table for every slot in every frame in every PRACH configuration periods in the maximum association pattern period // ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -1197,14 +1305,14 @@ static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_frame = nb_of_frames_per_prach_conf_period; prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_slot = nr_slots_per_frame[mu]; - LOG_D(MAC,"PRACH Conf Period Idx %d\n", prach_conf_period_idx); + LOG_D(NR_MAC,"PRACH Conf Period Idx %d\n", prach_conf_period_idx); // For every frames in a PRACH configuration period // ------------------------------------------------ for (prach_conf_period_frame_idx=0; prach_conf_period_frame_idxformat = format; prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion++; - LOG_D(MAC,"Adding a PRACH occasion: frame %u, slot-symbol %d-%d, occ_in_time-occ_in-freq %d-%d, nb ROs in conf period %d, for this slot: RO# in time %d, RO# in freq %d\n", + LOG_D(NR_MAC,"Adding a PRACH occasion: frame %u, slot-symbol %d-%d, occ_in_time-occ_in-freq %d-%d, nb ROs in conf period %d, for this slot: RO# in time %d, RO# in freq %d\n", frame, slot, start_symbol, n_prach_occ_in_time, n_prach_occ_in_freq, prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion, prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_time, prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_freq); @@ -1257,7 +1365,7 @@ static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) { } // Build the list of all the valid/transmitted SSBs according to the config -static void build_ssb_list(NR_ServingCellConfigCommon_t *scc) { +static void build_ssb_list(NR_UE_MAC_INST_t *mac) { // Create the list of transmitted SSBs // =================================== @@ -1265,19 +1373,21 @@ static void build_ssb_list(NR_ServingCellConfigCommon_t *scc) { uint64_t ssb_positionsInBurst; uint8_t ssb_idx = 0; - switch (scc->ssb_PositionsInBurst->present) { + if (mac->scc) { + NR_ServingCellConfigCommon_t *scc = mac->scc; + switch (scc->ssb_PositionsInBurst->present) { case NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_shortBitmap: ssb_bitmap = &scc->ssb_PositionsInBurst->choice.shortBitmap; ssb_positionsInBurst = BIT_STRING_to_uint8(ssb_bitmap); - LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst); + LOG_D(NR_MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst); for (uint8_t bit_nb=3; bit_nb<=3; bit_nb--) { // If SSB is transmitted if ((ssb_positionsInBurst>>bit_nb) & 0x01) { ssb_list.nb_tx_ssb++; ssb_list.tx_ssb[ssb_idx].transmitted = true; - LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx); + LOG_D(NR_MAC,"SSB idx %d transmitted\n", ssb_idx); } ssb_idx++; } @@ -1286,14 +1396,14 @@ static void build_ssb_list(NR_ServingCellConfigCommon_t *scc) { ssb_bitmap = &scc->ssb_PositionsInBurst->choice.mediumBitmap; ssb_positionsInBurst = BIT_STRING_to_uint8(ssb_bitmap); - LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst); + LOG_D(NR_MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst); for (uint8_t bit_nb=7; bit_nb<=7; bit_nb--) { // If SSB is transmitted if ((ssb_positionsInBurst>>bit_nb) & 0x01) { ssb_list.nb_tx_ssb++; ssb_list.tx_ssb[ssb_idx].transmitted = true; - LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx); + LOG_D(NR_MAC,"SSB idx %d transmitted\n", ssb_idx); } ssb_idx++; } @@ -1302,14 +1412,14 @@ static void build_ssb_list(NR_ServingCellConfigCommon_t *scc) { ssb_bitmap = &scc->ssb_PositionsInBurst->choice.longBitmap; ssb_positionsInBurst = BIT_STRING_to_uint64(ssb_bitmap); - LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst); + LOG_D(NR_MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst); for (uint8_t bit_nb=63; bit_nb<=63; bit_nb--) { // If SSB is transmitted if ((ssb_positionsInBurst>>bit_nb) & 0x01) { ssb_list.nb_tx_ssb++; ssb_list.tx_ssb[ssb_idx].transmitted = true; - LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx); + LOG_D(NR_MAC,"SSB idx %d transmitted\n", ssb_idx); } ssb_idx++; } @@ -1317,16 +1427,36 @@ static void build_ssb_list(NR_ServingCellConfigCommon_t *scc) { default: AssertFatal(false,"ssb_PositionsInBurst not present\n"); break; + } + } else { // This is configuration from SIB1 + + AssertFatal(mac->scc_SIB->ssb_PositionsInBurst.groupPresence == NULL, "Handle case for >8 SSBs\n"); + ssb_bitmap = &mac->scc_SIB->ssb_PositionsInBurst.inOneGroup; + + ssb_positionsInBurst = BIT_STRING_to_uint8(ssb_bitmap); + LOG_D(NR_MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst); + + for (uint8_t bit_nb=7; bit_nb<=7; bit_nb--) { + // If SSB is transmitted + if ((ssb_positionsInBurst>>bit_nb) & 0x01) { + ssb_list.nb_tx_ssb++; + ssb_list.tx_ssb[ssb_idx].transmitted = true; + LOG_D(NR_MAC,"SSB idx %d transmitted\n", ssb_idx); + } + ssb_idx++; + } } } // Map the transmitted SSBs to the ROs and create the association pattern according to the config -static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) { +static void map_ssb_to_ro(NR_UE_MAC_INST_t *mac) { // Map SSBs to PRACH occasions // =========================== // WIP: Assumption: No PRACH occasion is rejected because of a conflict with SSBs or TDD_UL_DL_ConfigurationCommon schedule - NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_RACH_ConfigCommon_t *setup = (mac->scc) ? + mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup: + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR ssb_perRACH_config = setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present; boolean_t multiple_ssb_per_ro; // true if more than one or exactly one SSB per RACH occasion, false if more than one RO per SSB @@ -1373,14 +1503,14 @@ static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) { AssertFatal(1 == 0, "Unsupported ssb_perRACH_config %d\n", ssb_perRACH_config); break; } - LOG_D(MAC,"SSB rach ratio %d, Multiple SSB per RO %d\n", ssb_rach_ratio, multiple_ssb_per_ro); + LOG_D(NR_MAC,"SSB rach ratio %d, Multiple SSB per RO %d\n", ssb_rach_ratio, multiple_ssb_per_ro); // Evaluate the number of PRACH configuration periods required to map all the SSBs and set the association period // ============================================================================================================== // WIP: Assumption for now is that all the PRACH configuration periods within a maximum association pattern period have the same number of PRACH occasions // (No PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule) // There is only one possible association period which can contain up to 16 PRACH configuration periods - LOG_D(MAC,"Evaluate the number of PRACH configuration periods required to map all the SSBs and set the association period\n"); + LOG_D(NR_MAC,"Evaluate the number of PRACH configuration periods required to map all the SSBs and set the association period\n"); if (true == multiple_ssb_per_ro) { required_nb_of_prach_occasion = ((ssb_list.nb_tx_ssb-1) + ssb_rach_ratio) / ssb_rach_ratio; } @@ -1388,6 +1518,7 @@ static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) { required_nb_of_prach_occasion = ssb_list.nb_tx_ssb * ssb_rach_ratio; } + AssertFatal(prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion>0,"prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion shouldn't be 0 (ssb_list.nb_tx_ssb %d, ssb_rach_ratio %d\n",ssb_list.nb_tx_ssb,ssb_rach_ratio); required_nb_of_prach_conf_period = ((required_nb_of_prach_occasion-1) + prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion) / prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion; if (required_nb_of_prach_conf_period == 1) { @@ -1413,7 +1544,7 @@ static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) { prach_assoc_pattern.prach_association_period_list[0].nb_of_frame = prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period * prach_assoc_pattern.prach_conf_period_list[0].nb_of_frame; prach_assoc_pattern.nb_of_frame = prach_assoc_pattern.prach_association_period_list[0].nb_of_frame; - LOG_D(MAC,"Assoc period %d, Nb of frames in assoc period %d\n", + LOG_D(NR_MAC,"Assoc period %d, Nb of frames in assoc period %d\n", prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period, prach_assoc_pattern.prach_association_period_list[0].nb_of_frame); @@ -1425,7 +1556,7 @@ static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) { prach_conf_period_t *prach_conf_period_p; // Map all the association periods within the association pattern period - LOG_D(MAC,"Proceed to the SSB to RO mapping\n"); + LOG_D(NR_MAC,"Proceed to the SSB to RO mapping\n"); for (association_period_idx=0; association_period_idx ssb_list.tx_ssb[ssb_idx].nb_mapped_ro,"Too many mapped ROs (%d) to a single SSB\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro); - LOG_D(MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq); - LOG_D(MAC,"Nb mapped ROs for this ssb idx: in the association period only %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro); + LOG_D(NR_MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq); + LOG_D(NR_MAC,"Nb mapped ROs for this ssb idx: in the association period only %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro); // If all the required SSBs are mapped to this RO, exit the loop of SSBs if (ro_p->nb_mapped_ssb == ssb_rach_ratio) { @@ -1521,6 +1652,9 @@ static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) { for (ssb_idx=0; ssb_idx %d\n", + ssb_idx,ssb_list.tx_ssb[ssb_idx].transmitted); + // Map only the transmitted ssb_idx if (true == ssb_list.tx_ssb[ssb_idx].transmitted) { @@ -1545,8 +1679,8 @@ static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) { AssertFatal(MAX_NB_RO_PER_SSB_IN_ASSOCIATION_PATTERN > ssb_list.tx_ssb[ssb_idx].nb_mapped_ro,"Too many mapped ROs (%d) to a single SSB\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro); nb_mapped_ro_in_association_period++; - LOG_D(MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq); - LOG_D(MAC,"Nb mapped ROs for this ssb idx: in the association period only %u / total %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro, nb_mapped_ro_in_association_period); + LOG_D(NR_MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq); + LOG_D(NR_MAC,"Nb mapped ROs for this ssb idx: in the association period only %u / total %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro, nb_mapped_ro_in_association_period); // Exit the loop if this SSB has been mapped to all the required ROs // WIP: Assuming that ssb_rach_ratio equals the maximum nb of times a given ssb_idx is mapped within an association period: @@ -1609,7 +1743,9 @@ static int get_nr_prach_info_from_ssb_index(uint8_t ssb_idx, // - exact slot number // - frame offset ssb_info_p = &ssb_list.tx_ssb[ssb_idx]; + LOG_D(NR_MAC,"checking for prach : ssb_info_p->nb_mapped_ro %d\n",ssb_info_p->nb_mapped_ro); for (uint8_t n_mapped_ro=0; n_mapped_ronb_mapped_ro; n_mapped_ro++) { + LOG_D(NR_MAC,"%d.%d: mapped_ro[%d]->frame.slot %d.%d, prach_assoc_pattern.nb_of_frame %d\n",frame,slot,n_mapped_ro,ssb_info_p->mapped_ro[n_mapped_ro]->frame,ssb_info_p->mapped_ro[n_mapped_ro]->slot,prach_assoc_pattern.nb_of_frame); if ((slot == ssb_info_p->mapped_ro[n_mapped_ro]->slot) && (ssb_info_p->mapped_ro[n_mapped_ro]->frame == (frame % prach_assoc_pattern.nb_of_frame))) { @@ -1669,24 +1805,24 @@ static int get_nr_prach_info_from_ssb_index(uint8_t ssb_idx, } // Build the SSB to RO mapping upon RRC configuration update -void build_ssb_to_ro_map(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired){ +void build_ssb_to_ro_map(NR_UE_MAC_INST_t *mac) { // Clear all the lists and maps memset(&prach_assoc_pattern, 0, sizeof(prach_association_pattern_t)); memset(&ssb_list, 0, sizeof(ssb_list_info_t)); // Build the list of all the valid RACH occasions in the maximum association pattern period according to the PRACH config - LOG_D(MAC,"Build RO list\n"); - build_ro_list(scc, unpaired); + LOG_D(NR_MAC,"Build RO list\n"); + build_ro_list(mac); // Build the list of all the valid/transmitted SSBs according to the config - LOG_D(MAC,"Build SSB list\n"); - build_ssb_list(scc); + LOG_D(NR_MAC,"Build SSB list\n"); + build_ssb_list(mac); // Map the transmitted SSBs to the ROs and create the association pattern according to the config - LOG_D(MAC,"Map SSB to RO\n"); - map_ssb_to_ro(scc); - LOG_D(MAC,"Map SSB to RO done\n"); + LOG_D(NR_MAC,"Map SSB to RO\n"); + map_ssb_to_ro(mac); + LOG_D(NR_MAC,"Map SSB to RO done\n"); } // This function schedules the PRACH according to prach_ConfigurationIndex and TS 38.211, tables 6.3.3.2.x @@ -1710,13 +1846,17 @@ void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t s nr_scheduled_response_t scheduled_response; NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_ServingCellConfigCommonSIB_t *scc_SIB = mac->scc_SIB; + NR_RACH_ConfigCommon_t *setup; + if (scc!=NULL) setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + else setup = scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric; ra->RA_offset = 2; // to compensate the rx frame offset at the gNB ra->generate_nr_prach = GENERATE_IDLE; // Reset flag for PRACH generation + NR_TDD_UL_DL_ConfigCommon_t *tdd_config = scc==NULL ? scc_SIB->tdd_UL_DL_ConfigurationCommon : scc->tdd_UL_DL_ConfigurationCommon; - if (is_nr_UL_slot(scc, slotP, mac->frame_type)) { + if (is_nr_UL_slot(tdd_config, slotP, mac->frame_type)) { // WIP Need to get the proper selected ssb_idx // Initial beam selection functionality is not available yet @@ -1746,7 +1886,7 @@ void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t s ncs = get_NCS(rach_ConfigGeneric->zeroCorrelationZoneConfig, format0, setup->restrictedSetConfig); - prach_config_pdu->phys_cell_id = *scc->physCellId; + prach_config_pdu->phys_cell_id = mac->physCellId; prach_config_pdu->num_prach_ocas = 1; prach_config_pdu->prach_slot = prach_occasion_info_p->slot; prach_config_pdu->prach_start_symbol = prach_occasion_info_p->start_symbol; @@ -1757,7 +1897,7 @@ void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t s prach_config_pdu->restricted_set = prach_config->restricted_set_config; prach_config_pdu->freq_msg1 = prach_config->num_prach_fd_occasions_list[prach_occasion_info_p->fdm].k1; - LOG_D(MAC,"Selected RO Frame %u, Slot %u, Symbol %u, Fdm %u\n", frameP, prach_config_pdu->prach_slot, prach_config_pdu->prach_start_symbol, prach_config_pdu->num_ra); + LOG_D(NR_MAC,"Selected RO Frame %u, Slot %u, Symbol %u, Fdm %u\n", frameP, prach_config_pdu->prach_slot, prach_config_pdu->prach_start_symbol, prach_config_pdu->num_ra); // Search which SSB is mapped in the RO (among all the SSBs mapped to this RO) for (prach_config_pdu->ssb_nb_in_ro=0; prach_config_pdu->ssb_nb_in_ronb_mapped_ssb; prach_config_pdu->ssb_nb_in_ro++) { @@ -1826,6 +1966,7 @@ void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t s } // if is_nr_UL_slot } +#define MAX_LCID 8 //Fixme: also defined in LCID table uint8_t nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t subframe, uint8_t eNB_index, @@ -1833,17 +1974,16 @@ nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, uint8_t total_rlc_pdu_header_len = 0; int16_t buflen_remain = 0; uint8_t lcid = 0; - uint16_t sdu_lengths[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - uint8_t sdu_lcids[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + uint16_t sdu_lengths[MAX_LCID] = { 0 }; + uint8_t sdu_lcids[MAX_LCID] = { 0 }; uint16_t payload_offset = 0, num_sdus = 0; uint8_t ulsch_sdus[MAX_ULSCH_PAYLOAD_BYTES]; uint16_t sdu_length_total = 0; //unsigned short post_padding = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(module_idP); - rlc_buffer_occupancy_t lcid_buffer_occupancy_old = - 0, lcid_buffer_occupancy_new = 0; - LOG_D(MAC, + rlc_buffer_occupancy_t lcid_buffer_occupancy_new = 0; + LOG_D(NR_MAC, "[UE %d] MAC PROCESS UL TRANSPORT BLOCK at frame%d subframe %d TBS=%d\n", module_idP, frameP, subframe, buflen); AssertFatal(CC_id == 0, @@ -1852,23 +1992,20 @@ nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, // Check for DCCH first // TO DO: Multiplex in the order defined by the logical channel prioritization for (lcid = UL_SCH_LCID_SRB1; - lcid < NR_MAX_NUM_LCID; lcid++) { - - lcid_buffer_occupancy_old = mac_rlc_get_buffer_occupancy_ind(module_idP, mac->crnti, eNB_index, frameP, subframe, ENB_FLAG_NO, lcid); - lcid_buffer_occupancy_new = lcid_buffer_occupancy_old; - - if(lcid_buffer_occupancy_new){ + lcid < MAX_LCID; lcid++) { + lcid_buffer_occupancy_new = mac_rlc_get_buffer_occupancy_ind(module_idP, mac->crnti, eNB_index, frameP, subframe, ENB_FLAG_NO, lcid); + if(lcid_buffer_occupancy_new) { buflen_remain = buflen - (total_rlc_pdu_header_len + sdu_length_total + MAX_RLC_SDU_SUBHEADER_SIZE); - LOG_D(MAC, + LOG_D(NR_MAC, "[UE %d] Frame %d : UL-DXCH -> ULSCH, RLC %d has %d bytes to " "send (Transport Block size %d SDU Length Total %d , mac header len %d, buflen_remain %d )\n", //BSR byte before Tx=%d module_idP, frameP, lcid, lcid_buffer_occupancy_new, buflen, sdu_length_total, total_rlc_pdu_header_len, buflen_remain); // ,nr_ue_mac_inst->scheduling_info.BSR_bytes[nr_ue_mac_inst->scheduling_info.LCGID[lcid]] - while(buflen_remain > 0 && lcid_buffer_occupancy_new){ + while(buflen_remain > 0 && lcid_buffer_occupancy_new){ sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, mac->crnti, @@ -1896,20 +2033,13 @@ nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, } /* Get updated BO after multiplexing this PDU */ - lcid_buffer_occupancy_new = mac_rlc_get_buffer_occupancy_ind(module_idP, - mac->crnti, - eNB_index, - frameP, - subframe, - ENB_FLAG_NO, - lcid); + lcid_buffer_occupancy_new = mac_rlc_get_buffer_occupancy_ind(module_idP,mac->crnti,eNB_index,frameP, subframe, ENB_FLAG_NO, lcid); buflen_remain = buflen - (total_rlc_pdu_header_len + sdu_length_total + MAX_RLC_SDU_SUBHEADER_SIZE); - } + } + } } -} - // Generate ULSCH PDU if (num_sdus>0) { payload_offset = nr_generate_ulsch_pdu(ulsch_sdus, @@ -1924,9 +2054,9 @@ nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, 0, // long_bsr 0, // post_padding buflen); // TBS in bytes + } else { + return 0; } - else - return 0; // Padding: fill remainder of ULSCH with 0 if (buflen - payload_offset > 0){ @@ -1935,7 +2065,7 @@ nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, } #if defined(ENABLE_MAC_PAYLOAD_DEBUG) - LOG_I(MAC, "Printing UL MAC payload UE side, payload_offset: %d \n", payload_offset); + LOG_I(NR_MAC, "Printing UL MAC payload UE side, payload_offset: %d \n", payload_offset); for (int i = 0; i < buflen ; i++) { //harq_process_ul_ue->a[i] = (unsigned char) rand(); //printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]); diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 98e9ce4351..87cc7851ff 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -54,6 +54,87 @@ extern RAN_CONTEXT_t RC; extern void mac_top_init_gNB(void); extern uint8_t nfapi_mode; +void process_rlcBearerConfig(struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list, + struct NR_CellGroupConfig__rlc_BearerToReleaseList *rlc_bearer2release_list, + NR_UE_sched_ctrl_t *sched_ctrl) { + + if (rlc_bearer2add_list) + // keep lcids + for (int i=0;ilist.count;i++) { + sched_ctrl->lcid_mask |= (1<list.array[i]->logicalChannelIdentity); + LOG_I(NR_MAC,"Adding LCID %d (%s %d)\n", + (int)rlc_bearer2add_list->list.array[i]->logicalChannelIdentity, + rlc_bearer2add_list->list.array[i]->logicalChannelIdentity<4 ? "SRB" : "DRB", + (int)rlc_bearer2add_list->list.array[i]->logicalChannelIdentity); + } + if (rlc_bearer2release_list) + for (int i=0;ilist.count;i++) + sched_ctrl->lcid_mask |= (1<<*rlc_bearer2release_list->list.array[i]); + +} + + +void process_drx_Config(NR_UE_sched_ctrl_t *sched_ctrl,NR_SetupRelease_DRX_Config_t *drx_Config) { + if (!drx_Config) return; + AssertFatal(drx_Config->present != NR_SetupRelease_DRX_Config_PR_NOTHING, "Cannot have NR_SetupRelease_DRX_Config_PR_NOTHING\n"); + + if (drx_Config->present == NR_SetupRelease_DRX_Config_PR_setup) { + LOG_I(NR_MAC,"Adding DRX config\n"); + } + else { + LOG_I(NR_MAC,"Removing DRX config\n"); + } +} + +void process_schedulingRequestConfig(NR_UE_sched_ctrl_t *sched_ctrl,NR_SchedulingRequestConfig_t *schedulingRequestConfig) { + if (!schedulingRequestConfig) return; + + LOG_I(NR_MAC,"Adding SchedulingRequestconfig\n"); +} + +void process_bsrConfig(NR_UE_sched_ctrl_t *sched_ctrl,NR_BSR_Config_t *bsr_Config) { + if (!bsr_Config) return; + LOG_I(NR_MAC,"Adding BSR config\n"); +} + +void process_tag_Config(NR_UE_sched_ctrl_t *sched_ctrl,NR_TAG_Config_t *tag_Config) { + if (!tag_Config) return; + LOG_I(NR_MAC,"Adding TAG config\n"); +} + +void process_phr_Config(NR_UE_sched_ctrl_t *sched_ctrl,NR_SetupRelease_PHR_Config_t *phr_Config) { + if (!phr_Config) return; + AssertFatal(phr_Config->present != NR_SetupRelease_PHR_Config_PR_NOTHING, "Cannot have NR_SetupRelease_PHR_Config_PR_NOTHING\n"); + + if (phr_Config->present == NR_SetupRelease_PHR_Config_PR_setup) { + LOG_I(NR_MAC,"Adding PHR config\n"); + } + else { + LOG_I(NR_MAC,"Removing PHR config\n"); + } +} + +void process_CellGroup(NR_CellGroupConfig_t *CellGroup, NR_UE_sched_ctrl_t *sched_ctrl) { + + AssertFatal(CellGroup, "CellGroup is null\n"); + NR_MAC_CellGroupConfig_t *mac_CellGroupConfig = CellGroup->mac_CellGroupConfig; + + + if (mac_CellGroupConfig) { + process_drx_Config(sched_ctrl,mac_CellGroupConfig->drx_Config); + process_schedulingRequestConfig(sched_ctrl,mac_CellGroupConfig->schedulingRequestConfig); + process_bsrConfig(sched_ctrl,mac_CellGroupConfig->bsr_Config); + process_tag_Config(sched_ctrl,mac_CellGroupConfig->tag_Config); + process_phr_Config(sched_ctrl,mac_CellGroupConfig->phr_Config); + } + else { + // apply defaults + + } + + process_rlcBearerConfig(CellGroup->rlc_BearerToAddModList,CellGroup->rlc_BearerToReleaseList,sched_ctrl); + +} void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, NR_ServingCellConfigCommon_t *scc) { nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[Mod_idP]->config[0]; @@ -67,7 +148,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]); cfg->carrier_config.dl_bandwidth.tl.tag = NFAPI_NR_CONFIG_DL_BANDWIDTH_TAG; //temporary cfg->num_tlv++; - LOG_I(PHY,"%s() dl_BandwidthP:%d\n", __FUNCTION__, cfg->carrier_config.dl_bandwidth.value); + LOG_I(NR_MAC,"%s() dl_BandwidthP:%d\n", __FUNCTION__, cfg->carrier_config.dl_bandwidth.value); cfg->carrier_config.dl_frequency.value = from_nrarfcn(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing, @@ -95,7 +176,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts *scc->uplinkConfigCommon->frequencyInfoUL->frequencyBandList->list.array[0]); cfg->carrier_config.uplink_bandwidth.tl.tag = NFAPI_NR_CONFIG_UPLINK_BANDWIDTH_TAG; //temporary cfg->num_tlv++; - LOG_I(PHY,"%s() dl_BandwidthP:%d\n", __FUNCTION__, cfg->carrier_config.uplink_bandwidth.value); + LOG_I(NR_MAC,"%s() dl_BandwidthP:%d\n", __FUNCTION__, cfg->carrier_config.uplink_bandwidth.value); int UL_pointA; if (scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA == NULL) @@ -298,7 +379,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts cfg->carrier_config.num_rx_ant.value = pusch_AntennaPorts; AssertFatal(pusch_AntennaPorts > 0 && pusch_AntennaPorts < 13, "pusch_AntennaPorts in 1...12\n"); cfg->carrier_config.num_rx_ant.tl.tag = NFAPI_NR_CONFIG_NUM_RX_ANT_TAG; - LOG_I(MAC,"Set TX/RX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); + LOG_I(NR_MAC,"Set TX/RX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); AssertFatal(cfg->carrier_config.num_tx_ant.value > 0,"carrier_config.num_tx_ant.value %d !\n",cfg->carrier_config.num_tx_ant.value ); cfg->num_tlv++; cfg->num_tlv++; @@ -315,7 +396,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts cfg->tdd_table.tdd_period.value = *scc->tdd_UL_DL_ConfigurationCommon->pattern1.ext1->dl_UL_TransmissionPeriodicity_v1530; } if(cfg->cell_config.frame_duplex_type.value == TDD){ - LOG_I(MAC,"Setting TDD configuration period to %d\n",cfg->tdd_table.tdd_period.value); + LOG_I(NR_MAC,"Setting TDD configuration period to %d\n",cfg->tdd_table.tdd_period.value); int periods_per_frame = set_tdd_config_nr(cfg, scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots, @@ -324,9 +405,9 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols); if (periods_per_frame < 0) - LOG_E(MAC,"TDD configuration can not be done\n"); + LOG_E(NR_MAC,"TDD configuration can not be done\n"); else { - LOG_I(MAC,"TDD has been properly configurated\n"); + LOG_I(NR_MAC,"TDD has been properly configurated\n"); RC.nrmac[Mod_idP]->tdd_beam_association = (int16_t *)malloc16(periods_per_frame*sizeof(int16_t)); } } @@ -338,12 +419,10 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, - int pusch_tgt_snrx10, - int pucch_tgt_snrx10, NR_ServingCellConfigCommon_t *scc, - int add_ue, - uint32_t rnti, - NR_CellGroupConfig_t *secondaryCellGroup){ + int add_ue, + uint32_t rnti, + NR_CellGroupConfig_t *CellGroup) { if (scc != NULL ) { AssertFatal((scc->ssb_PositionsInBurst->present > 0) && (scc->ssb_PositionsInBurst->present < 4), "SSB Bitmap type %d is not valid\n",scc->ssb_PositionsInBurst->present); @@ -372,14 +451,14 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, AssertFatal(RC.nrmac[Mod_idP]->common_channels[0].vrb_map_UL, "could not allocate memory for RC.nrmac[]->common_channels[0].vrb_map_UL\n"); - LOG_I(MAC,"Configuring common parameters from NR ServingCellConfig\n"); + LOG_I(NR_MAC,"Configuring common parameters from NR ServingCellConfig\n"); config_common(Mod_idP, ssb_SubcarrierOffset, pdsch_AntennaPorts, pusch_AntennaPorts, - scc); - LOG_E(MAC, "%s() %s:%d RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req:%p\n", __FUNCTION__, __FILE__, __LINE__, RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req); + scc); + LOG_D(NR_MAC, "%s() %s:%d RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req:%p\n", __FUNCTION__, __FILE__, __LINE__, RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req); // if in nFAPI mode if ( (NFAPI_MODE == NFAPI_MODE_PNF || NFAPI_MODE == NFAPI_MODE_VNF) && (RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req == NULL) ){ @@ -389,10 +468,8 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, printf("Waiting for PHY_config_req\n"); } } - RC.nrmac[Mod_idP]->ssb_SubcarrierOffset = ssb_SubcarrierOffset; - RC.nrmac[Mod_idP]->pusch_target_snrx10 = pusch_tgt_snrx10; - RC.nrmac[Mod_idP]->pucch_target_snrx10 = pucch_tgt_snrx10; + NR_PHY_Config_t phycfg; phycfg.Mod_id = Mod_idP; phycfg.CC_id = 0; @@ -407,14 +484,14 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, const int nr_slots_period = tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots; const int nr_dlmix_slots = tdd->nrofDownlinkSlots + (tdd->nrofDownlinkSymbols != 0); const int nr_ulstart_slot = tdd->nrofDownlinkSlots + (tdd->nrofUplinkSymbols == 0); + for (int slot = 0; slot < n; ++slot) { /* FIXME: it seems there is a problem with slot 0/10/slots right after UL: * we just get retransmissions. Thus, do not schedule such slots in DL */ if (slot % nr_slots_period != 0) RC.nrmac[Mod_idP]->dlsch_slot_bitmap[slot / 64] |= ((slot % nr_slots_period) < nr_dlmix_slots) << (slot % 64); RC.nrmac[Mod_idP]->ulsch_slot_bitmap[slot / 64] |= ((slot % nr_slots_period) >= nr_ulstart_slot) << (slot % 64); - LOG_D(MAC, - "slot %d DL %d UL %d\n", + LOG_D(NR_MAC, "slot %d DL %d UL %d\n", slot, (RC.nrmac[Mod_idP]->dlsch_slot_bitmap[slot / 64] & (1 << (slot % 64))) != 0, (RC.nrmac[Mod_idP]->ulsch_slot_bitmap[slot / 64] & (1 << (slot % 64))) != 0); @@ -427,32 +504,47 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, RC.nrmac[Mod_idP]->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(Mod_idP, 0); RC.nrmac[Mod_idP]->pre_processor_ul = nr_init_fr1_ulsch_preprocessor(Mod_idP, 0); } + + if (get_softmodem_params()->sa > 0) { + NR_COMMON_channels_t *cc = &RC.nrmac[Mod_idP]->common_channels[0]; + for (int n=0;nra[n].cfra = false; + cc->ra[n].rnti = 0; + cc->ra[n].preambles.num_preambles = MAX_NUM_NR_PRACH_PREAMBLES; + cc->ra[n].preambles.preamble_list = (uint8_t *) malloc(MAX_NUM_NR_PRACH_PREAMBLES*sizeof(uint8_t)); + for (int i = 0; i < MAX_NUM_NR_PRACH_PREAMBLES; i++) + cc->ra[n].preambles.preamble_list[i] = i; + } + } } - if (secondaryCellGroup) { + if (CellGroup) { - RC.nrmac[Mod_idP]->secondaryCellGroupCommon = secondaryCellGroup; + const NR_ServingCellConfig_t *servingCellConfig = CellGroup->spCellConfig->spCellConfigDedicated; - const NR_ServingCellConfig_t *servingCellConfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated; const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig->downlinkBWP_ToAddModList; - AssertFatal(bwpList->list.count > 0, "downlinkBWP_ToAddModList has no BWPs!\n"); - for (int i = 0; i < bwpList->list.count; ++i) { - const NR_BWP_Downlink_t *bwp = bwpList->list.array[i]; - calculate_preferred_dl_tda(Mod_idP, bwp); + if(bwpList) { + AssertFatal(bwpList->list.count > 0, "downlinkBWP_ToAddModList has no BWPs!\n"); + for (int i = 0; i < bwpList->list.count; ++i) { + const NR_BWP_Downlink_t *bwp = bwpList->list.array[i]; + calculate_preferred_dl_tda(Mod_idP, bwp); + } } - const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = - servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList; - AssertFatal(ubwpList->list.count > 0, "downlinkBWP_ToAddModList no BWPs!\n"); - for (int i = 0; i < ubwpList->list.count; ++i) { - const NR_BWP_Uplink_t *ubwp = ubwpList->list.array[i]; - calculate_preferred_ul_tda(Mod_idP, ubwp); + const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList; + if(ubwpList) { + AssertFatal(ubwpList->list.count > 0, "downlinkBWP_ToAddModList no BWPs!\n"); + for (int i = 0; i < ubwpList->list.count; ++i) { + const NR_BWP_Uplink_t *ubwp = ubwpList->list.array[i]; + calculate_preferred_ul_tda(Mod_idP, ubwp); + } } NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; if (add_ue == 1 && get_softmodem_params()->phy_test) { - const int UE_id = add_new_nr_ue(Mod_idP, rnti, secondaryCellGroup); - LOG_I(PHY,"Added new UE_id %d/%x with initial secondaryCellGroup\n",UE_id,rnti); + const int UE_id = add_new_nr_ue(Mod_idP, rnti, CellGroup); + LOG_I(NR_MAC,"Added new UE_id %d/%x with initial CellGroup\n",UE_id,rnti); + process_CellGroup(CellGroup,&UE_info->UE_sched_ctrl[UE_id]); } else if (add_ue == 1 && !get_softmodem_params()->phy_test) { const int CC_id = 0; NR_COMMON_channels_t *cc = &RC.nrmac[Mod_idP]->common_channels[CC_id]; @@ -462,16 +554,17 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, if((cc->ra[ra_index].state == RA_IDLE) && (!cc->ra[ra_index].cfra)) break; } if (ra_index == NR_NB_RA_PROC_MAX) { - LOG_E(MAC, "%s() %s:%d RA processes are not available for CFRA RNTI :%x\n", __FUNCTION__, __FILE__, __LINE__, rnti); + LOG_E(NR_MAC, "%s() %s:%d RA processes are not available for CFRA RNTI :%x\n", __FUNCTION__, __FILE__, __LINE__, rnti); return -1; } NR_RA_t *ra = &cc->ra[ra_index]; - ra->secondaryCellGroup = secondaryCellGroup; - if (secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated!=NULL) { - if (secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra != NULL) { + ra->CellGroup = CellGroup; + if (CellGroup->spCellConfig && CellGroup->spCellConfig->reconfigurationWithSync && + CellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated!=NULL) { + if (CellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra != NULL) { ra->cfra = true; ra->rnti = rnti; - struct NR_CFRA *cfra = secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra; + struct NR_CFRA *cfra = CellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra; uint8_t num_preamble = cfra->resources.choice.ssb->ssb_ResourceList.list.count; ra->preambles.num_preambles = num_preamble; ra->preambles.preamble_list = (uint8_t *) malloc(num_preamble*sizeof(uint8_t)); @@ -494,11 +587,12 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, for (int i = 0; i < MAX_NUM_NR_PRACH_PREAMBLES; i++) ra->preambles.preamble_list[i] = i; } - LOG_I(PHY,"Added new RA process for UE RNTI %04x with initial secondaryCellGroup\n", rnti); - } else { // secondaryCellGroup has been updated + LOG_I(NR_MAC,"Added new RA process for UE RNTI %04x with initial CellGroup\n", rnti); + } else { // CellGroup has been updated const int UE_id = find_nr_UE_id(Mod_idP,rnti); - UE_info->secondaryCellGroup[UE_id] = secondaryCellGroup; - LOG_I(PHY,"Modified UE_id %d/%x with secondaryCellGroup\n",UE_id,rnti); + UE_info->CellGroup[UE_id] = CellGroup; + LOG_I(NR_MAC,"Modified UE_id %d/%x with CellGroup\n",UE_id,rnti); + process_CellGroup(CellGroup,&UE_info->UE_sched_ctrl[UE_id]); } } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_MAC_CONFIG, VCD_FUNCTION_OUT); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index f655d3d65e..fc2daeac05 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -66,7 +66,7 @@ void dump_mac_stats(gNB_MAC_INST *gNB) NR_UE_info_t *UE_info = &gNB->UE_info; int num = 1; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) { - LOG_I(MAC, "UE ID %d RNTI %04x (%d/%d) PH %d dB PCMAX %d dBm\n", + LOG_I(NR_MAC, "UE ID %d RNTI %04x (%d/%d) PH %d dB PCMAX %d dBm\n", UE_id, UE_info->rnti[UE_id], num++, @@ -75,32 +75,29 @@ void dump_mac_stats(gNB_MAC_INST *gNB) UE_info->UE_sched_ctrl[UE_id].pcmax); NR_mac_stats_t *stats = &UE_info->mac_stats[UE_id]; const int avg_rsrp = stats->num_rsrp_meas > 0 ? stats->cumul_rsrp / stats->num_rsrp_meas : 0; - LOG_I(MAC, "UE %d: dlsch_rounds %d/%d/%d/%d, dlsch_errors %d, average RSRP %d (%d meas)\n", + LOG_I(NR_MAC, "UE %d: dlsch_rounds %d/%d/%d/%d, dlsch_errors %d, average RSRP %d (%d meas)\n", UE_id, stats->dlsch_rounds[0], stats->dlsch_rounds[1], stats->dlsch_rounds[2], stats->dlsch_rounds[3], stats->dlsch_errors, avg_rsrp, stats->num_rsrp_meas); stats->num_rsrp_meas = 0; stats->cumul_rsrp = 0 ; - LOG_I(MAC, "UE %d: dlsch_total_bytes %d\n", UE_id, stats->dlsch_total_bytes); - const NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - LOG_I(MAC, "UE %d: ulsch_rounds %d/%d/%d/%d, ulsch_errors %d, PUSCH SNR %2.1f dB, PUCCH SNR %2.1f dB, noise rssi %2.1f dB\n", + LOG_I(NR_MAC, "UE %d: dlsch_total_bytes %d\n", UE_id, stats->dlsch_total_bytes); + LOG_I(NR_MAC, "UE %d: ulsch_rounds %d/%d/%d/%d, ulsch_DTX %d, ulsch_errors %d\n", UE_id, stats->ulsch_rounds[0], stats->ulsch_rounds[1], stats->ulsch_rounds[2], stats->ulsch_rounds[3], - stats->ulsch_errors, - (float) sched_ctrl->pusch_snrx10 / 10, - (float) sched_ctrl->pucch_snrx10 / 10, - (float) (sched_ctrl->raw_rssi - 1280) / 10); - LOG_I(MAC, + stats->ulsch_DTX, + stats->ulsch_errors); + LOG_I(NR_MAC, "UE %d: ulsch_total_bytes_scheduled %d, ulsch_total_bytes_received %d\n", UE_id, stats->ulsch_total_bytes_scheduled, stats->ulsch_total_bytes_rx); for (int lc_id = 0; lc_id < 63; lc_id++) { if (stats->lc_bytes_tx[lc_id] > 0) - LOG_I(MAC, "UE %d: LCID %d: %d bytes TX\n", UE_id, lc_id, stats->lc_bytes_tx[lc_id]); + LOG_D(NR_MAC, "UE %d: LCID %d: %d bytes TX\n", UE_id, lc_id, stats->lc_bytes_tx[lc_id]); if (stats->lc_bytes_rx[lc_id] > 0) - LOG_I(MAC, "UE %d: LCID %d: %d bytes RX\n", UE_id, lc_id, stats->lc_bytes_rx[lc_id]); + LOG_D(NR_MAC, "UE %d: LCID %d: %d bytes RX\n", UE_id, lc_id, stats->lc_bytes_rx[lc_id]); } } print_meas(&gNB->eNB_scheduler, "DL & UL scheduling timing stats", NULL, NULL); @@ -322,14 +319,14 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, sub_frame_t slot){ - protocol_ctxt_t ctxt; + protocol_ctxt_t ctxt={0}; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - + + const int bwp_id = 1; gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; - const int bwp_id = *gNB->secondaryCellGroupCommon->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; if (slot==0 && (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]>=257)) { const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; @@ -355,8 +352,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing); } - memset(RC.nrmac[module_idP]->cce_list[bwp_id][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - memset(RC.nrmac[module_idP]->cce_list[bwp_id][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1 + memset(RC.nrmac[module_idP]->cce_list[0][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 + for (int i_bwp=1 ; i_bwp < MAX_NUM_BWP; i_bwp++) + memset(RC.nrmac[module_idP]->cce_list[i_bwp][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1-4 NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) for (int i=0; iUL_tti_req_ahead[0][slotP]; nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0]; - if (is_nr_UL_slot(scc, slotP, cc->frame_type)) { + if (is_nr_UL_slot(scc->tdd_UL_DL_ConfigurationCommon, slotP, cc->frame_type)) { uint8_t config_index = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.prach_ConfigurationIndex; uint8_t mu,N_dur,N_t_slot,start_symbol = 0,N_RA_slot; @@ -539,10 +539,13 @@ void nr_initiate_ra_proc(module_id_t module_idP, else ra_rnti = 1 + symbol + (slotP * 14) + (freq_index * 14 * 80) + (ul_carrier_id * 14 * 80 * 8); - ra->bwp_id = 1; - NR_BWP_Downlink_t *bwp = NULL; // For SA we use InitialBWP - if(get_softmodem_params()->sa != 1) { - bwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + // This should be handled differently when we use the initialBWP for RA + ra->bwp_id = 0; + NR_BWP_Downlink_t *bwp=NULL; + if (ra->CellGroup && ra->CellGroup->spCellConfig && ra->CellGroup->spCellConfig->spCellConfigDedicated && + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { + ra->bwp_id = 1; + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 1); @@ -585,13 +588,15 @@ void nr_initiate_ra_proc(module_id_t module_idP, } AssertFatal(commonSearchSpaceList->list.count > 0, "common SearchSpace list has 0 elements\n"); - // Common searchspace list + // Common SearchSpace list for (int i = 0; i < commonSearchSpaceList->list.count; i++) { ss = commonSearchSpaceList->list.array[i]; if (ss->searchSpaceId == ra_SearchSpace) ra->ra_ss = ss; } + AssertFatal(ra->ra_ss!=NULL,"SearchSpace cannot be null for RA\n"); + // retrieving ra pdcch monitoring period and offset find_monitoring_periodicity_offset_common(ra->ra_ss, &monitoring_slot_period, &monitoring_offset); @@ -612,8 +617,7 @@ void nr_initiate_ra_proc(module_id_t module_idP, LOG_D(NR_MAC, "%s() Msg2[%04d%d] SFN/SF:%04d%d\n", __FUNCTION__, ra->Msg2_frame, ra->Msg2_slot, frameP, slotP); int loop = 0; - // This condition allows for the usage of a preconfigured rnti for the first UE - if (!ra->cfra || ra->rnti == 0) { + if (ra->rnti == 0) { // This condition allows for the usage of a preconfigured rnti for the CFRA do { ra->rnti = (taus() % 65518) + 1; loop++; @@ -690,12 +694,18 @@ void nr_get_Msg3alloc(module_id_t module_id, uint16_t msg3_nb_rb = 8 + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); // sdu has 6 or 8 bytes - int mu = ubwp->bwp_Common->genericParameters.subcarrierSpacing; + int mu = ubwp ? + ubwp->bwp_Common->genericParameters.subcarrierSpacing : + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; int StartSymbolIndex, NrOfSymbols, startSymbolAndLength, temp_slot; ra->Msg3_tda_id = 16; // initialization to a value above limit - for (int i=0; ibwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.count; i++) { - startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; + NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList= ubwp ? + ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList: + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + + for (int i=0; ilist.count; i++) { + startSymbolAndLength = pusch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; SLIV2SL(startSymbolAndLength, &StartSymbolIndex, &NrOfSymbols); // we want to transmit in the uplink symbols of mixed slot if (NrOfSymbols == scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols) { @@ -705,7 +715,7 @@ void nr_get_Msg3alloc(module_id_t module_id, } AssertFatal(ra->Msg3_tda_id<16,"Unable to find Msg3 time domain allocation in list\n"); - uint8_t k2 = *ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->k2; + uint8_t k2 = *pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->k2; temp_slot = current_slot + k2 + DELTA[mu]; // msg3 slot according to 8.3 in 38.213 ra->Msg3_slot = temp_slot%nr_slots_per_frame[mu]; @@ -729,7 +739,10 @@ void nr_get_Msg3alloc(module_id_t module_id, LOG_D(NR_MAC, "[RAPROC] Msg3 slot %d: current slot %u Msg3 frame %u k2 %u Msg3_tda_id %u start symbol index %u\n", ra->Msg3_slot, current_slot, ra->Msg3_frame, k2,ra->Msg3_tda_id, StartSymbolIndex); uint16_t *vrb_map_UL = &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[ra->Msg3_slot * MAX_BWP_SIZE]; - const uint16_t bwpSize = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + const uint16_t bwpSize = NRRIV2BW(ubwp ? + ubwp->bwp_Common->genericParameters.locationAndBandwidth : + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, + MAX_BWP_SIZE); /* search msg3_nb_rb free RBs */ int rbSize = 0; @@ -786,12 +799,28 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t nfapi_nr_pusch_pdu_t *pusch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pusch_pdu; memset(pusch_pdu, 0, sizeof(nfapi_nr_pusch_pdu_t)); future_ul_tti_req->n_pdus += 1; + int ibwp_size = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + int ibwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + int abwp_size = ibwp_size; + int abwp_start = ibwp_start; + int scs = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; + int fh = 0; + int startSymbolAndLength = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->startSymbolAndLength; + int mappingtype = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->mappingType; + + if (ra->CellGroup) { + AssertFatal(ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count <= 4, + "downlinkBWP_ToAddModList has %d BWP!\n", ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); + NR_BWP_Uplink_t *ubwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; + + startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->startSymbolAndLength; + mappingtype = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->mappingType; + abwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + abwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + scs = ubwp->bwp_Common->genericParameters.subcarrierSpacing; + fh = ubwp->bwp_Dedicated->pusch_Config->choice.setup->frequencyHopping ? 1 : 0; + } - AssertFatal(ra->secondaryCellGroup, - "no secondaryCellGroup for RNTI %04x\n", - ra->crnti); - - NR_BWP_Uplink_t *ubwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; LOG_D(NR_MAC, "Frame %d, Subframe %d Adding Msg3 UL Config Request for (%d,%d) : (%d,%d,%d) for rnti: %d\n", frameP, slotP, @@ -802,24 +831,19 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t ra->msg3_round, ra->rnti); - int startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->startSymbolAndLength; int start_symbol_index,nr_of_symbols; SLIV2SL(startSymbolAndLength, &start_symbol_index, &nr_of_symbols); - int mappingtype = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->mappingType; pusch_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; pusch_pdu->rnti = ra->rnti; pusch_pdu->handle = 0; - int abwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - int abwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - int ibwp_size = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - int ibwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if ((ibwp_start < abwp_start) || (ibwp_size > abwp_size)) pusch_pdu->bwp_start = abwp_start; else pusch_pdu->bwp_start = ibwp_start; pusch_pdu->bwp_size = ibwp_size; - pusch_pdu->subcarrier_spacing = ubwp->bwp_Common->genericParameters.subcarrierSpacing; + pusch_pdu->subcarrier_spacing = scs; pusch_pdu->cyclic_prefix = 0; pusch_pdu->mcs_index = 0; pusch_pdu->mcs_table = 0; @@ -848,10 +872,8 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t else pusch_pdu->rb_size = ra->msg3_nb_rb; pusch_pdu->vrb_to_prb_mapping = 0; - if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->frequencyHopping == NULL) - pusch_pdu->frequency_hopping = 0; - else - pusch_pdu->frequency_hopping = 1; + + pusch_pdu->frequency_hopping = fh; //pusch_pdu->tx_direct_current_location;//The uplink Tx Direct Current location for the carrier. Only values in the value range of this field between 0 and 3299, which indicate the subcarrier index within the carrier corresponding 1o the numerology of the corresponding uplink BWP and value 3300, which indicates "Outside the carrier" and value 3301, which indicates "Undetermined position within the carrier" are used. [TS38.331, UplinkTxDirectCurrentBWP IE] pusch_pdu->uplink_frequency_shift_7p5khz = 0; //Resource Allocation in time domain @@ -893,24 +915,29 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra long BWPSize = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - NR_BWP_Downlink_t *bwp = NULL; // For SA we use InitialBWP - if(get_softmodem_params()->sa != 1) { - bwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; - } - + NR_BWP_Downlink_t *bwp = NULL; NR_ControlResourceSet_t *coreset = NULL; - if(bwp) { - coreset = get_coreset(bwp, ss, 0); - } else { - coreset = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonControlResourceSet; - } - + NR_BWP_t *genericParameters = NULL; + NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList=NULL; long BWPStart = 0; - if (get_softmodem_params()->sa != 1) { - BWPStart = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - } else { - BWPStart = NRRIV2PRBOFFSET(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + + if (ra->CellGroup && + ra->CellGroup->spCellConfig && + ra->CellGroup->spCellConfig->spCellConfigDedicated && + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]) { + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + genericParameters = &bwp->bwp_Common->genericParameters; + pdsch_TimeDomainAllocationList = bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; } + else { + genericParameters= &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + pdsch_TimeDomainAllocationList = scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + } + BWPStart = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + coreset = get_coreset(scc,bwp, ss, NR_SearchSpace__searchSpaceType_PR_common); + + AssertFatal(coreset!=NULL,"Coreset cannot be null for RA-Msg2\n"); uint16_t *vrb_map = cc[CC_id].vrb_map; for (int i = 0; (i < rbSize) && (rbStart <= (BWPSize - rbSize)); i++) { @@ -943,25 +970,19 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // Calculate number of symbols int startSymbolIndex, nrOfSymbols; - const int startSymbolAndLength = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated-> - downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup-> - pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->startSymbolAndLength; + const int startSymbolAndLength = pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->startSymbolAndLength; SLIV2SL(startSymbolAndLength, &startSymbolIndex, &nrOfSymbols); AssertFatal(startSymbolIndex >= 0, "StartSymbolIndex is negative\n"); - int mappingtype = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated-> - downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup-> - pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->mappingType; + LOG_D(MAC,"Msg2 startSymbolIndex.nrOfSymbols %d.%d\n",startSymbolIndex,nrOfSymbols); - uint8_t numDmrsCdmGrpsNoData = 2; - if (nrOfSymbols == 2) { - numDmrsCdmGrpsNoData = 1; - } + int mappingtype = pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->mappingType; // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them + const int bwpid = bwp ? bwp->bwp_Id : 0; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -970,7 +991,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -983,32 +1004,39 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra LOG_I(NR_MAC,"[gNB %d][RAPROC] CC_id %d Frame %d, slotP %d: Generating RA-Msg2 DCI, rnti 0x%x, state %d\n", module_idP, CC_id, frameP, slotP, ra->RA_rnti, ra->state); - AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->RA_rnti); - // SCF222: PDU index incremented for each PDSCH PDU sent in TX control message. This is used to associate control // information to data and is reset every slot. const int pduindex = nr_mac->pdu_index[CC_id]++; uint8_t mcsTableIdx = 0; - if (bwp==NULL || bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table == NULL) - mcsTableIdx = 0; - else{ + if (bwp && + bwp->bwp_Dedicated && + bwp->bwp_Dedicated->pdsch_Config && + bwp->bwp_Dedicated->pdsch_Config->choice.setup && + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table) { if (*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table == 0) mcsTableIdx = 1; else mcsTableIdx = 2; } + else mcsTableIdx = 0; - AssertFatal(numDmrsCdmGrpsNoData == 1 || numDmrsCdmGrpsNoData == 2, - "nr_mac->schedCtrlCommon->numDmrsCdmGrpsNoData %d is not possible", - numDmrsCdmGrpsNoData); + int dmrsConfigType=0; + if (bwp && + bwp->bwp_Dedicated && + bwp->bwp_Dedicated->pdsch_Config && + bwp->bwp_Dedicated->pdsch_Config->choice.setup && + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA && + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup && + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type) + dmrsConfigType = 1; pdsch_pdu_rel15->pduBitmap = 0; pdsch_pdu_rel15->rnti = ra->RA_rnti; pdsch_pdu_rel15->pduIndex = pduindex; pdsch_pdu_rel15->BWPSize = BWPSize; pdsch_pdu_rel15->BWPStart = BWPStart; - pdsch_pdu_rel15->SubcarrierSpacing = bwp!=NULL ? bwp->bwp_Common->genericParameters.subcarrierSpacing : scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing; + pdsch_pdu_rel15->SubcarrierSpacing = genericParameters->subcarrierSpacing; pdsch_pdu_rel15->CyclicPrefix = 0; pdsch_pdu_rel15->NrOfCodewords = 1; pdsch_pdu_rel15->targetCodeRate[0] = nr_get_code_rate_dl(mcsIndex,mcsTableIdx); @@ -1020,10 +1048,10 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra pdsch_pdu_rel15->nrOfLayers = 1; pdsch_pdu_rel15->transmissionScheme = 0; pdsch_pdu_rel15->refPoint = 0; - pdsch_pdu_rel15->dmrsConfigType = bwp!=NULL ? (bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; + pdsch_pdu_rel15->dmrsConfigType = dmrsConfigType; pdsch_pdu_rel15->dlDmrsScramblingId = *scc->physCellId; pdsch_pdu_rel15->SCID = 0; - pdsch_pdu_rel15->numDmrsCdmGrpsNoData = numDmrsCdmGrpsNoData; + pdsch_pdu_rel15->numDmrsCdmGrpsNoData = nrOfSymbols <= 2 ? 1 : 2; pdsch_pdu_rel15->dmrsPorts = 1; pdsch_pdu_rel15->resourceAlloc = 1; pdsch_pdu_rel15->rbStart = rbStart; @@ -1083,19 +1111,21 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra pdcch_pdu_rel15->DurationSymbols); fill_dci_pdu_rel15(scc, - nr_mac->secondaryCellGroupCommon, + ra->CellGroup, &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci - 1], &dci_payload, NR_DL_DCI_FORMAT_1_0, NR_RNTI_RA, pdsch_pdu_rel15->BWPSize, - ra->bwp_id); + bwpid); // DL TX request nfapi_nr_pdu_t *tx_req = &nr_mac->TX_req[CC_id].pdu_list[nr_mac->TX_req[CC_id].Number_of_PDUs]; // Program UL processing for Msg3 - NR_BWP_Uplink_t *ubwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + NR_BWP_Uplink_t *ubwp = ra->CellGroup ? + ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id-1] : + NULL; nr_get_Msg3alloc(module_idP, CC_id, scc, ubwp, slotP, frameP, ra, nr_mac->tdd_beam_association); nr_add_msg3(module_idP, CC_id, frameP, slotP, ra, (uint8_t *) &tx_req->TLVs[0].value.direct[0]); @@ -1137,24 +1167,34 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; NR_SearchSpace_t *ss = ra->ra_ss; - NR_BWP_Downlink_t *bwp = NULL; // For SA we use InitialBWP - if(get_softmodem_params()->sa != 1) { - bwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; - } - + NR_BWP_Downlink_t *bwp = NULL; NR_ControlResourceSet_t *coreset = NULL; - if(bwp) { - coreset = get_coreset(bwp, ss, 0); - } else { - coreset = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonControlResourceSet; + NR_BWP_t *genericParameters = NULL; + NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList=NULL; + + if (ra->CellGroup && + ra->CellGroup->spCellConfig && + ra->CellGroup->spCellConfig->spCellConfigDedicated && + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]) { + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + genericParameters = &bwp->bwp_Common->genericParameters; + pdsch_TimeDomainAllocationList = bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; } + else { + genericParameters= &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + pdsch_TimeDomainAllocationList = scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + } + coreset = get_coreset(scc,bwp, ss, NR_SearchSpace__searchSpaceType_PR_common); + + AssertFatal(coreset!=NULL,"Coreset cannot be null for RA-Msg4\n"); int UE_id = find_nr_UE_id(module_idP, ra->rnti); NR_UE_info_t *UE_info = &nr_mac->UE_info; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - long BWPSize = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - long BWPStart = NRRIV2PRBOFFSET(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + long BWPSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + long BWPStart = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); // HARQ management AssertFatal(sched_ctrl->available_dl_harq.head >= 0, @@ -1167,7 +1207,27 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra harq->is_waiting = true; ra->harq_pid = current_harq_pid; - int alloc = nr_acknack_scheduling(module_idP, UE_id, frameP, slotP); + // get CCEindex, needed also for PUCCH and then later for PDCCH + uint8_t aggregation_level; + uint8_t nr_of_candidates; + find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss); + int CCEIndex = allocate_nr_CCEs(nr_mac, bwp, coreset, aggregation_level,0,0,nr_of_candidates); + if (CCEIndex < 0) { + LOG_E(NR_MAC, "%s(): cannot find free CCE for RA RNTI %04x!\n", __func__, ra->rnti); + return; + } + + int n_rb=0; + for (int i=0;i<6;i++) + for (int j=0;j<8;j++) { + n_rb+=((coreset->frequencyDomainResources.buf[i]>>j)&1); + } + n_rb*=6; + const uint16_t N_cce = n_rb * coreset->duration / NR_NB_REG_PER_CCE; + const int delta_PRI=0; + int r_pucch = ((CCEIndex<<1)/N_cce)+(delta_PRI<<1); + + int alloc = nr_acknack_scheduling(module_idP, UE_id, frameP, slotP, r_pucch); AssertFatal(alloc>=0,"Couldn't find a pucch allocation for ack nack (msg4)\n"); NR_sched_pucch_t *pucch = &sched_ctrl->sched_pucch[alloc]; harq->feedback_slot = pucch->ul_slot; @@ -1184,22 +1244,18 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->L = mac_sdu_length; mac_pdu_length = mac_pdu_length + mac_sdu_length + sizeof(NR_MAC_SUBHEADER_SHORT); - LOG_D(NR_MAC,"Encoded RRCSetup Piggyback (%d + 2 bytes), mac_pdu_length %d\n",mac_sdu_length,mac_pdu_length); + LOG_D(NR_MAC,"Encoded RRCSetup Piggyback (%d + %d bytes), mac_pdu_length %d\n", mac_sdu_length, (int)sizeof(NR_MAC_SUBHEADER_SHORT), mac_pdu_length); // Calculate number of symbols int startSymbolIndex, nrOfSymbols; - const int startSymbolAndLength = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated-> - downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup-> - pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->startSymbolAndLength; + const int startSymbolAndLength = pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->startSymbolAndLength; SLIV2SL(startSymbolAndLength, &startSymbolIndex, &nrOfSymbols); AssertFatal(startSymbolIndex >= 0, "StartSymbolIndex is negative\n"); - int mappingtype = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated-> - downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup-> - pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->mappingType; + int mappingtype = pdsch_TimeDomainAllocationList->list.array[time_domain_assignment]->mappingType; uint16_t dlDmrsSymbPos = fill_dmrs_mask(NULL, - nr_mac->common_channels->ServingCellConfigCommon->dmrs_TypeA_Position, + scc->dmrs_TypeA_Position, nrOfSymbols, startSymbolIndex, mappingtype); @@ -1227,14 +1283,17 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra } uint8_t mcsTableIdx = 0; - if (bwp==NULL || bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table == NULL) - mcsTableIdx = 0; - else{ + if (bwp && + bwp->bwp_Dedicated && + bwp->bwp_Dedicated->pdsch_Config && + bwp->bwp_Dedicated->pdsch_Config->choice.setup && + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table) { if (*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table == 0) mcsTableIdx = 1; else mcsTableIdx = 2; } + else mcsTableIdx = 0; int rbStart = 0; int rbSize = 0; @@ -1267,19 +1326,12 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra return; } - uint8_t aggregation_level; - uint8_t nr_of_candidates; - find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss); - int CCEIndex = allocate_nr_CCEs(nr_mac, bwp, coreset, aggregation_level,0,0,nr_of_candidates); - if (CCEIndex < 0) { - LOG_E(NR_MAC, "%s(): cannot find free CCE for RA RNTI %04x!\n", __func__, ra->rnti); - return; - } // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them + const int bwpid = bwp ? bwp->bwp_Id : 0; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -1288,7 +1340,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1300,12 +1352,6 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra LOG_I(NR_MAC,"[gNB %d] [RAPROC] CC_id %d Frame %d, slotP %d: Generating RA-Msg4 DCI, state %d\n", module_idP, CC_id, frameP, slotP, ra->state); - AssertFatal(ra->secondaryCellGroup, "no secondaryCellGroup for RNTI %04x\n", ra->rnti); - - AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", - ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); - // SCF222: PDU index incremented for each PDSCH PDU sent in TX control message. This is used to associate control // information to data and is reset every slot. const int pduindex = nr_mac->pdu_index[CC_id]++; @@ -1315,7 +1361,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra pdsch_pdu_rel15->pduIndex = pduindex; pdsch_pdu_rel15->BWPSize = BWPSize; pdsch_pdu_rel15->BWPStart = BWPStart; - pdsch_pdu_rel15->SubcarrierSpacing = bwp!=NULL ? bwp->bwp_Common->genericParameters.subcarrierSpacing : scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing; + pdsch_pdu_rel15->SubcarrierSpacing = genericParameters->subcarrierSpacing; pdsch_pdu_rel15->CyclicPrefix = 0; pdsch_pdu_rel15->NrOfCodewords = 1; pdsch_pdu_rel15->targetCodeRate[0] = nr_get_code_rate_dl(mcsIndex,mcsTableIdx); @@ -1330,7 +1376,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra pdsch_pdu_rel15->dmrsConfigType = dmrsConfigType; pdsch_pdu_rel15->dlDmrsScramblingId = *scc->physCellId; pdsch_pdu_rel15->SCID = 0; - pdsch_pdu_rel15->numDmrsCdmGrpsNoData = 2; + pdsch_pdu_rel15->numDmrsCdmGrpsNoData = nrOfSymbols <= 2 ? 1 : 2; pdsch_pdu_rel15->dmrsPorts = 1; pdsch_pdu_rel15->resourceAlloc = 1; pdsch_pdu_rel15->rbStart = rbStart; @@ -1375,11 +1421,11 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.ndi = harq->ndi; dci_payload.dai[0].val = (pucch->dai_c-1)&3; dci_payload.tpc = sched_ctrl->tpc1; // TPC for PUCCH: table 7.2.1-1 in 38.213 - dci_payload.pucch_resource_indicator = pucch->resource_indicator; + dci_payload.pucch_resource_indicator = delta_PRI; // This is delta_PRI from 9.2.1 in 38.213 dci_payload.pdsch_to_harq_feedback_timing_indicator.val = pucch->timing_indicator; LOG_D(NR_MAC, - "[RAPROC] DCI type 1 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d \n", + "[RAPROC] DCI type 1 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d pucchres %d harqtiming %d\n", dci_payload.frequency_domain_assignment.val, pdsch_pdu_rel15->rbStart, pdsch_pdu_rel15->rbSize, @@ -1387,7 +1433,9 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dci_payload.time_domain_assignment.val, dci_payload.vrb_to_prb_mapping.val, dci_payload.mcs, - dci_payload.tb_scaling); + dci_payload.tb_scaling, + dci_payload.pucch_resource_indicator, + dci_payload.pdsch_to_harq_feedback_timing_indicator.val); LOG_D(NR_MAC, "[RAPROC] DCI params: rnti 0x%x, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", @@ -1399,13 +1447,13 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra pdcch_pdu_rel15->DurationSymbols); fill_dci_pdu_rel15(scc, - nr_mac->secondaryCellGroupCommon, + ra->CellGroup, &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci - 1], &dci_payload, NR_DL_DCI_FORMAT_1_0, NR_RNTI_TC, pdsch_pdu_rel15->BWPSize, - ra->bwp_id); + bwpid); // Add padding header and zero rest out if there is space left if (mac_pdu_length < harq->tb_size) { @@ -1470,6 +1518,7 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram LOG_I(NR_MAC, "(ue %i, rnti 0x%04x) Received Ack of RA-Msg4. CBRA procedure succeeded!\n", UE_id, ra->rnti); nr_clear_ra_proc(module_id, CC_id, frame, ra); UE_info->active[UE_id] = true; + UE_info->Msg4_ACKed[UE_id] = true; } else { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index f93ad92a13..8b12742fcc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -139,8 +139,7 @@ void schedule_ssb(frame_t frame, sub_frame_t slot, } -void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP) -{ +void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP) { gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc; nfapi_nr_dl_tti_request_t *dl_tti_request; @@ -380,14 +379,13 @@ void schedule_control_sib1(module_id_t module_id, uint16_t dlDmrsSymbPos = fill_dmrs_mask(NULL, gNB_mac->common_channels->ServingCellConfigCommon->dmrs_TypeA_Position, nrOfSymbols, startSymbolIndex, mappingtype); uint16_t dmrs_length = get_num_dmrs(dlDmrsSymbPos); + LOG_D(MAC,"dlDmrsSymbPos %x\n",dlDmrsSymbPos); int rbSize = 0; uint32_t TBS = 0; do { rbSize++; - TBS = nr_compute_tbs(nr_get_Qm_dl(gNB_mac->sched_ctrlCommon->sched_pdsch.mcs, - gNB_mac->sched_ctrlCommon->pdsch_semi_static.mcsTableIdx), - nr_get_code_rate_dl(gNB_mac->sched_ctrlCommon->sched_pdsch.mcs, - gNB_mac->sched_ctrlCommon->pdsch_semi_static.mcsTableIdx), + TBS = nr_compute_tbs(nr_get_Qm_dl(gNB_mac->sched_ctrlCommon->sched_pdsch.mcs, gNB_mac->sched_ctrlCommon->pdsch_semi_static.mcsTableIdx), + nr_get_code_rate_dl(gNB_mac->sched_ctrlCommon->sched_pdsch.mcs, gNB_mac->sched_ctrlCommon->pdsch_semi_static.mcsTableIdx), rbSize, nrOfSymbols, N_PRB_DMRS * dmrs_length,0, 0,1) >> 3; } while (rbStart + rbSize < bwpSize && !vrb_map[rbStart + rbSize] && TBS < gNB_mac->sched_ctrlCommon->num_total_bytes); @@ -398,6 +396,8 @@ void schedule_control_sib1(module_id_t module_id, LOG_D(MAC,"nrOfSymbols = %i\n", nrOfSymbols); LOG_D(MAC, "rbSize = %i\n", gNB_mac->sched_ctrlCommon->sched_pdsch.rbSize); LOG_D(MAC,"TBS = %i\n", TBS); + LOG_D(MAC,"dmrs_length %d\n",dmrs_length); + LOG_D(MAC,"N_PRB_DMRS = %d\n",N_PRB_DMRS); LOG_D(MAC,"mappingtype = %d\n", mappingtype); // Mark the corresponding RBs as used @@ -417,7 +417,6 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP, gNB_MAC_INST *gNB_mac = RC.nrmac[Mod_idP]; NR_COMMON_channels_t *cc = gNB_mac->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; - NR_CellGroupConfig_t *secondaryCellGroup = gNB_mac->secondaryCellGroupCommon; NR_BWP_Downlink_t *bwp = gNB_mac->sched_ctrlCommon->active_bwp; nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -524,7 +523,7 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP, int rnti_type = NR_RNTI_SI; fill_dci_pdu_rel15(scc, - secondaryCellGroup, + NULL, &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci - 1], &dci_payload, dci_format, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index b43c9e2e2c..47614ed8c0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -55,11 +55,12 @@ #define HALFWORD 16 #define WORD 32 //#define SIZE_OF_POINTER sizeof (void *) +static int loop_dcch_dtch = DL_SCH_LCID_DTCH; void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t *bwp) { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; - const int bwp_id = bwp->bwp_Id; + const int bwp_id = bwp ? bwp->bwp_Id : 0; if (nrmac->preferred_dl_tda[bwp_id]) return; @@ -69,9 +70,10 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL; const int symb_dlMixed = tdd ? (1 << tdd->nrofDownlinkSymbols) - 1 : 0; - const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; - const NR_SearchSpace_t *search_space = get_searchspace(bwp, target_ss); - const NR_ControlResourceSet_t *coreset = get_coreset(bwp, search_space, 1 /* dedicated */); + const int target_ss = bwp ? NR_SearchSpace__searchSpaceType_PR_ue_Specific : NR_SearchSpace__searchSpaceType_PR_common; + NR_SearchSpace_t *search_space = get_searchspace(scc, bwp ? bwp->bwp_Dedicated : NULL, target_ss); + const NR_ControlResourceSet_t *coreset = get_coreset(scc, (NR_BWP_Downlink_t*)bwp, search_space, target_ss); + // get coreset symbol "map" const uint16_t symb_coreset = (1 << coreset->duration) - 1; @@ -176,7 +178,7 @@ int nr_write_ce_dlsch_pdu(module_id_t module_idP, ((NR_MAC_CE_TA *) ce_ptr)->TAGID = tag_id; } - LOG_D(MAC, "NR MAC CE timing advance command = %d (%d) TAG ID = %d\n", timing_advance_cmd, ((NR_MAC_CE_TA *) ce_ptr)->TA_COMMAND, tag_id); + LOG_D(NR_MAC, "NR MAC CE timing advance command = %d (%d) TAG ID = %d\n", timing_advance_cmd, ((NR_MAC_CE_TA *) ce_ptr)->TA_COMMAND, tag_id); mac_ce_size = sizeof(NR_MAC_CE_TA); // Copying bytes for MAC CEs to the mac pdu pointer memcpy((void *) mac_pdu_ptr, (void *) ce_ptr, mac_ce_size); @@ -195,7 +197,7 @@ int nr_write_ce_dlsch_pdu(module_id_t module_idP, // contention resolution identity MAC ce has a fixed 48 bit size // this contains the UL CCCH SDU. If UL CCCH SDU is longer than 48 bits, // it contains the first 48 bits of the UL CCCH SDU - LOG_T(MAC, "[gNB ][RAPROC] Generate contention resolution msg: %x.%x.%x.%x.%x.%x\n", + LOG_T(NR_MAC, "[gNB ][RAPROC] Generate contention resolution msg: %x.%x.%x.%x.%x.%x\n", ue_cont_res_id[0], ue_cont_res_id[1], ue_cont_res_id[2], ue_cont_res_id[3], ue_cont_res_id[4], ue_cont_res_id[5]); // Copying bytes (6 octects) to CEs pointer @@ -220,7 +222,7 @@ int nr_write_ce_dlsch_pdu(module_id_t module_idP, nr_UESpec_TCI_StateInd_PDCCH.ServingCellId = (ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.servingCellId) & 0x1F; //extracting LSB 5 Bits nr_UESpec_TCI_StateInd_PDCCH.TciStateId = (ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.tciStateId) & 0x7F; //extracting LSB 7 bits nr_UESpec_TCI_StateInd_PDCCH.CoresetId2 = (ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.coresetId) & 0x1; //extracting LSB 1 bit - LOG_D(MAC, "NR MAC CE TCI state indication for UE Specific PDCCH = %d \n", nr_UESpec_TCI_StateInd_PDCCH.TciStateId); + LOG_D(NR_MAC, "NR MAC CE TCI state indication for UE Specific PDCCH = %d \n", nr_UESpec_TCI_StateInd_PDCCH.TciStateId); mac_ce_size = sizeof(NR_TCI_PDCCH); // Copying bytes for MAC CEs to the mac pdu pointer memcpy((void *) mac_pdu_ptr, (void *)&nr_UESpec_TCI_StateInd_PDCCH, mac_ce_size); @@ -331,7 +333,7 @@ int nr_write_ce_dlsch_pdu(module_id_t module_idP, ((NR_MAC_CE_SP_ZP_CSI_RS_RES_SET *) mac_pdu_ptr)->BWPID = ue_sched_ctl->UE_mac_ce_ctrl.sp_zp_csi_rs.bwpid & 0x3; //2 bits ((NR_MAC_CE_SP_ZP_CSI_RS_RES_SET *) mac_pdu_ptr)->CSIRS_RSC_ID = ue_sched_ctl->UE_mac_ce_ctrl.sp_zp_csi_rs.rsc_id & 0xF; //4 bits ((NR_MAC_CE_SP_ZP_CSI_RS_RES_SET *) mac_pdu_ptr)->R = 0; - LOG_D(MAC, "NR MAC CE of ZP CSIRS Serv cell ID = %d BWPID= %d Rsc set ID = %d\n", ue_sched_ctl->UE_mac_ce_ctrl.sp_zp_csi_rs.serv_cell_id, ue_sched_ctl->UE_mac_ce_ctrl.sp_zp_csi_rs.bwpid, + LOG_D(NR_MAC, "NR MAC CE of ZP CSIRS Serv cell ID = %d BWPID= %d Rsc set ID = %d\n", ue_sched_ctl->UE_mac_ce_ctrl.sp_zp_csi_rs.serv_cell_id, ue_sched_ctl->UE_mac_ce_ctrl.sp_zp_csi_rs.bwpid, ue_sched_ctl->UE_mac_ce_ctrl.sp_zp_csi_rs.rsc_id); mac_ce_size = sizeof(NR_MAC_CE_SP_ZP_CSI_RS_RES_SET); mac_pdu_ptr += (unsigned char) mac_ce_size; @@ -388,7 +390,15 @@ void nr_store_dlsch_buffer(module_id_t module_id, NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; sched_ctrl->num_total_bytes = 0; - const int lcid = DL_SCH_LCID_DTCH; + if ((sched_ctrl->lcid_mask&(1<<4)) > 0 && loop_dcch_dtch == DL_SCH_LCID_DCCH1) + loop_dcch_dtch = DL_SCH_LCID_DTCH; + else if ((sched_ctrl->lcid_mask&(1<<1)) > 0 && loop_dcch_dtch == DL_SCH_LCID_DTCH) + loop_dcch_dtch = DL_SCH_LCID_DCCH; + else if ((sched_ctrl->lcid_mask&(1<<2)) > 0 && loop_dcch_dtch == DL_SCH_LCID_DCCH) + loop_dcch_dtch = DL_SCH_LCID_DCCH1; + + const int lcid = loop_dcch_dtch; + // const int lcid = DL_SCH_LCID_DTCH; const uint16_t rnti = UE_info->rnti[UE_id]; sched_ctrl->rlc_status[lcid] = mac_rlc_status_ind(module_id, rnti, @@ -401,11 +411,23 @@ void nr_store_dlsch_buffer(module_id_t module_id, 0, 0); sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; - LOG_D(MAC, - "[%s][%d.%d], DTCH%d->DLSCH, RLC status %d bytes TA %d\n", + LOG_D(NR_MAC, + "%d.%d, LCID%d:->DLSCH, RLC status %d bytes. \n", + frame, + slot, + lcid, + sched_ctrl->num_total_bytes); + + if (sched_ctrl->num_total_bytes == 0 + && !sched_ctrl->ta_apply) /* If TA should be applied, give at least one RB */ + return; + + LOG_D(NR_MAC, + "[%s][%d.%d], %s%d->DLSCH, RLC status %d bytes TA %d\n", __func__, frame, slot, + lcid<4?"DCCH":"DTCH", lcid, sched_ctrl->rlc_status[lcid].bytes_in_buffer, sched_ctrl->ta_apply); @@ -418,18 +440,25 @@ bool allocate_dl_retransmission(module_id_t module_id, uint8_t *rballoc_mask, int *n_rb_sched, int UE_id, - int current_harq_pid) -{ + int current_harq_pid) { + const NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels->ServingCellConfigCommon; NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; NR_sched_pdsch_t *retInfo = &sched_ctrl->harq_processes[current_harq_pid].sched_pdsch; - const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - const uint8_t num_dmrs_cdm_grps_no_data = 1; + NR_BWP_t *genericParameters = sched_ctrl->active_bwp ? + &sched_ctrl->active_bwp->bwp_Common->genericParameters : + &RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + + const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + int rbStart = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + + NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; + const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; + const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_bwp ? (f ? 1 : (ps->nrOfSymbols == 2 ? 1 : 2)) : (ps->nrOfSymbols == 2 ? 1 : 2); int rbSize = 0; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; if (tda == retInfo->time_domain_allocation) { /* Check that there are enough resources for retransmission */ while (rbSize < retInfo->rbSize) { @@ -438,7 +467,7 @@ bool allocate_dl_retransmission(module_id_t module_id, while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++; if (rbStart >= bwpSize) { - LOG_D(MAC, "cannot allocate retransmission for UE %d/RNTI %04x: no resources\n", UE_id, UE_info->rnti[UE_id]); + LOG_D(NR_MAC, "cannot allocate retransmission for UE %d/RNTI %04x: no resources\n", UE_id, UE_info->rnti[UE_id]); return false; } while (rbStart + rbSize < bwpSize && rballoc_mask[rbStart + rbSize] && rbSize < retInfo->rbSize) @@ -446,10 +475,9 @@ bool allocate_dl_retransmission(module_id_t module_id, } /* check whether we need to switch the TDA allocation since the last * (re-)transmission */ - NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( - scc, UE_info->secondaryCellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, ps); + scc, UE_info->CellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, ps); } else { /* the retransmission will use a different time domain allocation, check * that we have enough resources */ @@ -459,7 +487,7 @@ bool allocate_dl_retransmission(module_id_t module_id, rbSize++; NR_pdsch_semi_static_t temp_ps; nr_set_pdsch_semi_static( - scc, UE_info->secondaryCellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, &temp_ps); + scc, UE_info->CellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, &temp_ps); uint32_t new_tbs; uint16_t new_rbSize; bool success = nr_find_nb_rb(retInfo->Qm, @@ -492,7 +520,7 @@ bool allocate_dl_retransmission(module_id_t module_id, /* Find PUCCH occasion: if it fails, undo CCE allocation (undoing PUCCH * allocation after CCE alloc fail would be more complex) */ - const int alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot); + const int alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot, -1); if (alloc<0) { LOG_D(MAC, "%s(): could not find PUCCH for UE %d/%04x@%d.%d\n", @@ -532,8 +560,10 @@ void pf_dl(module_id_t module_id, int max_num_ue, int n_rb_sched, uint8_t *rballoc_mask) { - const NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels->ServingCellConfigCommon; - NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + + gNB_MAC_INST *mac = RC.nrmac[module_id]; + NR_UE_info_t *UE_info = &mac->UE_info; + NR_ServingCellConfigCommon_t *scc=mac->common_channels[0].ServingCellConfigCommon; float coeff_ue[MAX_MOBILES_PER_GNB]; // UEs that could be scheduled int ue_array[MAX_MOBILES_PER_GNB]; @@ -541,6 +571,7 @@ void pf_dl(module_id_t module_id, /* Loop UE_info->list to check retransmission */ for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + if (UE_info->Msg4_ACKed[UE_id] != true) continue; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; @@ -558,7 +589,7 @@ void pf_dl(module_id_t module_id, bool r = allocate_dl_retransmission( module_id, frame, slot, rballoc_mask, &n_rb_sched, UE_id, sched_pdsch->dl_harq_pid); if (!r) { - LOG_D(MAC, "%4d.%2d retransmission can NOT be allocated\n", frame, slot); + LOG_D(NR_MAC, "%4d.%2d retransmission can NOT be allocated\n", frame, slot); continue; } /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */ @@ -573,7 +604,7 @@ void pf_dl(module_id_t module_id, sched_pdsch->mcs = 9; uint32_t tbs = pf_tbs[ps->mcsTableIdx][sched_pdsch->mcs]; coeff_ue[UE_id] = (float) tbs / thr_ue[UE_id]; - LOG_D(MAC,"b %d, thr_ue[%d] %f, tbs %d, coeff_ue[%d] %f\n", + LOG_D(NR_MAC,"b %d, thr_ue[%d] %f, tbs %d, coeff_ue[%d] %f\n", b, UE_id, thr_ue[UE_id], tbs, UE_id, coeff_ue[UE_id]); /* Create UE_sched list for UEs eligible for new transmission*/ add_tail_nr_list(&UE_sched, UE_id); @@ -601,14 +632,19 @@ void pf_dl(module_id_t module_id, *p = -1; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + int bwp_Id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; const uint16_t rnti = UE_info->rnti[UE_id]; - const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + NR_BWP_t *genericParameters = sched_ctrl->active_bwp ? + &sched_ctrl->active_bwp->bwp_Common->genericParameters: + &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + + const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth,MAX_BWP_SIZE); + int rbStart = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); /* Find a free CCE */ bool freeCCE = find_free_CCE(module_id, slot, UE_id); if (!freeCCE) { - LOG_D(MAC, "%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n", frame, slot, UE_id, rnti); + LOG_D(NR_MAC, "%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n", frame, slot, UE_id, rnti); continue; } /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */ @@ -617,9 +653,9 @@ void pf_dl(module_id_t module_id, /* Find PUCCH occasion: if it fails, undo CCE allocation (undoing PUCCH * allocation after CCE alloc fail would be more complex) */ - const int alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot); + const int alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot, -1); if (alloc<0) { - LOG_D(MAC, + LOG_D(NR_MAC, "%s(): could not find PUCCH for UE %d/%04x@%d.%d\n", __func__, UE_id, @@ -628,7 +664,7 @@ void pf_dl(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = mac->cce_list[bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -641,13 +677,14 @@ void pf_dl(module_id_t module_id, max_rbSize++; /* MCS has been set above */ - const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; + const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; + const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_bwp ? (f ? 1 : (ps->nrOfSymbols == 2 ? 1 : 2)) : (ps->nrOfSymbols == 2 ? 1 : 2); if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( - scc, UE_info->secondaryCellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, ps); + scc, UE_info->CellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, ps); sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, ps->mcsTableIdx); sched_pdsch->R = nr_get_code_rate_dl(sched_pdsch->mcs, ps->mcsTableIdx); sched_pdsch->pucch_allocation = alloc; @@ -677,6 +714,7 @@ void pf_dl(module_id_t module_id, void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t slot) { NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon; if (UE_info->num_UEs == 0) return; @@ -688,11 +726,10 @@ void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; - if (tda < 0) - return; - - const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp ? + sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth: + scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, + MAX_BWP_SIZE); uint16_t *vrb_map = RC.nrmac[module_id]->common_channels[CC_id].vrb_map; uint8_t rballoc_mask[bwpSize]; @@ -764,6 +801,7 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_list_t *UE_list = &UE_info->list; for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->ul_failure==1 && get_softmodem_params()->phy_test==0) continue; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; UE_info->mac_stats[UE_id].dlsch_current_bytes = 0; @@ -773,7 +811,7 @@ void nr_schedule_ue_spec(module_id_t module_id, * If we add the CE, ta_apply will be reset */ if (frame == (sched_ctrl->ta_frame + 10) % 1024){ sched_ctrl->ta_apply = true; /* the timer is reset once TA CE is scheduled */ - LOG_D(MAC, "[UE %d][%d.%d] UL timing alignment procedures: setting flag for Timing Advance command\n", UE_id, frame, slot); + LOG_D(NR_MAC, "[UE %d][%d.%d] UL timing alignment procedures: setting flag for Timing Advance command\n", UE_id, frame, slot); } if (sched_pdsch->rbSize <= 0) @@ -819,7 +857,7 @@ void nr_schedule_ue_spec(module_id_t module_id, harq->is_waiting = true; UE_info->mac_stats[UE_id].dlsch_rounds[harq->round]++; - LOG_D(MAC, + LOG_D(NR_MAC, "%4d.%2d RNTI %04x start %3d RBs %3d startSymbol %2d nb_symbol %2d MCS %2d TBS %4d HARQ PID %2d round %d NDI %d\n", frame, slot, @@ -835,15 +873,11 @@ void nr_schedule_ue_spec(module_id_t module_id, harq->ndi); NR_BWP_Downlink_t *bwp = sched_ctrl->active_bwp; - AssertFatal(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList, - "searchSpacesToAddModList is null\n"); - AssertFatal(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.count > 0, - "searchSPacesToAddModList is empty\n"); /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not * exist, create it */ - const int bwpid = sched_ctrl->active_bwp->bwp_Id; - const int coresetid = sched_ctrl->coreset->controlResourceSetId; + const int bwpid = bwp ? bwp->bwp_Id : 0; + const int coresetid = bwp ? sched_ctrl->coreset->controlResourceSetId : gNB_mac->sched_ctrlCommon->coreset->controlResourceSetId; nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -852,7 +886,10 @@ void nr_schedule_ue_spec(module_id_t module_id, dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu)); dl_req->nPDUs += 1; pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; - nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, bwp); + LOG_D(NR_MAC,"Trying to configure DL pdcch for bwp %d, cs %d\n",bwpid,coresetid); + NR_SearchSpace_t *ss = bwp ? sched_ctrl->search_space:gNB_mac->sched_ctrlCommon->search_space; + NR_ControlResourceSet_t *coreset = bwp? sched_ctrl->coreset:gNB_mac->sched_ctrlCommon->coreset; + nr_configure_pdcch(pdcch_pdu, ss, coreset, scc, bwp); gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu; } @@ -872,13 +909,13 @@ void nr_schedule_ue_spec(module_id_t module_id, pdsch_pdu->pduIndex = pduindex; // BWP - pdsch_pdu->BWPSize = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdsch_pdu->BWPStart = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); - pdsch_pdu->SubcarrierSpacing = bwp->bwp_Common->genericParameters.subcarrierSpacing; - if (bwp->bwp_Common->genericParameters.cyclicPrefix) - pdsch_pdu->CyclicPrefix = *bwp->bwp_Common->genericParameters.cyclicPrefix; - else - pdsch_pdu->CyclicPrefix = 0; + NR_BWP_t *genericParameters = bwp ? &bwp->bwp_Common->genericParameters : &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + + pdsch_pdu->BWPSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + pdsch_pdu->BWPStart = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth,MAX_BWP_SIZE); + pdsch_pdu->SubcarrierSpacing = genericParameters->subcarrierSpacing; + + pdsch_pdu->CyclicPrefix = genericParameters->cyclicPrefix ? *genericParameters->cyclicPrefix : 0; // Codeword information pdsch_pdu->NrOfCodewords = 1; @@ -886,6 +923,7 @@ void nr_schedule_ue_spec(module_id_t module_id, pdsch_pdu->qamModOrder[0] = Qm; pdsch_pdu->mcsIndex[0] = sched_pdsch->mcs; pdsch_pdu->mcsTable[0] = ps->mcsTableIdx; + AssertFatal(harq!=NULL,"harq is null\n"); AssertFatal(harq->round<4,"%d",harq->round); pdsch_pdu->rvIndex[0] = nr_rv_round_map[harq->round]; pdsch_pdu->TBSize[0] = TBS; @@ -913,9 +951,16 @@ void nr_schedule_ue_spec(module_id_t module_id, pdsch_pdu->StartSymbolIndex = ps->startSymbolIndex; pdsch_pdu->NrOfSymbols = ps->nrOfSymbols; + NR_PDSCH_Config_t *pdsch_Config=NULL; + if (bwp && + bwp->bwp_Dedicated && + bwp->bwp_Dedicated->pdsch_Config && + bwp->bwp_Dedicated->pdsch_Config->choice.setup) + pdsch_Config = bwp->bwp_Dedicated->pdsch_Config->choice.setup; + /* Check and validate PTRS values */ struct NR_SetupRelease_PTRS_DownlinkConfig *phaseTrackingRS = - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS; + pdsch_Config ? pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS : NULL; if (phaseTrackingRS) { bool valid_ptrs_setup = set_dl_ptrs_values(phaseTrackingRS->choice.setup, pdsch_pdu->rbSize, @@ -935,7 +980,9 @@ void nr_schedule_ue_spec(module_id_t module_id, nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu->dci_pdu[pdcch_pdu->numDlDci]; pdcch_pdu->numDlDci++; dci_pdu->RNTI = rnti; - if (sched_ctrl->coreset->pdcch_DMRS_ScramblingID && + if (sched_ctrl->coreset && + sched_ctrl->search_space && + sched_ctrl->coreset->pdcch_DMRS_ScramblingID && sched_ctrl->search_space->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) { dci_pdu->ScramblingId = *sched_ctrl->coreset->pdcch_DMRS_ScramblingID; dci_pdu->ScramblingRNTI = rnti; @@ -952,20 +999,19 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_pdu_rel15_t dci_payload; memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t)); // bwp indicator - - const int n_dl_bwp = UE_info->secondaryCellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; - AssertFatal(n_dl_bwp <= 4, - "downlinkBWP_ToAddModList has %d BWP!\n", - n_dl_bwp); + const int n_dl_bwp = bwp ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count : 0; + AssertFatal(n_dl_bwp <= 4, "downlinkBWP_ToAddModList has %d BWP!\n", n_dl_bwp); + // as per table 7.3.1.1.2-1 in 38.212 - dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1; - AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, - "Only frequency resource allocation type 1 is currently supported\n"); + dci_payload.bwp_indicator.val = bwp ? (n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1) : 0; + if (bwp) AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, + "Only frequency resource allocation type 1 is currently supported\n"); dci_payload.frequency_domain_assignment.val = PRBalloc_to_locationandbandwidth0( pdsch_pdu->rbSize, pdsch_pdu->rbStart, pdsch_pdu->BWPSize); + dci_payload.format_indicator = 1; dci_payload.time_domain_assignment.val = ps->time_domain_allocation; dci_payload.mcs = sched_pdsch->mcs; dci_payload.rv = pdsch_pdu->rvIndex[0]; @@ -977,9 +1023,9 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_payload.pdsch_to_harq_feedback_timing_indicator.val = pucch->timing_indicator; // PDSCH to HARQ TI dci_payload.antenna_ports.val = 0; // nb of cdm groups w/o data 1 and dmrs port 0 dci_payload.dmrs_sequence_initialization.val = pdsch_pdu->SCID; - LOG_D(MAC, + LOG_D(NR_MAC, "%4d.%2d DCI type 1 payload: freq_alloc %d (%d,%d,%d), " - "time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d\n", + "time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d\n", frame, slot, dci_payload.frequency_domain_assignment.val, @@ -991,22 +1037,23 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_payload.mcs, dci_payload.tb_scaling, dci_payload.ndi, - dci_payload.rv); + dci_payload.rv, + dci_payload.tpc); const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; - const int dci_format = f ? NR_DL_DCI_FORMAT_1_1 : NR_DL_DCI_FORMAT_1_0; + const int dci_format = bwp ? (f ? NR_DL_DCI_FORMAT_1_1 : NR_DL_DCI_FORMAT_1_0) : NR_DL_DCI_FORMAT_1_0; const int rnti_type = NR_RNTI_C; fill_dci_pdu_rel15(scc, - UE_info->secondaryCellGroup[UE_id], + UE_info->CellGroup[UE_id], dci_pdu, &dci_payload, dci_format, rnti_type, pdsch_pdu->BWPSize, - bwp->bwp_Id); + bwp? bwp->bwp_Id : 0); - LOG_D(MAC, + LOG_D(NR_MAC, "coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", (unsigned long long)pdcch_pdu->FreqDomainResource, pdcch_pdu->StartSymbolIndex, @@ -1016,7 +1063,7 @@ void nr_schedule_ue_spec(module_id_t module_id, /* we do not have to do anything, since we do not require to get data * from RLC or encode MAC CEs. The TX_req structure is filled below * or copy data to FAPI structures */ - LOG_D(MAC, + LOG_D(NR_MAC, "%d.%2d DL retransmission UE %d/RNTI %04x HARQ PID %d round %d NDI %d\n", frame, slot, @@ -1025,13 +1072,14 @@ void nr_schedule_ue_spec(module_id_t module_id, current_harq_pid, harq->round, harq->ndi); + AssertFatal(harq->sched_pdsch.tb_size == TBS, "UE %d mismatch between scheduled TBS and buffered TB for HARQ PID %d\n", UE_id, current_harq_pid); } else { /* initial transmission */ - LOG_D(MAC, "[%s] Initial HARQ transmission in %d.%d\n", __FUNCTION__, frame, slot); + LOG_D(NR_MAC, "[%s] Initial HARQ transmission in %d.%d\n", __FUNCTION__, frame, slot); uint8_t *buf = (uint8_t *) harq->tb; @@ -1047,7 +1095,8 @@ void nr_schedule_ue_spec(module_id_t module_id, /* next, get RLC data */ - const int lcid = DL_SCH_LCID_DTCH; + // const int lcid = DL_SCH_LCID_DTCH; + const int lcid = loop_dcch_dtch; int dlsch_total_bytes = 0; if (sched_ctrl->num_total_bytes > 0) { tbs_size_t len = 0; @@ -1074,12 +1123,13 @@ void nr_schedule_ue_spec(module_id_t module_id, 0, 0); - LOG_D(MAC, - "%4d.%2d RNTI %04x: %d bytes from DTCH %d (ndata %d, remaining size %d)\n", + LOG_D(NR_MAC, + "%4d.%2d RNTI %04x: %d bytes from %s %d (ndata %d, remaining size %d)\n", frame, slot, rnti, len, + lcid < 4 ? "DCCH" : "DTCH", lcid, ndata, size); @@ -1108,7 +1158,7 @@ void nr_schedule_ue_spec(module_id_t module_id, buf += 3; size -= 3; DevAssert(size > 0); - LOG_D(MAC, "Configuring DL_TX in %d.%d: TBS %d with %d B of random data\n", frame, slot, TBS, size); + LOG_D(NR_MAC, "Configuring DL_TX in %d.%d: TBS %d with %d B of random data\n", frame, slot, TBS, size); // fill dlsch_buffer with random data for (int i = 0; i < size; i++) buf[i] = lrand48() & 0xff; @@ -1150,7 +1200,7 @@ void nr_schedule_ue_spec(module_id_t module_id, if (sched_ctrl->ta_apply) { sched_ctrl->ta_apply = false; sched_ctrl->ta_frame = frame; - LOG_D(MAC, + LOG_D(NR_MAC, "%d.%2d UE %d TA scheduled, resetting TA frame\n", frame, slot, @@ -1173,6 +1223,6 @@ void nr_schedule_ue_spec(module_id_t module_id, gNB_mac->TX_req[CC_id].Slot = slot; /* mark UE as scheduled */ - memset(sched_pdsch, 0, sizeof(*sched_pdsch)); + sched_pdsch->rbSize = 0; } } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 4986ed747d..1dda52b567 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -271,10 +271,10 @@ void nr_preprocessor_phytest(module_id_t module_id, { if (!is_xlsch_in_slot(dlsch_slot_bitmap, slot)) return; - const int CC_id = 0; - const NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[CC_id].ServingCellConfigCommon; NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon; const int UE_id = 0; + const int CC_id = 0; AssertFatal(UE_info->active[UE_id], "%s(): expected UE %d to be active\n", __func__, @@ -341,7 +341,7 @@ void nr_preprocessor_phytest(module_id_t module_id, __func__, UE_id); - const int alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot); + const int alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot, -1); if (alloc < 0) { LOG_D(MAC, "%s(): could not find PUCCH for UE %d/%04x@%d.%d\n", @@ -366,11 +366,11 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_pdsch->pucch_allocation = alloc; sched_pdsch->rbStart = rbStart; sched_pdsch->rbSize = rbSize; - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot]; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; const uint8_t num_dmrs_cdm_grps_no_data = 1; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( - scc, UE_info->secondaryCellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, ps); + scc, UE_info->CellGroup[UE_id], sched_ctrl->active_bwp, tda, num_dmrs_cdm_grps_no_data, ps); sched_pdsch->mcs = target_dl_mcs; sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, ps->mcsTableIdx); @@ -416,7 +416,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 1; if (tda < 0) return false; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = @@ -425,7 +425,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ "time domain assignment %d >= %d\n", tda, tdaList->list.count); - int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu); + int K2 = get_K2(scc,sched_ctrl->active_ubwp, tda, mu); const int sched_frame = frame + (slot + K2 >= nr_slots_per_frame[mu]); const int sched_slot = (slot + K2) % nr_slots_per_frame[mu]; /* check if slot is UL, and that slot is 8 (assuming K2=6 because of UE diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 0ff4136057..958ceed323 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -122,15 +122,21 @@ static inline uint8_t get_max_cces(uint8_t scs) { return (nr_max_number_of_cces_per_slot[scs]); } -NR_ControlResourceSet_t *get_coreset(const NR_BWP_Downlink_t *bwp, const NR_SearchSpace_t *ss, int ss_type) -{ +NR_ControlResourceSet_t *get_coreset(NR_ServingCellConfigCommon_t *scc, + NR_BWP_Downlink_t *bwp, + NR_SearchSpace_t *ss, + NR_SearchSpace__searchSpaceType_PR ss_type) { NR_ControlResourceSetId_t coreset_id = *ss->controlResourceSetId; - if (ss_type == 0) { // common search space - AssertFatal(coreset_id != 0, "coreset0 currently not supported\n"); - NR_ControlResourceSet_t *coreset = bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet; - AssertFatal(coreset_id == coreset->controlResourceSetId, - "ID of common ss coreset does not correspond to id set in the " - "search space\n"); + if (ss_type == NR_SearchSpace__searchSpaceType_PR_common) { // common search space + NR_ControlResourceSet_t *coreset; + if (bwp) coreset = bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet; + else if (scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonControlResourceSet) + coreset = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonControlResourceSet; + else coreset = NULL; + + if (coreset) AssertFatal(coreset_id == coreset->controlResourceSetId, + "ID of common ss coreset does not correspond to id set in the " + "search space\n"); return coreset; } else { const int n = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.count; @@ -145,21 +151,24 @@ NR_ControlResourceSet_t *get_coreset(const NR_BWP_Downlink_t *bwp, const NR_Sear } } -NR_SearchSpace_t *get_searchspace(const NR_BWP_Downlink_t *bwp, NR_SearchSpace__searchSpaceType_PR target_ss) -{ - DevAssert(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList); - DevAssert(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.count > 0); +NR_SearchSpace_t *get_searchspace(NR_ServingCellConfigCommon_t *scc, + NR_BWP_DownlinkDedicated_t *bwp_Dedicated, + NR_SearchSpace__searchSpaceType_PR target_ss) { - const int n = bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.count; + const int n = bwp_Dedicated ? + bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.count: + scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list.count; for (int i=0;ibwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.array[i]; + NR_SearchSpace_t *ss = bwp_Dedicated ? + bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.array[i]: + scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list.array[i]; AssertFatal(ss->controlResourceSetId != NULL, "ss->controlResourceSetId is null\n"); AssertFatal(ss->searchSpaceType != NULL, "ss->searchSpaceType is null\n"); if (ss->searchSpaceType->present == target_ss) { return ss; } } - AssertFatal(0, "Couldn't find an adequate searchspace\n"); + AssertFatal(0, "Couldn't find an adequate searchspace bwp_Dedicated %p\n",bwp_Dedicated); } int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, @@ -176,8 +185,8 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, int coreset_id = coreset->controlResourceSetId; int *cce_list; - if( bwp==NULL || bwp->bwp_Id == 0 ) { - cce_list = nr_mac->cce_list[1][0]; + if(bwp == NULL || bwp->bwp_Id == 0) { + cce_list = nr_mac->cce_list[0][0]; } else { cce_list = nr_mac->cce_list[bwp->bwp_Id][coreset_id]; } @@ -265,36 +274,35 @@ void nr_set_pdsch_semi_static(const NR_ServingCellConfigCommon_t *scc, { ps->time_domain_allocation = tda; - const struct NR_PDSCH_TimeDomainResourceAllocationList *tdaList = - bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + const struct NR_PDSCH_TimeDomainResourceAllocationList *tdaList = bwp ? + bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList : + scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; AssertFatal(tda < tdaList->list.count, "time_domain_allocation %d>=%d\n", tda, tdaList->list.count); const int mapping_type = tdaList->list.array[tda]->mappingType; const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength; SLIV2SL(startSymbolAndLength, &ps->startSymbolIndex, &ps->nrOfSymbols); - if (!secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup - ->mcs_Table) - ps->mcsTableIdx = 0; - else if (*secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup - ->mcs_Table - == 0) - ps->mcsTableIdx = 1; - else - ps->mcsTableIdx = 2; + ps->mcsTableIdx = 0; + if (bwp && + bwp->bwp_Dedicated && + bwp->bwp_Dedicated->pdsch_Config && + bwp->bwp_Dedicated->pdsch_Config->choice.setup && + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table) { + if (*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table == 0) + ps->mcsTableIdx = 1; + else + ps->mcsTableIdx = 2; + } + else ps->mcsTableIdx = 0; ps->numDmrsCdmGrpsNoData = num_dmrs_cdm_grps_no_data; - ps->dmrsConfigType = - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type - == NULL - ? 0 - : 1; + ps->dmrsConfigType = bwp!=NULL ? (bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; + // if no data in dmrs cdm group is 1 only even REs have no data // if no data in dmrs cdm group is 2 both odd and even REs have no data ps->N_PRB_DMRS = num_dmrs_cdm_grps_no_data * (ps->dmrsConfigType == NFAPI_NR_DMRS_TYPE1 ? 6 : 4); - ps->N_DMRS_SLOT = - get_num_dmrs_symbols(bwp->bwp_Dedicated->pdsch_Config->choice.setup, scc->dmrs_TypeA_Position, ps->nrOfSymbols, ps->startSymbolIndex, mapping_type); - ps->dl_dmrs_symb_pos = - fill_dmrs_mask(bwp->bwp_Dedicated->pdsch_Config->choice.setup, scc->dmrs_TypeA_Position, ps->nrOfSymbols, ps->startSymbolIndex, mapping_type); + ps->dl_dmrs_symb_pos = fill_dmrs_mask(bwp ? bwp->bwp_Dedicated->pdsch_Config->choice.setup : NULL, scc->dmrs_TypeA_Position, ps->nrOfSymbols, ps->startSymbolIndex, mapping_type); + ps->N_DMRS_SLOT = get_num_dmrs(ps->dl_dmrs_symb_pos); } void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, @@ -308,27 +316,29 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, ps->time_domain_allocation = tda; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = - ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + ubwp? + ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList: + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList ; const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength; SLIV2SL(startSymbolAndLength, &ps->startSymbolIndex, &ps->nrOfSymbols); - ps->pusch_Config = ubwp->bwp_Dedicated->pusch_Config->choice.setup; - if (!ps->pusch_Config->transformPrecoder) + ps->pusch_Config = ubwp?ubwp->bwp_Dedicated->pusch_Config->choice.setup:NULL; + if (ps->pusch_Config == NULL || !ps->pusch_Config->transformPrecoder) ps->transform_precoding = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; else ps->transform_precoding = *ps->pusch_Config->transformPrecoder; const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; if (ps->transform_precoding) - ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config->mcs_Table, + ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config ? ps->pusch_Config->mcs_Table : NULL, 0, ps->dci_format, NR_RNTI_C, target_ss, false); else { - ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config->mcs_TableTransformPrecoder, + ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config ? ps->pusch_Config->mcs_TableTransformPrecoder : NULL, 1, ps->dci_format, NR_RNTI_C, @@ -341,20 +351,20 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, /* DMRS calculations */ ps->mapping_type = tdaList->list.array[tda]->mappingType; - ps->NR_DMRS_UplinkConfig = - ps->mapping_type == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA - ? ps->pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup - : ps->pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; - ps->dmrs_config_type = ps->NR_DMRS_UplinkConfig->dmrs_Type == NULL ? 0 : 1; + ps->NR_DMRS_UplinkConfig = ps->pusch_Config ? + (ps->mapping_type == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA ? + ps->pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup : + ps->pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup) : NULL; + ps->dmrs_config_type = ps->NR_DMRS_UplinkConfig ? ((ps->NR_DMRS_UplinkConfig->dmrs_Type == NULL ? 0 : 1)) : 0; const pusch_dmrs_AdditionalPosition_t additional_pos = - ps->NR_DMRS_UplinkConfig->dmrs_AdditionalPosition == NULL - ? 2 - : (*ps->NR_DMRS_UplinkConfig->dmrs_AdditionalPosition == - NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos3 - ? 3 - : *ps->NR_DMRS_UplinkConfig->dmrs_AdditionalPosition); + ps->NR_DMRS_UplinkConfig ? (ps->NR_DMRS_UplinkConfig->dmrs_AdditionalPosition == NULL + ? 2 + : (*ps->NR_DMRS_UplinkConfig->dmrs_AdditionalPosition == + NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos3 + ? 3 + : *ps->NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)):2; const pusch_maxLength_t pusch_maxLength = - ps->NR_DMRS_UplinkConfig->maxLength == NULL ? 1 : 2; + ps->NR_DMRS_UplinkConfig ? (ps->NR_DMRS_UplinkConfig->maxLength == NULL ? 1 : 2) : 1; ps->ul_dmrs_symb_pos = get_l_prime(ps->nrOfSymbols, ps->mapping_type, additional_pos, @@ -589,6 +599,7 @@ void nr_configure_css_dci_initial(nfapi_nr_dl_tti_pdcch_pdu_rel15_t* pdcch_pdu, } void config_uldci(const NR_BWP_Uplink_t *ubwp, + const NR_ServingCellConfigCommon_t *scc, const nfapi_nr_pusch_pdu_t *pusch_pdu, dci_pdu_rel15_t *dci_pdu_rel15, int dci_format, @@ -596,7 +607,9 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, uint8_t tpc, int n_ubwp, int bwp_id) { - const int bw = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + const int bw = NRRIV2BW(ubwp ? + ubwp->bwp_Common->genericParameters.locationAndBandwidth : + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dci_pdu_rel15->frequency_domain_assignment.val = PRBalloc_to_locationandbandwidth0(pusch_pdu->rb_size, pusch_pdu->rb_start, bw); dci_pdu_rel15->time_domain_assignment.val = time_domain_assignment; @@ -606,8 +619,8 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, dci_pdu_rel15->rv = pusch_pdu->pusch_data.rv_index; dci_pdu_rel15->harq_pid = pusch_pdu->pusch_data.harq_process_id; dci_pdu_rel15->tpc = tpc; - AssertFatal(ubwp->bwp_Dedicated->pusch_Config->choice.setup->resourceAllocation == NR_PUSCH_Config__resourceAllocation_resourceAllocationType1, - "Only frequency resource allocation type 1 is currently supported\n"); + if (ubwp) AssertFatal(ubwp->bwp_Dedicated->pusch_Config->choice.setup->resourceAllocation == NR_PUSCH_Config__resourceAllocation_resourceAllocationType1, + "Only frequency resource allocation type 1 is currently supported\n"); switch (dci_format) { case NR_UL_DCI_FORMAT_0_0: dci_pdu_rel15->format_indicator = 0; @@ -617,7 +630,11 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, // bwp indicator as per table 7.3.1.1.2-1 in 38.212 dci_pdu_rel15->bwp_indicator.val = n_ubwp < 4 ? bwp_id : bwp_id - 1; // SRS resource indicator - if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) { + if (ubwp && + ubwp->bwp_Dedicated && + ubwp->bwp_Dedicated->pusch_Config && + ubwp->bwp_Dedicated->pusch_Config->choice.setup && + ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) { AssertFatal(*ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig == NR_PUSCH_Config__txConfig_codebook, "Non Codebook configuration non supported\n"); dci_pdu_rel15->srs_resource_indicator.val = 0; // taking resource 0 for SRS @@ -631,8 +648,8 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, AssertFatal(0, "Valid UL formats are 0_0 and 0_1\n"); } - LOG_D(MAC, - "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d UL_BWP_ID %d\n", + LOG_D(NR_MAC, + "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n", __func__, dci_pdu_rel15->frequency_domain_assignment.val, dci_pdu_rel15->time_domain_assignment.val, @@ -644,90 +661,93 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, dci_pdu_rel15->bwp_indicator.val); } +const int default_pucch_fmt[] = {0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1}; +const int default_pucch_firstsymb[] = {12,12,12,10,10,10,10,4,4,4,4,0,0,0,0,0}; +const int default_pucch_numbsymb[] = {2,2,2,2,4,4,4,4,10,10,10,10,14,14,14,14,14}; +const int default_pucch_prboffset[] = {0,0,3,0,0,2,4,0,0,2,4,0,0,2,4,-1}; +const int default_pucch_csset[] = {2,3,3,2,4,4,4,2,4,4,4,2,4,4,4,4}; + +int nr_get_default_pucch_res(int pucch_ResourceCommon) { + + AssertFatal(pucch_ResourceCommon>=0 && pucch_ResourceCommon < 16, "illegal pucch_ResourceCommon %d\n",pucch_ResourceCommon); + + return(default_pucch_csset[pucch_ResourceCommon]); +} void nr_configure_pdcch(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu, NR_SearchSpace_t *ss, NR_ControlResourceSet_t *coreset, NR_ServingCellConfigCommon_t *scc, NR_BWP_Downlink_t *bwp) { - int sps = 0; + NR_BWP_t *genericParameters = bwp ? &bwp->bwp_Common->genericParameters : &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; - if (bwp) { // This is not the InitialBWP - pdcch_pdu->BWPSize = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdcch_pdu->BWPStart = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdcch_pdu->SubcarrierSpacing = bwp->bwp_Common->genericParameters.subcarrierSpacing; - pdcch_pdu->CyclicPrefix = (bwp->bwp_Common->genericParameters.cyclicPrefix==NULL) ? 0 : *bwp->bwp_Common->genericParameters.cyclicPrefix; + pdcch_pdu->BWPSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + pdcch_pdu->BWPStart = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + pdcch_pdu->SubcarrierSpacing = genericParameters->subcarrierSpacing; + pdcch_pdu->CyclicPrefix = genericParameters->cyclicPrefix ? *genericParameters->cyclicPrefix:0; - // first symbol - //AssertFatal(pdcch_scs==kHz15, "PDCCH SCS above 15kHz not allowed if a symbol above 2 is monitored"); - sps = bwp->bwp_Common->genericParameters.cyclicPrefix == NULL ? 14 : 12; + // first symbol + //AssertFatal(pdcch_scs==kHz15, "PDCCH SCS above 15kHz not allowed if a symbol above 2 is monitored"); + int sps = genericParameters->cyclicPrefix == NULL ? 14 : 12; + + AssertFatal(ss->monitoringSymbolsWithinSlot!=NULL,"ss->monitoringSymbolsWithinSlot is null\n"); + AssertFatal(ss->monitoringSymbolsWithinSlot->buf!=NULL,"ss->monitoringSymbolsWithinSlot->buf is null\n"); + + // for SPS=14 8 MSBs in positions 13 downto 6 + uint16_t monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | + (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps)); + + for (int i=0; i>(sps-1-i))&1) { + pdcch_pdu->StartSymbolIndex=i; + break; + } } - else { // this is for InitialBWP - pdcch_pdu->BWPSize = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdcch_pdu->BWPStart = NRRIV2PRBOFFSET(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdcch_pdu->SubcarrierSpacing = scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing; - pdcch_pdu->CyclicPrefix = (scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.cyclicPrefix==NULL) ? 0 : *scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.cyclicPrefix; - // first symbol - //AssertFatal(pdcch_scs==kHz15, "PDCCH SCS above 15kHz not allowed if a symbol above 2 is monitored"); - sps = scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.cyclicPrefix == NULL ? 14 : 12; + pdcch_pdu->DurationSymbols = coreset->duration; + + for (int i=0;i<6;i++) + pdcch_pdu->FreqDomainResource[i] = coreset->frequencyDomainResources.buf[i]; + + + //cce-REG-MappingType + pdcch_pdu->CceRegMappingType = coreset->cce_REG_MappingType.present == NR_ControlResourceSet__cce_REG_MappingType_PR_interleaved? + NFAPI_NR_CCE_REG_MAPPING_INTERLEAVED : NFAPI_NR_CCE_REG_MAPPING_NON_INTERLEAVED; + + if (pdcch_pdu->CceRegMappingType == NFAPI_NR_CCE_REG_MAPPING_INTERLEAVED) { + pdcch_pdu->RegBundleSize = (coreset->cce_REG_MappingType.choice.interleaved->reg_BundleSize == NR_ControlResourceSet__cce_REG_MappingType__interleaved__reg_BundleSize_n6) ? 6 : (2+coreset->cce_REG_MappingType.choice.interleaved->reg_BundleSize); + pdcch_pdu->InterleaverSize = (coreset->cce_REG_MappingType.choice.interleaved->interleaverSize==NR_ControlResourceSet__cce_REG_MappingType__interleaved__interleaverSize_n6) ? 6 : (2+coreset->cce_REG_MappingType.choice.interleaved->interleaverSize); + AssertFatal(scc->physCellId != NULL,"scc->physCellId is null\n"); + pdcch_pdu->ShiftIndex = coreset->cce_REG_MappingType.choice.interleaved->shiftIndex != NULL ? *coreset->cce_REG_MappingType.choice.interleaved->shiftIndex : *scc->physCellId; + } + else { + pdcch_pdu->RegBundleSize = 0; + pdcch_pdu->InterleaverSize = 0; + pdcch_pdu->ShiftIndex = 0; } - AssertFatal(ss->monitoringSymbolsWithinSlot!=NULL,"ss->monitoringSymbolsWithinSlot is null\n"); - AssertFatal(ss->monitoringSymbolsWithinSlot->buf!=NULL,"ss->monitoringSymbolsWithinSlot->buf is null\n"); - - // for SPS=14 8 MSBs in positions 13 downto 6 - uint16_t monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | - (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps)); - for (int i=0; i>(sps-1-i))&1) { - pdcch_pdu->StartSymbolIndex=i; - break; - } - } + if(coreset->controlResourceSetId == 0) { + pdcch_pdu->CoreSetType = NFAPI_NR_CSET_CONFIG_MIB_SIB1; + } else{ + pdcch_pdu->CoreSetType = NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; + } - pdcch_pdu->DurationSymbols = coreset->duration; - - for (int i=0;i<6;i++) - pdcch_pdu->FreqDomainResource[i] = coreset->frequencyDomainResources.buf[i]; - - - //cce-REG-MappingType - pdcch_pdu->CceRegMappingType = coreset->cce_REG_MappingType.present == NR_ControlResourceSet__cce_REG_MappingType_PR_interleaved? - NFAPI_NR_CCE_REG_MAPPING_INTERLEAVED : NFAPI_NR_CCE_REG_MAPPING_NON_INTERLEAVED; - - if (pdcch_pdu->CceRegMappingType == NFAPI_NR_CCE_REG_MAPPING_INTERLEAVED) { - pdcch_pdu->RegBundleSize = (coreset->cce_REG_MappingType.choice.interleaved->reg_BundleSize == NR_ControlResourceSet__cce_REG_MappingType__interleaved__reg_BundleSize_n6) ? 6 : (2+coreset->cce_REG_MappingType.choice.interleaved->reg_BundleSize); - pdcch_pdu->InterleaverSize = (coreset->cce_REG_MappingType.choice.interleaved->interleaverSize==NR_ControlResourceSet__cce_REG_MappingType__interleaved__interleaverSize_n6) ? 6 : (2+coreset->cce_REG_MappingType.choice.interleaved->interleaverSize); - AssertFatal(scc->physCellId != NULL,"scc->physCellId is null\n"); - pdcch_pdu->ShiftIndex = coreset->cce_REG_MappingType.choice.interleaved->shiftIndex != NULL ? *coreset->cce_REG_MappingType.choice.interleaved->shiftIndex : *scc->physCellId; - } - else { - pdcch_pdu->RegBundleSize = 0; - pdcch_pdu->InterleaverSize = 0; - pdcch_pdu->ShiftIndex = 0; - } - - if(coreset->controlResourceSetId == 0) { - pdcch_pdu->CoreSetType = NFAPI_NR_CSET_CONFIG_MIB_SIB1; - } else{ - pdcch_pdu->CoreSetType = NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; - } - - //precoderGranularity - pdcch_pdu->precoderGranularity = coreset->precoderGranularity; + //precoderGranularity + pdcch_pdu->precoderGranularity = coreset->precoderGranularity; } // This function configures pucch pdu fapi structure void nr_configure_pucch(nfapi_nr_pucch_pdu_t* pucch_pdu, - NR_ServingCellConfigCommon_t *scc, - NR_BWP_Uplink_t *bwp, + NR_ServingCellConfigCommon_t *scc, + NR_CellGroupConfig_t *CellGroup, + NR_BWP_Uplink_t *bwp, uint16_t rnti, uint8_t pucch_resource, uint16_t O_csi, uint16_t O_ack, - uint8_t O_sr) { + uint8_t O_sr, + int r_pucch) { NR_PUCCH_Config_t *pucch_Config; NR_PUCCH_Resource_t *pucchres; @@ -745,208 +765,234 @@ void nr_configure_pucch(nfapi_nr_pucch_pdu_t* pucch_pdu, uint16_t O_uci = O_csi + O_ack; - if (bwp) { // This is not the InitialBWP + NR_PUSCH_Config_t *pusch_Config = bwp ? bwp->bwp_Dedicated->pusch_Config->choice.setup : NULL; + long *pusch_id = bwp ? pusch_Config->dataScramblingIdentityPUSCH : NULL; - NR_PUSCH_Config_t *pusch_Config = bwp->bwp_Dedicated->pusch_Config->choice.setup; - long *pusch_id = pusch_Config->dataScramblingIdentityPUSCH; + if (bwp && pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA != NULL) + id0 = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup->transformPrecodingDisabled->scramblingID0; + else if (bwp && pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB != NULL) + id0 = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->transformPrecodingDisabled->scramblingID0; + else id0 = scc->physCellId; - if (pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA != NULL) - id0 = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup->transformPrecodingDisabled->scramblingID0; - if (pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB != NULL) - id0 = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->transformPrecodingDisabled->scramblingID0; - - // hop flags and hopping id are valid for any BWP - switch (bwp->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping){ - case 0 : - // if neither, both disabled - pucch_pdu->group_hop_flag = 0; - pucch_pdu->sequence_hop_flag = 0; - break; - case 1 : - // if enable, group enabled - pucch_pdu->group_hop_flag = 1; - pucch_pdu->sequence_hop_flag = 0; - break; - case 2 : - // if disable, sequence disabled - pucch_pdu->group_hop_flag = 0; - pucch_pdu->sequence_hop_flag = 1; - break; - default: - AssertFatal(1==0,"Group hopping flag %ld undefined (0,1,2) \n", bwp->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping); - } - - if (bwp->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId != NULL) - pucch_pdu->hopping_id = *bwp->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId; - else - pucch_pdu->hopping_id = *scc->physCellId; - - pucch_pdu->bwp_size = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pucch_pdu->bwp_start = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pucch_pdu->subcarrier_spacing = bwp->bwp_Common->genericParameters.subcarrierSpacing; - pucch_pdu->cyclic_prefix = (bwp->bwp_Common->genericParameters.cyclicPrefix==NULL) ? 0 : *bwp->bwp_Common->genericParameters.cyclicPrefix; - - pucch_Config = bwp->bwp_Dedicated->pucch_Config->choice.setup; - - AssertFatal(pucch_Config->resourceSetToAddModList!=NULL, - "PUCCH resourceSetToAddModList is null\n"); - - n_set = pucch_Config->resourceSetToAddModList->list.count; - AssertFatal(n_set>0,"PUCCH resourceSetToAddModList is empty\n"); - - LOG_D(MAC, "UCI n_set= %d\n", n_set); - - N2 = 2; - // procedure to select pucch resource id from resource sets according to - // number of uci bits and pucch resource indicator pucch_resource - // ( see table 9.2.3.2 in 38.213) - for (int i=0; iresourceSetToAddModList->list.array[i]; - n_list = pucchresset->resourceList.list.count; - if (pucchresset->pucch_ResourceSetId == 0 && O_uci<3) { - if (pucch_resource < n_list) - resource_id = pucchresset->resourceList.list.array[pucch_resource]; - else - AssertFatal(1==0,"Couldn't fine pucch resource indicator %d in PUCCH resource set %d for %d UCI bits",pucch_resource,i,O_uci); - } - if (pucchresset->pucch_ResourceSetId == 1 && O_uci>2) { -#if (NR_RRC_VERSION >= MAKE_VERSION(16, 0, 0)) - N3 = pucchresset->maxPayloadSize!= NULL ? *pucchresset->maxPayloadSize : 1706; -#else - N3 = pucchresset->maxPayloadMinus1!= NULL ? *pucchresset->maxPayloadMinus1 : 1706; -#endif - if (N2O_uci) { - if (pucch_resource < n_list) - resource_id = pucchresset->resourceList.list.array[pucch_resource]; - else - AssertFatal(1==0,"Couldn't fine pucch resource indicator %d in PUCCH resource set %d for %d UCI bits",pucch_resource,i,O_uci); - } - else N2 = N3; - } - } - - AssertFatal(resource_id!=NULL,"Couldn-t find any matching PUCCH resource in the PUCCH resource sets"); - - AssertFatal(pucch_Config->resourceToAddModList!=NULL, - "PUCCH resourceToAddModList is null\n"); - - n_list = pucch_Config->resourceToAddModList->list.count; - AssertFatal(n_list>0,"PUCCH resourceToAddModList is empty\n"); - - // going through the list of PUCCH resources to find the one indexed by resource_id - for (int i=0; iresourceToAddModList->list.array[i]; - if (pucchres->pucch_ResourceId == *resource_id) { - res_found = 1; - pucch_pdu->prb_start = pucchres->startingPRB; - pucch_pdu->rnti = rnti; - // FIXME why there is only one frequency hopping flag - // what about inter slot frequency hopping? - pucch_pdu->freq_hop_flag = pucchres->intraSlotFrequencyHopping!= NULL ? 1 : 0; - pucch_pdu->second_hop_prb = pucchres->secondHopPRB!= NULL ? *pucchres->secondHopPRB : 0; - switch(pucchres->format.present) { - case NR_PUCCH_Resource__format_PR_format0 : - pucch_pdu->format_type = 0; - pucch_pdu->initial_cyclic_shift = pucchres->format.choice.format0->initialCyclicShift; - pucch_pdu->nr_of_symbols = pucchres->format.choice.format0->nrofSymbols; - pucch_pdu->start_symbol_index = pucchres->format.choice.format0->startingSymbolIndex; - pucch_pdu->sr_flag = O_sr; - break; - case NR_PUCCH_Resource__format_PR_format1 : - pucch_pdu->format_type = 1; - pucch_pdu->initial_cyclic_shift = pucchres->format.choice.format1->initialCyclicShift; - pucch_pdu->nr_of_symbols = pucchres->format.choice.format1->nrofSymbols; - pucch_pdu->start_symbol_index = pucchres->format.choice.format1->startingSymbolIndex; - pucch_pdu->time_domain_occ_idx = pucchres->format.choice.format1->timeDomainOCC; - pucch_pdu->sr_flag = O_sr; - break; - case NR_PUCCH_Resource__format_PR_format2 : - pucch_pdu->format_type = 2; - pucch_pdu->nr_of_symbols = pucchres->format.choice.format2->nrofSymbols; - pucch_pdu->start_symbol_index = pucchres->format.choice.format2->startingSymbolIndex; - pucch_pdu->data_scrambling_id = pusch_id!= NULL ? *pusch_id : *scc->physCellId; - pucch_pdu->dmrs_scrambling_id = id0!= NULL ? *id0 : *scc->physCellId; - pucch_pdu->prb_size = compute_pucch_prb_size(2,pucchres->format.choice.format2->nrofPRBs, - O_uci+O_sr,O_csi,pucch_Config->format2->choice.setup->maxCodeRate, - 2,pucchres->format.choice.format2->nrofSymbols,8); - pucch_pdu->bit_len_csi_part1 = O_csi; - break; - case NR_PUCCH_Resource__format_PR_format3 : - pucch_pdu->format_type = 3; - pucch_pdu->nr_of_symbols = pucchres->format.choice.format3->nrofSymbols; - pucch_pdu->start_symbol_index = pucchres->format.choice.format3->startingSymbolIndex; - pucch_pdu->data_scrambling_id = pusch_id!= NULL ? *pusch_id : *scc->physCellId; - if (pucch_Config->format3 == NULL) { - pucch_pdu->pi_2bpsk = 0; - pucch_pdu->add_dmrs_flag = 0; - } - else { - pucchfmt = pucch_Config->format3->choice.setup; - pucch_pdu->pi_2bpsk = pucchfmt->pi2BPSK!= NULL ? 1 : 0; - pucch_pdu->add_dmrs_flag = pucchfmt->additionalDMRS!= NULL ? 1 : 0; - } - int f3_dmrs_symbols; - if (pucchres->format.choice.format3->nrofSymbols==4) - f3_dmrs_symbols = 1<freq_hop_flag; - else { - if(pucchres->format.choice.format3->nrofSymbols<10) - f3_dmrs_symbols = 2; - else - f3_dmrs_symbols = 2<add_dmrs_flag; - } - pucch_pdu->prb_size = compute_pucch_prb_size(3,pucchres->format.choice.format3->nrofPRBs, - O_uci+O_sr,O_csi,pucch_Config->format3->choice.setup->maxCodeRate, - 2-pucch_pdu->pi_2bpsk,pucchres->format.choice.format3->nrofSymbols-f3_dmrs_symbols,12); - pucch_pdu->bit_len_csi_part1 = O_csi; - break; - case NR_PUCCH_Resource__format_PR_format4 : - pucch_pdu->format_type = 4; - pucch_pdu->nr_of_symbols = pucchres->format.choice.format4->nrofSymbols; - pucch_pdu->start_symbol_index = pucchres->format.choice.format4->startingSymbolIndex; - pucch_pdu->pre_dft_occ_len = pucchres->format.choice.format4->occ_Length; - pucch_pdu->pre_dft_occ_idx = pucchres->format.choice.format4->occ_Index; - pucch_pdu->data_scrambling_id = pusch_id!= NULL ? *pusch_id : *scc->physCellId; - if (pucch_Config->format3 == NULL) { - pucch_pdu->pi_2bpsk = 0; - pucch_pdu->add_dmrs_flag = 0; - } - else { - pucchfmt = pucch_Config->format3->choice.setup; - pucch_pdu->pi_2bpsk = pucchfmt->pi2BPSK!= NULL ? 1 : 0; - pucch_pdu->add_dmrs_flag = pucchfmt->additionalDMRS!= NULL ? 1 : 0; - } - pucch_pdu->bit_len_csi_part1 = O_csi; - break; - default : - AssertFatal(1==0,"Undefined PUCCH format \n"); - } - } - } - AssertFatal(res_found==1,"No PUCCH resource found corresponding to id %ld\n",*resource_id); - } - else { // this is for InitialBWP - AssertFatal(1==0,"Fill in InitialBWP PUCCH configuration\n"); + NR_PUCCH_ConfigCommon_t *pucch_ConfigCommon = bwp ? + bwp->bwp_Common->pucch_ConfigCommon->choice.setup : + scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup; + // hop flags and hopping id are valid for any BWP + switch (pucch_ConfigCommon->pucch_GroupHopping){ + case 0 : + // if neither, both disabled + pucch_pdu->group_hop_flag = 0; + pucch_pdu->sequence_hop_flag = 0; + break; + case 1 : + // if enable, group enabled + pucch_pdu->group_hop_flag = 1; + pucch_pdu->sequence_hop_flag = 0; + break; + case 2 : + // if disable, sequence disabled + pucch_pdu->group_hop_flag = 0; + pucch_pdu->sequence_hop_flag = 1; + break; + default: + AssertFatal(1==0,"Group hopping flag %ld undefined (0,1,2) \n", bwp->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping); } + if (pucch_ConfigCommon->hoppingId != NULL) + pucch_pdu->hopping_id = *pucch_ConfigCommon->hoppingId; + else + pucch_pdu->hopping_id = *scc->physCellId; + NR_BWP_t *genericParameters = bwp ? + &bwp->bwp_Common->genericParameters: + &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + pucch_pdu->bwp_size = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + pucch_pdu->bwp_start = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth,MAX_BWP_SIZE); + pucch_pdu->subcarrier_spacing = genericParameters->subcarrierSpacing; + pucch_pdu->cyclic_prefix = (genericParameters->cyclicPrefix==NULL) ? 0 : *genericParameters->cyclicPrefix; + if (r_pucch<0 || bwp){ + // we have either a dedicated BWP or Dedicated PUCCH configuration on InitialBWP + pucch_Config = bwp ? + bwp->bwp_Dedicated->pucch_Config->choice.setup: + CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup; + + AssertFatal(pucch_Config->resourceSetToAddModList!=NULL, + "PUCCH resourceSetToAddModList is null\n"); + + n_set = pucch_Config->resourceSetToAddModList->list.count; + AssertFatal(n_set>0,"PUCCH resourceSetToAddModList is empty\n"); + + LOG_D(NR_MAC, "UCI n_set= %d\n", n_set); + + N2 = 2; + // procedure to select pucch resource id from resource sets according to + // number of uci bits and pucch resource indicator pucch_resource + // ( see table 9.2.3.2 in 38.213) + for (int i=0; iresourceSetToAddModList->list.array[i]; + n_list = pucchresset->resourceList.list.count; + if (pucchresset->pucch_ResourceSetId == 0 && O_uci<3) { + if (pucch_resource < n_list) + resource_id = pucchresset->resourceList.list.array[pucch_resource]; + else + AssertFatal(1==0,"Couldn't fine pucch resource indicator %d in PUCCH resource set %d for %d UCI bits",pucch_resource,i,O_uci); + } + if (pucchresset->pucch_ResourceSetId == 1 && O_uci>2) { +#if (NR_RRC_VERSION >= MAKE_VERSION(16, 0, 0)) + N3 = pucchresset->maxPayloadSize!= NULL ? *pucchresset->maxPayloadSize : 1706; +#else + N3 = pucchresset->maxPayloadMinus1!= NULL ? *pucchresset->maxPayloadMinus1 : 1706; +#endif + if (N2O_uci) { + if (pucch_resource < n_list) + resource_id = pucchresset->resourceList.list.array[pucch_resource]; + else + AssertFatal(1==0,"Couldn't fine pucch resource indicator %d in PUCCH resource set %d for %d UCI bits",pucch_resource,i,O_uci); + } + else N2 = N3; + } + } + + AssertFatal(resource_id!=NULL,"Couldn-t find any matching PUCCH resource in the PUCCH resource sets"); + + AssertFatal(pucch_Config->resourceToAddModList!=NULL, + "PUCCH resourceToAddModList is null\n"); + + n_list = pucch_Config->resourceToAddModList->list.count; + AssertFatal(n_list>0,"PUCCH resourceToAddModList is empty\n"); + + // going through the list of PUCCH resources to find the one indexed by resource_id + for (int i=0; iresourceToAddModList->list.array[i]; + if (pucchres->pucch_ResourceId == *resource_id) { + res_found = 1; + pucch_pdu->prb_start = pucchres->startingPRB; + pucch_pdu->rnti = rnti; + // FIXME why there is only one frequency hopping flag + // what about inter slot frequency hopping? + pucch_pdu->freq_hop_flag = pucchres->intraSlotFrequencyHopping!= NULL ? 1 : 0; + pucch_pdu->second_hop_prb = pucchres->secondHopPRB!= NULL ? *pucchres->secondHopPRB : 0; + switch(pucchres->format.present) { + case NR_PUCCH_Resource__format_PR_format0 : + pucch_pdu->format_type = 0; + pucch_pdu->initial_cyclic_shift = pucchres->format.choice.format0->initialCyclicShift; + pucch_pdu->nr_of_symbols = pucchres->format.choice.format0->nrofSymbols; + pucch_pdu->start_symbol_index = pucchres->format.choice.format0->startingSymbolIndex; + pucch_pdu->sr_flag = O_sr; + break; + case NR_PUCCH_Resource__format_PR_format1 : + pucch_pdu->format_type = 1; + pucch_pdu->initial_cyclic_shift = pucchres->format.choice.format1->initialCyclicShift; + pucch_pdu->nr_of_symbols = pucchres->format.choice.format1->nrofSymbols; + pucch_pdu->start_symbol_index = pucchres->format.choice.format1->startingSymbolIndex; + pucch_pdu->time_domain_occ_idx = pucchres->format.choice.format1->timeDomainOCC; + pucch_pdu->sr_flag = O_sr; + break; + case NR_PUCCH_Resource__format_PR_format2 : + pucch_pdu->format_type = 2; + pucch_pdu->nr_of_symbols = pucchres->format.choice.format2->nrofSymbols; + pucch_pdu->start_symbol_index = pucchres->format.choice.format2->startingSymbolIndex; + pucch_pdu->data_scrambling_id = pusch_id!= NULL ? *pusch_id : *scc->physCellId; + pucch_pdu->dmrs_scrambling_id = id0!= NULL ? *id0 : *scc->physCellId; + pucch_pdu->prb_size = compute_pucch_prb_size(2,pucchres->format.choice.format2->nrofPRBs, + O_uci+O_sr,O_csi,pucch_Config->format2->choice.setup->maxCodeRate, + 2,pucchres->format.choice.format2->nrofSymbols,8); + pucch_pdu->bit_len_csi_part1 = O_csi; + break; + case NR_PUCCH_Resource__format_PR_format3 : + pucch_pdu->format_type = 3; + pucch_pdu->nr_of_symbols = pucchres->format.choice.format3->nrofSymbols; + pucch_pdu->start_symbol_index = pucchres->format.choice.format3->startingSymbolIndex; + pucch_pdu->data_scrambling_id = pusch_id!= NULL ? *pusch_id : *scc->physCellId; + if (pucch_Config->format3 == NULL) { + pucch_pdu->pi_2bpsk = 0; + pucch_pdu->add_dmrs_flag = 0; + } + else { + pucchfmt = pucch_Config->format3->choice.setup; + pucch_pdu->pi_2bpsk = pucchfmt->pi2BPSK!= NULL ? 1 : 0; + pucch_pdu->add_dmrs_flag = pucchfmt->additionalDMRS!= NULL ? 1 : 0; + } + int f3_dmrs_symbols; + if (pucchres->format.choice.format3->nrofSymbols==4) + f3_dmrs_symbols = 1<freq_hop_flag; + else { + if(pucchres->format.choice.format3->nrofSymbols<10) + f3_dmrs_symbols = 2; + else + f3_dmrs_symbols = 2<add_dmrs_flag; + } + pucch_pdu->prb_size = compute_pucch_prb_size(3,pucchres->format.choice.format3->nrofPRBs, + O_uci+O_sr,O_csi,pucch_Config->format3->choice.setup->maxCodeRate, + 2-pucch_pdu->pi_2bpsk,pucchres->format.choice.format3->nrofSymbols-f3_dmrs_symbols,12); + pucch_pdu->bit_len_csi_part1 = O_csi; + break; + case NR_PUCCH_Resource__format_PR_format4 : + pucch_pdu->format_type = 4; + pucch_pdu->nr_of_symbols = pucchres->format.choice.format4->nrofSymbols; + pucch_pdu->start_symbol_index = pucchres->format.choice.format4->startingSymbolIndex; + pucch_pdu->pre_dft_occ_len = pucchres->format.choice.format4->occ_Length; + pucch_pdu->pre_dft_occ_idx = pucchres->format.choice.format4->occ_Index; + pucch_pdu->data_scrambling_id = pusch_id!= NULL ? *pusch_id : *scc->physCellId; + if (pucch_Config->format3 == NULL) { + pucch_pdu->pi_2bpsk = 0; + pucch_pdu->add_dmrs_flag = 0; + } + else { + pucchfmt = pucch_Config->format3->choice.setup; + pucch_pdu->pi_2bpsk = pucchfmt->pi2BPSK!= NULL ? 1 : 0; + pucch_pdu->add_dmrs_flag = pucchfmt->additionalDMRS!= NULL ? 1 : 0; + } + pucch_pdu->bit_len_csi_part1 = O_csi; + break; + default : + AssertFatal(1==0,"Undefined PUCCH format \n"); + } + } + } + AssertFatal(res_found==1,"No PUCCH resource found corresponding to id %ld\n",*resource_id); + } + else { // this is the default PUCCH configuration, PUCCH format 0 or 1 + int rsetindex = *scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup->pucch_ResourceCommon; + int prboffset = r_pucch/default_pucch_csset[rsetindex]; + int prboffsetm8 = (r_pucch-8)/default_pucch_csset[rsetindex]; + pucch_pdu->prb_start = (r_pucch>>3)==0 ? + default_pucch_prboffset[rsetindex] + prboffset: + pucch_pdu->bwp_size-1-default_pucch_prboffset[rsetindex]-prboffsetm8; + pucch_pdu->rnti = rnti; + pucch_pdu->freq_hop_flag = 1; + pucch_pdu->second_hop_prb = (r_pucch>>3)==0? + pucch_pdu->bwp_size-1-default_pucch_prboffset[rsetindex]-prboffset: + default_pucch_prboffset[rsetindex] + prboffsetm8; + pucch_pdu->format_type = default_pucch_fmt[rsetindex]; + pucch_pdu->initial_cyclic_shift = r_pucch%default_pucch_csset[rsetindex]; + if (rsetindex==3||rsetindex==7||rsetindex==11) pucch_pdu->initial_cyclic_shift*=6; + else if (rsetindex==1||rsetindex==2) pucch_pdu->initial_cyclic_shift*=3; + else pucch_pdu->initial_cyclic_shift*=4; + pucch_pdu->nr_of_symbols = default_pucch_numbsymb[rsetindex]; + pucch_pdu->start_symbol_index = default_pucch_firstsymb[rsetindex]; + if (pucch_pdu->format_type == 1) pucch_pdu->time_domain_occ_idx = 0; // check this!! + pucch_pdu->sr_flag = O_sr; + } + } -void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, +void prepare_dci(const NR_CellGroupConfig_t *CellGroup, dci_pdu_rel15_t *dci_pdu_rel15, nr_dci_format_t format, int bwp_id) { - NR_BWP_Downlink_t *bwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; + NR_BWP_Downlink_t *bwp=CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; switch(format) { case NR_UL_DCI_FORMAT_0_1: // format indicator dci_pdu_rel15->format_indicator = 0; // carrier indicator - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) + if (CellGroup->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) AssertFatal(1==0,"Cross Carrier Scheduling Config currently not supported\n"); // supplementary uplink - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->supplementaryUplink != NULL) + if (CellGroup->spCellConfig->spCellConfigDedicated->supplementaryUplink != NULL) AssertFatal(1==0,"Supplementary Uplink currently not supported\n"); // SRS request dci_pdu_rel15->srs_request.val = 0; @@ -956,7 +1002,7 @@ void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, // format indicator dci_pdu_rel15->format_indicator = 1; // carrier indicator - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) + if (CellGroup->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig != NULL) AssertFatal(1==0,"Cross Carrier Scheduling Config currently not supported\n"); //vrb to prb mapping if (bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver==NULL) @@ -978,8 +1024,8 @@ void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, if (bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI != NULL) AssertFatal(1==0,"TCI in DCI currently not supported\n"); //srs resource set - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching!=NULL) { - NR_SRS_CarrierSwitching_t *cs = secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching->choice.setup; + if (CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching!=NULL) { + NR_SRS_CarrierSwitching_t *cs = CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching->choice.setup; if (cs->srs_TPC_PDCCH_Group!=NULL){ switch(cs->srs_TPC_PDCCH_Group->present) { case NR_SRS_CarrierSwitching__srs_TPC_PDCCH_Group_PR_NOTHING: @@ -999,7 +1045,7 @@ void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, else dci_pdu_rel15->srs_request.val = 0; // CBGTI and CBGFI - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) + if (CellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->codeBlockGroupTransmission != NULL) AssertFatal(1==0,"CBG transmission currently not supported\n"); break; default : @@ -1009,7 +1055,7 @@ void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, - const NR_CellGroupConfig_t *secondaryCellGroup, + const NR_CellGroupConfig_t *CellGroup, nfapi_nr_dl_dci_pdu_t *pdcch_dci_pdu, dci_pdu_rel15_t *dci_pdu_rel15, int dci_format, @@ -1019,12 +1065,12 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, uint8_t fsize = 0, pos = 0; uint64_t *dci_pdu = (uint64_t *)pdcch_dci_pdu->Payload; - int dci_size = nr_dci_size(scc, secondaryCellGroup, dci_pdu_rel15, dci_format, rnti_type, N_RB, bwp_id); + int dci_size = nr_dci_size(scc->uplinkConfigCommon->initialUplinkBWP, CellGroup, dci_pdu_rel15, dci_format, rnti_type, N_RB, bwp_id); pdcch_dci_pdu->PayloadSizeBits = dci_size; AssertFatal(dci_size <= 64, "DCI sizes above 64 bits not yet supported"); if (dci_format == NR_DL_DCI_FORMAT_1_1 || dci_format == NR_UL_DCI_FORMAT_0_1) - prepare_dci(secondaryCellGroup, dci_pdu_rel15, dci_format, bwp_id); + prepare_dci(CellGroup, dci_pdu_rel15, dci_format, bwp_id); /// Payload generation switch (dci_format) { @@ -1035,7 +1081,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)); pos = fsize; *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << fsize) - 1)) << (dci_size - pos)); - LOG_D(MAC, + LOG_D(NR_MAC, "frequency-domain assignment %d (%d bits) N_RB_BWP %d=> %d (0x%lx)\n", dci_pdu_rel15->frequency_domain_assignment.val, fsize, @@ -1045,7 +1091,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, // Time domain assignment pos += 4; *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val & 0xf) << (dci_size - pos)); - LOG_D(MAC, + LOG_D(NR_MAC, "time-domain assignment %d (3 bits)=> %d (0x%lx)\n", dci_pdu_rel15->time_domain_assignment.val, dci_size - pos, @@ -1053,7 +1099,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, // VRB to PRB mapping pos++; *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val & 0x1) << (dci_size - pos); - LOG_D(MAC, + LOG_D(NR_MAC, "vrb to prb mapping %d (1 bits)=> %d (0x%lx)\n", dci_pdu_rel15->vrb_to_prb_mapping.val, dci_size - pos, @@ -1062,21 +1108,21 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, pos += 5; *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos); #ifdef DEBUG_FILL_DCI - LOG_I(MAC, "mcs %d (5 bits)=> %d (0x%lx)\n", dci_pdu_rel15->mcs, dci_size - pos, *dci_pdu); + LOG_I(NR_MAC, "mcs %d (5 bits)=> %d (0x%lx)\n", dci_pdu_rel15->mcs, dci_size - pos, *dci_pdu); #endif // TB scaling pos += 2; *dci_pdu |= ((uint64_t)dci_pdu_rel15->tb_scaling & 0x3) << (dci_size - pos); #ifdef DEBUG_FILL_DCI - LOG_I(MAC, "tb_scaling %d (2 bits)=> %d (0x%lx)\n", dci_pdu_rel15->tb_scaling, dci_size - pos, *dci_pdu); + LOG_I(NR_MAC, "tb_scaling %d (2 bits)=> %d (0x%lx)\n", dci_pdu_rel15->tb_scaling, dci_size - pos, *dci_pdu); #endif break; case NR_RNTI_C: // indicating a DL DCI format 1bit pos++; - *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 1) << (dci_size - pos); - LOG_D(MAC, + *dci_pdu |= ((uint64_t)1) << (dci_size - pos); + LOG_D(NR_MAC, "Format indicator %d (%d bits) N_RB_BWP %d => %d (0x%lx)\n", dci_pdu_rel15->format_indicator, 1, @@ -1087,7 +1133,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)); pos += fsize; *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << fsize) - 1)) << (dci_size - pos)); - LOG_D(MAC, + LOG_D(NR_MAC, "Freq domain assignment %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->frequency_domain_assignment.val, fsize, @@ -1117,7 +1163,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, // Time domain assignment 4bit pos += 4; *dci_pdu |= ((dci_pdu_rel15->time_domain_assignment.val & 0xf) << (dci_size - pos)); - LOG_D(MAC, + LOG_D(NR_MAC, "Time domain assignment %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->time_domain_assignment.val, 4, @@ -1126,7 +1172,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, // VRB to PRB mapping 1bit pos++; *dci_pdu |= (dci_pdu_rel15->vrb_to_prb_mapping.val & 1) << (dci_size - pos); - LOG_D(MAC, + LOG_D(NR_MAC, "VRB to PRB %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->vrb_to_prb_mapping.val, 1, @@ -1135,31 +1181,31 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, // MCS 5bit //bit over 32, so dci_pdu ++ pos += 5; *dci_pdu |= (dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos); - LOG_D(MAC, "MCS %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->mcs, 5, dci_size - pos, *dci_pdu); + LOG_D(NR_MAC, "MCS %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->mcs, 5, dci_size - pos, *dci_pdu); // New data indicator 1bit pos++; *dci_pdu |= (dci_pdu_rel15->ndi & 1) << (dci_size - pos); - LOG_D(MAC, "NDI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->ndi, 1, dci_size - pos, *dci_pdu); + LOG_D(NR_MAC, "NDI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->ndi, 1, dci_size - pos, *dci_pdu); // Redundancy version 2bit pos += 2; *dci_pdu |= (dci_pdu_rel15->rv & 0x3) << (dci_size - pos); - LOG_D(MAC, "RV %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->rv, 2, dci_size - pos, *dci_pdu); + LOG_D(NR_MAC, "RV %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->rv, 2, dci_size - pos, *dci_pdu); // HARQ process number 4bit pos += 4; *dci_pdu |= ((dci_pdu_rel15->harq_pid & 0xf) << (dci_size - pos)); - LOG_D(MAC, "HARQ_PID %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->harq_pid, 4, dci_size - pos, *dci_pdu); + LOG_D(NR_MAC, "HARQ_PID %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->harq_pid, 4, dci_size - pos, *dci_pdu); // Downlink assignment index 2bit pos += 2; *dci_pdu |= ((dci_pdu_rel15->dai[0].val & 3) << (dci_size - pos)); - LOG_D(MAC, "DAI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->dai[0].val, 2, dci_size - pos, *dci_pdu); + LOG_D(NR_MAC, "DAI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->dai[0].val, 2, dci_size - pos, *dci_pdu); // TPC command for scheduled PUCCH 2bit pos += 2; *dci_pdu |= ((dci_pdu_rel15->tpc & 3) << (dci_size - pos)); - LOG_D(MAC, "TPC %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->tpc, 2, dci_size - pos, *dci_pdu); + LOG_D(NR_MAC, "TPC %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->tpc, 2, dci_size - pos, *dci_pdu); // PUCCH resource indicator 3bit pos += 3; *dci_pdu |= ((dci_pdu_rel15->pucch_resource_indicator & 0x7) << (dci_size - pos)); - LOG_D(MAC, + LOG_D(NR_MAC, "PUCCH RI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->pucch_resource_indicator, 3, @@ -1168,7 +1214,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, // PDSCH-to-HARQ_feedback timing indicator 3bit pos += 3; *dci_pdu |= ((dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val & 0x7) << (dci_size - pos)); - LOG_D(MAC, + LOG_D(NR_MAC, "PDSCH to HARQ TI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val, 3, @@ -1287,31 +1333,34 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, case NR_UL_DCI_FORMAT_0_0: switch (rnti_type) { case NR_RNTI_C: - // indicating a DL DCI format 1bit - *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 1) << (dci_size - pos++); + // indicating a UL DCI format 1bit + pos=1; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 1) << (dci_size - pos); // Freq domain assignment max 16 bit fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)); - for (int i = 0; i < fsize; i++) - *dci_pdu |= ((dci_pdu_rel15->frequency_domain_assignment.val >> (fsize - i - 1)) & 1) << (dci_size - pos++); + pos+=fsize; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << fsize) - 1)) << (dci_size - pos); // Time domain assignment 4bit - for (int i = 0; i < 4; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val >> (3 - i)) & 1) << (dci_size - pos++); + pos += 4; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->time_domain_assignment.val & ((1 << 4) - 1)) << (dci_size - pos); // Frequency hopping flag – 1 bit - *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val & 1) << (dci_size - pos++); + pos++; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val & 1) << (dci_size - pos); // MCS 5 bit - for (int i = 0; i < 5; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs >> (4 - i)) & 1) << (dci_size - pos++); + pos+=5; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos); // New data indicator 1bit - *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 1) << (dci_size - pos++); + pos++; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 1) << (dci_size - pos); // Redundancy version 2bit - for (int i = 0; i < 2; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv >> (1 - i)) & 1) << (dci_size - pos++); + pos+=2; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv & 0x3) << (dci_size - pos); // HARQ process number 4bit - for (int i = 0; i < 4; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->harq_pid >> (3 - i)) & 1) << (dci_size - pos++); + pos+=4; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->harq_pid & 0xf) << (dci_size - pos); // TPC command for scheduled PUSCH – 2 bits - for (int i = 0; i < 2; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->tpc >> (1 - i)) & 1) << (dci_size - pos++); + pos+=2; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->tpc & 0x3) << (dci_size - pos); // Padding bits for (int a = pos; a < 32; a++) *dci_pdu |= ((uint64_t)dci_pdu_rel15->padding & 1) << (dci_size - pos++); @@ -1321,6 +1370,19 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, *dci_pdu |= ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos++); */ + + LOG_D(NR_MAC,"N_RB = %i\n", N_RB); + LOG_D(NR_MAC,"dci_size = %i\n", dci_size); + LOG_D(NR_MAC,"fsize = %i\n", fsize); + LOG_D(NR_MAC,"dci_pdu_rel15->frequency_domain_assignment.val = %i\n", dci_pdu_rel15->frequency_domain_assignment.val); + LOG_D(NR_MAC,"dci_pdu_rel15->time_domain_assignment.val = %i\n", dci_pdu_rel15->time_domain_assignment.val); + LOG_D(NR_MAC,"dci_pdu_rel15->frequency_hopping_flag.val = %i\n", dci_pdu_rel15->frequency_hopping_flag.val); + LOG_D(NR_MAC,"dci_pdu_rel15->mcs = %i\n", dci_pdu_rel15->mcs); + LOG_D(NR_MAC,"dci_pdu_rel15->ndi = %i\n", dci_pdu_rel15->ndi); + LOG_D(NR_MAC,"dci_pdu_rel15->rv = %i\n", dci_pdu_rel15->rv); + LOG_D(NR_MAC,"dci_pdu_rel15->harq_pid = %i\n", dci_pdu_rel15->harq_pid); + LOG_D(NR_MAC,"dci_pdu_rel15->tpc = %i\n", dci_pdu_rel15->tpc); + LOG_D(NR_MAC,"dci_pdu_rel15->padding = %i\n", dci_pdu_rel15->padding); break; case NFAPI_NR_RNTI_TC: @@ -1524,7 +1586,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, pos += 1; *dci_pdu |= ((uint64_t)dci_pdu_rel15->dmrs_sequence_initialization.val & 0x1) << (dci_size - pos); } - LOG_D(MAC, "DCI has %d bits and the payload is %lx\n", dci_size, *dci_pdu); + LOG_D(NR_MAC, "DCI has %d bits and the payload is %lx\n", dci_size, *dci_pdu); } int get_spf(nfapi_nr_config_request_scf_t *cfg) { @@ -1563,7 +1625,7 @@ int extract_length(int startSymbolAndLength) { void dump_nr_list(NR_list_t *listP) { for (int j = listP->head; j >= 0; j = listP->next[j]) - LOG_T(MAC, "NR list node %d => %d\n", j, listP->next[j]); + LOG_T(NR_MAC, "NR list node %d => %d\n", j, listP->next[j]); } /* @@ -1699,7 +1761,7 @@ int find_nr_RA_id(module_id_t mod_idP, int CC_idP, rnti_t rntiP) { RA_t *ra = (RA_t *) &RC.nrmac[mod_idP]->common_channels[CC_idP].ra[0]; for (RA_id = 0; RA_id < NB_RA_PROC_MAX; RA_id++) { - LOG_D(MAC, "Checking RA_id %d for %x : state %d\n", + LOG_D(NR_MAC, "Checking RA_id %d for %x : state %d\n", RA_id, rntiP, ra[RA_id].state); @@ -1750,67 +1812,71 @@ int get_ul_bwp_id(const NR_ServingCellConfig_t *servingCellConfig) } //------------------------------------------------------------------------------ -int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *secondaryCellGroup) +int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellGroup) { + NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_idP]->common_channels[0].ServingCellConfigCommon; NR_UE_info_t *UE_info = &RC.nrmac[mod_idP]->UE_info; - LOG_I(MAC, "[gNB %d] Adding UE with rnti %x (num_UEs %d)\n", + LOG_I(NR_MAC, "[gNB %d] Adding UE with rnti %x (num_UEs %d)\n", mod_idP, rntiP, UE_info->num_UEs); dump_nr_list(&UE_info->list); for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) { - if (UE_info->active[i]) + if (UE_info->active[i]) { + LOG_D(NR_MAC,"UE %x is active, skipping\n",rntiP); continue; - + } int UE_id = i; UE_info->num_UEs++; UE_info->active[UE_id] = true; + if (CellGroup) UE_info->Msg4_ACKed[UE_id] = true; + else UE_info->Msg4_ACKed[UE_id] = false; UE_info->rnti[UE_id] = rntiP; - UE_info->secondaryCellGroup[UE_id] = secondaryCellGroup; + UE_info->CellGroup[UE_id] = CellGroup; add_nr_list(&UE_info->list, UE_id); memset(&UE_info->mac_stats[UE_id], 0, sizeof(NR_mac_stats_t)); set_Y(UE_info->Y[UE_id], rntiP); - compute_csi_bitlen (secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup, UE_info, UE_id, mod_idP); + if (CellGroup && CellGroup->spCellConfig && CellGroup->spCellConfig && CellGroup->spCellConfig->spCellConfigDedicated) + compute_csi_bitlen (CellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup, UE_info, UE_id, mod_idP); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; memset(sched_ctrl, 0, sizeof(*sched_ctrl)); + sched_ctrl->lcid_mask = 0; sched_ctrl->ta_frame = 0; sched_ctrl->ta_update = 31; sched_ctrl->ta_apply = false; + sched_ctrl->ul_rssi = 0; + sched_ctrl->pucch_consecutive_dtx_cnt = 0; + sched_ctrl->pusch_consecutive_dtx_cnt = 0; + sched_ctrl->ul_failure = 0; /* set illegal time domain allocation to force recomputation of all fields */ sched_ctrl->pdsch_semi_static.time_domain_allocation = -1; sched_ctrl->pusch_semi_static.time_domain_allocation = -1; - const NR_ServingCellConfig_t *servingCellConfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated; + const NR_ServingCellConfig_t *servingCellConfig = CellGroup ? CellGroup->spCellConfig->spCellConfigDedicated : NULL; /* Set default BWPs */ - const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig->downlinkBWP_ToAddModList; - const int bwp_id = get_dl_bwp_id(servingCellConfig); - AssertFatal(bwp_id > 0 && bwp_id <= bwpList->list.count, - "%s(): illegal bwp_id %d (max %d)!\n", - __func__, - bwp_id, - bwpList->list.count); - sched_ctrl->active_bwp = bwpList->list.array[bwp_id - 1]; - const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; - sched_ctrl->search_space = get_searchspace(sched_ctrl->active_bwp, target_ss); - sched_ctrl->coreset = get_coreset(sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */); - - const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList; - const int ubwp_id = get_ul_bwp_id(servingCellConfig); - AssertFatal(ubwp_id > 0 && ubwp_id <= ubwpList->list.count, - "%s(): illegal ubwp_id %d (max %d)!\n", - __func__, - ubwp_id, - ubwpList->list.count); - sched_ctrl->active_ubwp = ubwpList->list.array[ubwp_id - 1]; + const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; + if (bwpList) AssertFatal(bwpList->list.count <= 4, + "downlinkBWP_ToAddModList has %d BWP!\n", + bwpList->list.count); + const int bwp_id = *servingCellConfig->firstActiveDownlinkBWP_Id; + sched_ctrl->active_bwp = bwpList ? bwpList->list.array[bwp_id - 1] : NULL; + const int target_ss = sched_ctrl->active_bwp ? NR_SearchSpace__searchSpaceType_PR_ue_Specific : NR_SearchSpace__searchSpaceType_PR_common; + sched_ctrl->search_space = get_searchspace(scc, sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Dedicated : NULL, target_ss); + sched_ctrl->coreset = get_coreset(scc, sched_ctrl->active_bwp, sched_ctrl->search_space, target_ss); + const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; + if (ubwpList) AssertFatal(ubwpList->list.count <= 4, + "uplinkBWP_ToAddModList has %d BWP!\n", + ubwpList->list.count); + sched_ctrl->active_ubwp = ubwpList ? ubwpList->list.array[bwp_id - 1] : NULL; /* get Number of HARQ processes for this UE */ - AssertFatal(servingCellConfig->pdsch_ServingCellConfig->present == NR_SetupRelease_PDSCH_ServingCellConfig_PR_setup, - "no pdsch-ServingCellConfig found for UE %d\n", - UE_id); - const NR_PDSCH_ServingCellConfig_t *pdsch = servingCellConfig->pdsch_ServingCellConfig->choice.setup; - const int nrofHARQ = pdsch->nrofHARQ_ProcessesForPDSCH ? - get_nrofHARQ_ProcessesForPDSCH(*pdsch->nrofHARQ_ProcessesForPDSCH) : 8; + if (servingCellConfig) AssertFatal(servingCellConfig->pdsch_ServingCellConfig->present == NR_SetupRelease_PDSCH_ServingCellConfig_PR_setup, + "no pdsch-ServingCellConfig found for UE %d\n", + UE_id); + const NR_PDSCH_ServingCellConfig_t *pdsch = servingCellConfig ? servingCellConfig->pdsch_ServingCellConfig->choice.setup : NULL; + const int nrofHARQ = pdsch ? (pdsch->nrofHARQ_ProcessesForPDSCH ? + get_nrofHARQ_ProcessesForPDSCH(*pdsch->nrofHARQ_ProcessesForPDSCH) : 8) : 8; // add all available DL HARQ processes for this UE create_nr_list(&sched_ctrl->available_dl_harq, nrofHARQ); for (int harq = 0; harq < nrofHARQ; harq++) @@ -1824,7 +1890,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *secon add_tail_nr_list(&sched_ctrl->available_ul_harq, harq); create_nr_list(&sched_ctrl->feedback_ul_harq, 16); create_nr_list(&sched_ctrl->retrans_ul_harq, 16); - LOG_I(MAC, "[gNB %d] Add NR UE_id %d : rnti %x\n", + LOG_I(NR_MAC, "[gNB %d] Add NR UE_id %d : rnti %x\n", mod_idP, UE_id, rntiP); @@ -1833,7 +1899,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *secon } // printf("MAC: cannot add new UE for rnti %x\n", rntiP); - LOG_E(MAC, "error in add_new_ue(), could not find space in UE_info, Dumping UE list\n"); + LOG_E(NR_MAC, "error in add_new_ue(), could not find space in UE_info, Dumping UE list\n"); dump_nr_list(&UE_info->list); return -1; } @@ -1871,7 +1937,7 @@ void mac_remove_nr_ue(module_id_t mod_id, rnti_t rnti) destroy_nr_list(&sched_ctrl->available_ul_harq); destroy_nr_list(&sched_ctrl->feedback_ul_harq); destroy_nr_list(&sched_ctrl->retrans_ul_harq); - LOG_I(MAC, "[gNB %d] Remove NR UE_id %d : rnti %x\n", + LOG_I(NR_MAC, "[gNB %d] Remove NR UE_id %d : rnti %x\n", mod_id, UE_id, rnti); @@ -1890,7 +1956,7 @@ void mac_remove_nr_ue(module_id_t mod_id, rnti_t rnti) NR_COMMON_channels_t *cc = &RC.nrmac[mod_id]->common_channels[cc_id]; for (i = 0; i < NR_NB_RA_PROC_MAX; i++) { if (cc->ra[i].rnti == rnti) { - LOG_D(MAC, "free RA process %d for rnti %d\n", i, rnti); + LOG_D(NR_MAC, "free RA process %d for rnti %d\n", i, rnti); /* is it enough? */ cc->ra[i].cfra = false; cc->ra[i].rnti = 0; @@ -1912,11 +1978,11 @@ void nr_mac_remove_ra_rnti(module_id_t mod_id, rnti_t rnti) { uint8_t nr_get_tpc(int target, uint8_t cqi, int incr) { // al values passed to this function are x10 - int snrx10 = (cqi*5) - 640; if (snrx10 > target + incr) return 0; // decrease 1dB if (snrx10 < target - incr) return 2; // increase 1dB if (snrx10 < target - (3*incr)) return 3; // increase 3dB + LOG_D(NR_MAC,"tpc : target %d, snrx10 %d\n",target,snrx10); return 1; // no change } @@ -1928,11 +1994,16 @@ void get_pdsch_to_harq_feedback(int Mod_idP, int bwp_id=1; NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; - NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id]; - NR_BWP_Downlink_t *bwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; - NR_BWP_Uplink_t *ubwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_id-1]; + NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; + NR_BWP_Downlink_t *bwp=NULL; + NR_BWP_Uplink_t *ubwp=NULL; - NR_SearchSpace_t *ss; + if (CellGroup && CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) + bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; + if (CellGroup && CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList) + ubwp = CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_id-1]; + + NR_SearchSpace_t *ss=NULL; // common search type uses DCI format 1_0 if (ss_type == NR_SearchSpace__searchSpaceType_PR_common) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index f53f79ba49..c7ee3fcff2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -26,6 +26,7 @@ * \company Eurecom */ +#include #include "LAYER2/MAC/mac.h" #include "NR_MAC_gNB/nr_mac_gNB.h" #include "NR_MAC_COMMON/nr_mac_extern.h" @@ -72,12 +73,14 @@ void nr_fill_nfapi_pucch(module_id_t mod_id, NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels->ServingCellConfigCommon; nr_configure_pucch(pucch_pdu, scc, + UE_info->CellGroup[UE_id], UE_info->UE_sched_ctrl[UE_id].active_ubwp, UE_info->rnti[UE_id], pucch->resource_indicator, pucch->csi_bits, pucch->dai_c, - pucch->sr_flag); + pucch->sr_flag, + pucch->r_pucch); } #define MIN_RSRP_VALUE -141 @@ -129,6 +132,7 @@ void nr_schedule_pucch(int Mod_idP, for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->ul_failure==1 && get_softmodem_params()->phy_test==0) continue; const int n = sizeof(sched_ctrl->sched_pucch) / sizeof(*sched_ctrl->sched_pucch); for (int i = 0; i < n; i++) { NR_sched_pucch_t *curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[i]; @@ -139,7 +143,7 @@ void nr_schedule_pucch(int Mod_idP, || frameP != curr_pucch->frame || slotP != curr_pucch->ul_slot) continue; - + LOG_D(NR_MAC,"Scheduling PUCCH[%d] RX for UE %d in %d.%d O_ack %d\n",i,UE_id,curr_pucch->frame,curr_pucch->ul_slot,O_ack); nr_fill_nfapi_pucch(Mod_idP, frameP, slotP, curr_pucch, UE_id); memset(curr_pucch, 0, sizeof(*curr_pucch)); } @@ -476,9 +480,11 @@ void nr_csi_meas_reporting(int Mod_idP, NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; NR_list_t *UE_list = &UE_info->list; for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { - const NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id]; + const NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const NR_CSI_MeasConfig_t *csi_measconfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; + if (!CellGroup || !CellGroup->spCellConfig || !CellGroup->spCellConfig->spCellConfigDedicated || + !CellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig) continue; + const NR_CSI_MeasConfig_t *csi_measconfig = CellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; AssertFatal(csi_measconfig->csi_ReportConfigToAddModList->list.count > 0, "NO CSI report configuration available"); NR_PUCCH_Config_t *pucch_Config = sched_ctrl->active_ubwp->bwp_Dedicated->pucch_Config->choice.setup; @@ -586,13 +592,13 @@ static void handle_dl_harq(module_id_t mod_id, int checkTargetSSBInFirst64TCIStates_pdschConfig(int ssb_index_t, int Mod_idP, int UE_id) { NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; - NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id] ; - int nb_tci_states = secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.count; + NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id] ; + int nb_tci_states = CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.count; NR_TCI_State_t *tci =NULL; int i; for(i=0; ispCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.array[i]; + tci = (NR_TCI_State_t *)CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.array[i]; if(tci != NULL) { if(tci->qcl_Type1.referenceSignal.present == NR_QCL_Info__referenceSignal_PR_ssb) { @@ -613,19 +619,19 @@ int checkTargetSSBInFirst64TCIStates_pdschConfig(int ssb_index_t, int Mod_idP, i int checkTargetSSBInTCIStates_pdcchConfig(int ssb_index_t, int Mod_idP, int UE_id) { NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; - NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id] ; - int nb_tci_states = secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.count; + NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id] ; + int nb_tci_states = CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.count; NR_TCI_State_t *tci =NULL; NR_TCI_StateId_t *tci_id = NULL; int bwp_id = 1; - NR_BWP_Downlink_t *bwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; + NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; NR_ControlResourceSet_t *coreset = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]; int i; int flag = 0; int tci_stateID = -1; for(i=0; ispCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.array[i]; + tci = (NR_TCI_State_t *)CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.array[i]; if(tci != NULL && tci->qcl_Type1.referenceSignal.present == NR_QCL_Info__referenceSignal_PR_ssb) { if(tci->qcl_Type1.referenceSignal.choice.ssb == ssb_index_t) { @@ -687,10 +693,10 @@ void tci_handling(module_id_t Mod_idP, int UE_id, frame_t frame, slot_t slot) { uint8_t idx = 0; int bwp_id = 1; NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; - NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id]; - NR_BWP_Downlink_t *bwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; + NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; + NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; //bwp indicator - int n_dl_bwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; + int n_dl_bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count; uint8_t nr_ssbri_cri = 0; uint8_t nb_of_csi_ssb_report = UE_info->csi_report_template[UE_id][cqi_idx].nb_of_csi_ssb_report; int better_rsrp_reported = -140-(-0); /*minimum_measured_RSRP_value - minimum_differntail_RSRP_value*///considering the minimum RSRP value as better RSRP initially @@ -1049,7 +1055,12 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, LOG_E(MAC, "%s(): unknown RNTI %04x in PUCCH UCI\n", __func__, uci_234->rnti); return; } - NR_CSI_MeasConfig_t *csi_MeasConfig = RC.nrmac[mod_id]->UE_info.secondaryCellGroup[UE_id]->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; + AssertFatal(RC.nrmac[mod_id]->UE_info.CellGroup[UE_id],"Cellgroup is null for UE %d/%x\n",UE_id,uci_234->rnti); + AssertFatal(RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig, "Cellgroup->spCellConfig is null for UE %d/%x\n",UE_id,uci_234->rnti); + AssertFatal(RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated, "Cellgroup->spCellConfig->spCellConfigDedicated is null for UE %d/%x\n",UE_id,uci_234->rnti); + if ( RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->csi_MeasConfig==NULL) return; + + NR_CSI_MeasConfig_t *csi_MeasConfig = RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; NR_UE_info_t *UE_info = &RC.nrmac[mod_id]->UE_info; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; @@ -1092,7 +1103,8 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, int nr_acknack_scheduling(int mod_id, int UE_id, frame_t frame, - sub_frame_t slot) + sub_frame_t slot, + int r_pucch) { const NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels->ServingCellConfigCommon; const int n_slots_frame = nr_slots_per_frame[*scc->ssbSubcarrierSpacing]; @@ -1118,6 +1130,7 @@ int nr_acknack_scheduling(int mod_id, * * each UE has dedicated PUCCH Format 0 resources, and we use index 0! */ NR_UE_sched_ctrl_t *sched_ctrl = &RC.nrmac[mod_id]->UE_info.UE_sched_ctrl[UE_id]; NR_sched_pucch_t *pucch = &sched_ctrl->sched_pucch[0]; + pucch->r_pucch=r_pucch; AssertFatal(pucch->csi_bits == 0, "%s(): csi_bits %d in sched_pucch[0]\n", __func__, @@ -1136,6 +1149,7 @@ int nr_acknack_scheduling(int mod_id, csi_pucch = &sched_ctrl->sched_pucch[1]; // skip the CSI PUCCH if it is present and if in the next frame/slot // and if we don't multiplex + csi_pucch->r_pucch=-1; if (csi_pucch->csi_bits > 0 && csi_pucch->frame == pucch->frame && csi_pucch->ul_slot == pucch->ul_slot @@ -1147,6 +1161,7 @@ int nr_acknack_scheduling(int mod_id, } } + LOG_D(MAC,"1. DL slot %d, UL_ACK %d\n",slot,pucch->ul_slot); /* if the UE's next PUCCH occasion is after the possible UL slots (within the * same frame) or wrapped around to the next frame, then we assume there is * no possible PUCCH allocation anymore */ @@ -1155,8 +1170,8 @@ int nr_acknack_scheduling(int mod_id, || (pucch->frame == frame + 1)) return -1; - // this is hardcoded for now as ue specific - NR_SearchSpace__searchSpaceType_PR ss_type = NR_SearchSpace__searchSpaceType_PR_ue_Specific; + // this is hardcoded for now as ue specific only if we are not on the initialBWP (to be fixed to allow ue_Specific also on initialBWP + NR_SearchSpace__searchSpaceType_PR ss_type = sched_ctrl->active_bwp ? NR_SearchSpace__searchSpaceType_PR_ue_Specific: NR_SearchSpace__searchSpaceType_PR_common; uint8_t pdsch_to_harq_feedback[8]; get_pdsch_to_harq_feedback(mod_id, UE_id, ss_type, pdsch_to_harq_feedback); @@ -1181,7 +1196,7 @@ int nr_acknack_scheduling(int mod_id, memset(pucch, 0, sizeof(*pucch)); pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f; pucch->ul_slot = (s + 1) % n_slots_frame; - return nr_acknack_scheduling(mod_id, UE_id, frame, slot); + return nr_acknack_scheduling(mod_id, UE_id, frame, slot, pucch->r_pucch); } pucch->timing_indicator = i; @@ -1213,6 +1228,8 @@ int nr_acknack_scheduling(int mod_id, // Find the right timing_indicator value. int i = 0; while (i < 8) { + LOG_D(MAC,"pdsch_to_harq_feedback[%d] = %d (pucch->ul_slot %d - slot %d)\n", + i,pdsch_to_harq_feedback[i],pucch->ul_slot,slot); if (pdsch_to_harq_feedback[i] == pucch->ul_slot - slot) break; ++i; @@ -1247,7 +1264,7 @@ int nr_acknack_scheduling(int mod_id, memset(pucch, 0, sizeof(*pucch)); pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f; pucch->ul_slot = (s + 1) % n_slots_frame; - return nr_acknack_scheduling(mod_id, UE_id, frame, slot); + return nr_acknack_scheduling(mod_id, UE_id, frame, slot, -1); } // multiplexing harq and csi in a pucch else { @@ -1259,13 +1276,26 @@ int nr_acknack_scheduling(int mod_id, pucch->timing_indicator = i; // index in the list of timing indicators + LOG_D(MAC,"2. DL slot %d, UL_ACK %d (index %d)\n",slot,pucch->ul_slot,i); + pucch->dai_c++; pucch->resource_indicator = 0; // each UE has dedicated PUCCH resources + NR_PUCCH_Config_t *pucch_Config = NULL; + if (sched_ctrl->active_ubwp) { + pucch_Config = sched_ctrl->active_ubwp->bwp_Dedicated->pucch_Config->choice.setup; + } else if (RC.nrmac[mod_id]->UE_info.CellGroup[UE_id] && + RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig && + RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated && + RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig && + RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP && + RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup) { + pucch_Config = RC.nrmac[mod_id]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup; + } + /* verify that at that slot and symbol, resources are free. We only do this * for initialCyclicShift 0 (we assume it always has that one), so other * initialCyclicShifts can overlap with ICS 0!*/ - const NR_PUCCH_Config_t *pucch_Config = sched_ctrl->active_ubwp->bwp_Dedicated->pucch_Config->choice.setup; const NR_PUCCH_Resource_t *resource = pucch_Config->resourceToAddModList->list.array[pucch->resource_indicator]; DevAssert(resource->format.present == NR_PUCCH_Resource__format_PR_format0); if (resource->format.choice.format0->initialCyclicShift == 0) { @@ -1417,7 +1447,19 @@ void nr_sr_reporting(int Mod_idP, frame_t SFN, sub_frame_t slot) NR_list_t *UE_list = &UE_info->list; for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - NR_PUCCH_Config_t *pucch_Config = sched_ctrl->active_ubwp->bwp_Dedicated->pucch_Config->choice.setup; + + NR_PUCCH_Config_t *pucch_Config = NULL; + if (sched_ctrl->active_ubwp) { + pucch_Config = sched_ctrl->active_ubwp->bwp_Dedicated->pucch_Config->choice.setup; + } else if (RC.nrmac[Mod_idP]->UE_info.CellGroup[UE_id] && + RC.nrmac[Mod_idP]->UE_info.CellGroup[UE_id]->spCellConfig && + RC.nrmac[Mod_idP]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated && + RC.nrmac[Mod_idP]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig && + RC.nrmac[Mod_idP]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP && + RC.nrmac[Mod_idP]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup) { + pucch_Config = RC.nrmac[Mod_idP]->UE_info.CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup; + } + AssertFatal(pucch_Config->schedulingRequestResourceToAddModList->list.count>0,"NO SR configuration available"); for (int SR_resource_id =0; SR_resource_id < pucch_Config->schedulingRequestResourceToAddModList->list.count;SR_resource_id++) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 323baa48a7..3effe300ab 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -32,6 +32,8 @@ #include "LAYER2/NR_MAC_gNB/mac_proto.h" #include "executables/softmodem-common.h" #include "common/utils/nr/nr_common.h" +#include + //38.321 Table 6.1.3.1-1 const uint32_t NR_SHORT_BSR_TABLE[32] = { @@ -69,7 +71,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub return; /* there is a mixed slot only when in TDD */ - const NR_ServingCellConfigCommon_t *scc = nrmac->common_channels->ServingCellConfigCommon; + NR_ServingCellConfigCommon_t *scc = nrmac->common_channels->ServingCellConfigCommon; const int mu = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; const NR_TDD_UL_DL_Pattern_t *tdd = scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL; @@ -117,7 +119,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; AssertFatal(tdaList->list.count >= 3, "need to have at least three TDAs for UL slots\n"); const NR_PUSCH_TimeDomainResourceAllocation_t *tdaP_UL = tdaList->list.array[0]; - const int k2 = get_K2(ubwp, /* tda = */ 0, mu); + const int k2 = get_K2(scc, (NR_BWP_Uplink_t*)ubwp,0, mu); int start, len; SLIV2SL(tdaP_UL->startSymbolAndLength, &start, &len); const uint16_t symb_tda = ((1 << len) - 1) << start; @@ -129,10 +131,10 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub // get largest time domain allocation (TDA) for UL slot and UL in mixed slot int tdaMi = -1; const NR_PUSCH_TimeDomainResourceAllocation_t *tdaP_Mi = tdaList->list.array[1]; - AssertFatal(k2 == get_K2(ubwp, /* tda = */ 1, mu), + AssertFatal(k2 == get_K2(scc, (NR_BWP_Uplink_t*)ubwp, 1, mu), "scheduler cannot handle different k2 for UL slot (%d) and UL Mixed slot (%ld)\n", k2, - get_K2(ubwp, /* tda = */ 1, mu)); + get_K2(scc, (NR_BWP_Uplink_t*)ubwp, 1, mu)); SLIV2SL(tdaP_Mi->startSymbolAndLength, &start, &len); const uint16_t symb_tda_mi = ((1 << len) - 1) << start; // check whether PUCCH and TDA overlap: then, we cannot use it. Also, check @@ -188,7 +190,6 @@ void nr_process_mac_pdu(module_id_t module_idP, int pdu_len = mac_pdu_len; uint16_t mac_ce_len, mac_subheader_len, mac_sdu_len; - NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; // For both DL/UL-SCH @@ -222,7 +223,7 @@ void nr_process_mac_pdu(module_id_t module_idP, mac_sdu_len = 0; rx_lcid = ((NR_MAC_SUBHEADER_FIXED *)pdu_ptr)->LCID; - LOG_D(MAC, "LCID received at gNB side: %d \n", rx_lcid); + LOG_D(NR_MAC, "LCID received at gNB side: %d \n", rx_lcid); unsigned char *ce_ptr; int n_Lcg = 0; @@ -231,7 +232,7 @@ void nr_process_mac_pdu(module_id_t module_idP, // MAC CE /*#ifdef DEBUG_HEADER_PARSING - LOG_D(MAC, "[UE] LCID %d, PDU length %d\n", ((NR_MAC_SUBHEADER_FIXED *)pdu_ptr)->LCID, pdu_len); + LOG_D(NR_MAC, "[UE] LCID %d, PDU length %d\n", ((NR_MAC_SUBHEADER_FIXED *)pdu_ptr)->LCID, pdu_len); #endif*/ case UL_SCH_LCID_RECOMMENDED_BITRATE_QUERY: // 38.321 Ch6.1.3.20 @@ -251,7 +252,7 @@ void nr_process_mac_pdu(module_id_t module_idP, NR_BSR_SHORT *bsr_s = (NR_BSR_SHORT *) ce_ptr; sched_ctrl->estimated_ul_buffer = 0; sched_ctrl->estimated_ul_buffer = NR_SHORT_BSR_TABLE[bsr_s->Buffer_size]; - LOG_D(MAC, + LOG_D(NR_MAC, "SHORT BSR at %4d.%2d, LCG ID %d, BS Index %d, BS value < %d, est buf %d\n", frameP, slot, @@ -279,10 +280,17 @@ void nr_process_mac_pdu(module_id_t module_idP, n_Lcg = bsr_l->LcgID7 + bsr_l->LcgID6 + bsr_l->LcgID5 + bsr_l->LcgID4 + bsr_l->LcgID3 + bsr_l->LcgID2 + bsr_l->LcgID1 + bsr_l->LcgID0; + LOG_D(NR_MAC, "LONG BSR, LCG ID(7-0) %d/%d/%d/%d/%d/%d/%d/%d\n", + bsr_l->LcgID7, bsr_l->LcgID6, bsr_l->LcgID5, bsr_l->LcgID4, + bsr_l->LcgID3, bsr_l->LcgID2, bsr_l->LcgID1, bsr_l->LcgID0); + for (int n = 0; n < n_Lcg; n++){ + LOG_D(NR_MAC, "LONG BSR, %d/%d (n/n_Lcg), BS Index %d, BS value < %d", + n, n_Lcg, pdu_ptr[mac_subheader_len + 1 + n], + NR_LONG_BSR_TABLE[pdu_ptr[mac_subheader_len + 1 + n]]); sched_ctrl->estimated_ul_buffer += NR_LONG_BSR_TABLE[pdu_ptr[mac_subheader_len + 1 + n]]; - LOG_D(MAC, + LOG_D(NR_MAC, "LONG BSR at %4d.%2d, %d/%d (n/n_Lcg), BS Index %d, BS value < %d, total %d\n", frameP, slot, @@ -307,19 +315,19 @@ void nr_process_mac_pdu(module_id_t module_idP, //fixed length mac_ce_len = 2; /* Extract SINGLE ENTRY PHR elements for PHR calculation */ - ce_ptr = &pdu_ptr[mac_subheader_len]; - NR_SINGLE_ENTRY_PHR_MAC_CE *phr = (NR_SINGLE_ENTRY_PHR_MAC_CE *) ce_ptr; - /* Save the phr info */ - const int PH = phr->PH; - const int PCMAX = phr->PCMAX; - /* 38.133 Table10.1.17.1-1 */ - if (PH < 55) - sched_ctrl->ph = PH - 32; - else - sched_ctrl->ph = PH - 32 + (PH - 54); - /* 38.133 Table10.1.18.1-1 */ - sched_ctrl->pcmax = PCMAX - 29; - LOG_D(MAC, "SINGLE ENTRY PHR R1 %d PH %d (%d dB) R2 %d PCMAX %d (%d dBm)\n", + ce_ptr = &pdu_ptr[mac_subheader_len]; + NR_SINGLE_ENTRY_PHR_MAC_CE *phr = (NR_SINGLE_ENTRY_PHR_MAC_CE *) ce_ptr; + /* Save the phr info */ + const int PH = phr->PH; + const int PCMAX = phr->PCMAX; + /* 38.133 Table10.1.17.1-1 */ + if (PH < 55) + sched_ctrl->ph = PH - 32; + else + sched_ctrl->ph = PH - 32 + (PH - 54); + /* 38.133 Table10.1.18.1-1 */ + sched_ctrl->pcmax = PCMAX - 29; + LOG_D(MAC, "SINGLE ENTRY PHR R1 %d PH %d (%d dB) R2 %d PCMAX %d (%d dBm)\n", phr->R1, PH, sched_ctrl->ph, phr->R2, PCMAX, sched_ctrl->pcmax); break; @@ -352,13 +360,30 @@ void nr_process_mac_pdu(module_id_t module_idP, // end of MAC PDU, can ignore the rest. break; - // MAC SDUs case UL_SCH_LCID_SRB1: - // todo - break; case UL_SCH_LCID_SRB2: - // todo - break; + if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){ + //mac_sdu_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8; + mac_subheader_len = 3; + mac_sdu_len = ((uint16_t)(((NR_MAC_SUBHEADER_LONG *) pdu_ptr)->L1 & 0x7f) << 8) + | ((uint16_t)((NR_MAC_SUBHEADER_LONG *) pdu_ptr)->L2 & 0xff); + } else { + mac_sdu_len = (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L; + mac_subheader_len = 2; + } + LOG_D(NR_MAC, "[UE %d] Frame %d : ULSCH -> UL-DCCH %d (gNB %d, %d bytes), rnti: %d \n", module_idP, frameP, rx_lcid, module_idP, mac_sdu_len, *UE_info->rnti); + mac_rlc_data_ind(module_idP, + UE_info->rnti[UE_id], + module_idP, + frameP, + ENB_FLAG_YES, + MBMS_FLAG_NO, + rx_lcid, + (char *) (pdu_ptr + mac_subheader_len), + mac_sdu_len, + 1, + NULL); + break; case UL_SCH_LCID_SRB3: // todo break; @@ -414,16 +439,16 @@ void nr_process_mac_pdu(module_id_t module_idP, mac_subheader_len = 2; } - LOG_D(MAC, - "[UE %d] Frame %d : ULSCH -> UL-DTCH %d (gNB %d, %d bytes)\n", + LOG_D(NR_MAC, "[UE %d] Frame %d : ULSCH -> UL-%s %d (gNB %d, %d bytes)\n", module_idP, frameP, + rx_lcid<4?"DCCH":"DTCH", rx_lcid, module_idP, mac_sdu_len); UE_info->mac_stats[UE_id].lc_bytes_rx[rx_lcid] += mac_sdu_len; #if defined(ENABLE_MAC_PAYLOAD_DEBUG) - log_dump(MAC, pdu_ptr + mac_subheader_len, 32, LOG_DUMP_CHAR, "\n"); + log_dump(NR_MAC, pdu_ptr + mac_subheader_len, 32, LOG_DUMP_CHAR, "\n"); #endif mac_rlc_data_ind(module_idP, @@ -446,7 +471,7 @@ void nr_process_mac_pdu(module_id_t module_idP, break; default: - LOG_E(MAC, "Received unknown MAC header (LCID = 0x%02x)\n", rx_lcid); + LOG_E(NR_MAC, "Received unknown MAC header (LCID = 0x%02x)\n", rx_lcid); return; break; } @@ -454,8 +479,8 @@ void nr_process_mac_pdu(module_id_t module_idP, pdu_len -= ( mac_subheader_len + mac_ce_len + mac_sdu_len ); if (pdu_len < 0) { - LOG_E(MAC, "%s() residual mac pdu length < 0!, pdu_len: %d\n", __func__, pdu_len); - LOG_E(MAC, "MAC PDU "); + LOG_E(NR_MAC, "%s() residual mac pdu length < 0!, pdu_len: %d\n", __func__, pdu_len); + LOG_E(NR_MAC, "MAC PDU "); for (int i = 0; i < 20; i++) // Only printf 1st - 20nd bytes printf("%02x ", pdu_ptr[i]); printf("\n"); @@ -489,7 +514,7 @@ void handle_nr_ul_harq(module_id_t mod_id, { int UE_id = find_nr_UE_id(mod_id, crc_pdu->rnti); if (UE_id < 0) { - LOG_E(MAC, "%s(): unknown RNTI %04x in PUSCH\n", __func__, crc_pdu->rnti); + LOG_E(NR_MAC, "%s(): unknown RNTI %04x in PUSCH\n", __func__, crc_pdu->rnti); return; } NR_UE_info_t *UE_info = &RC.nrmac[mod_id]->UE_info; @@ -497,7 +522,7 @@ void handle_nr_ul_harq(module_id_t mod_id, int8_t harq_pid = sched_ctrl->feedback_ul_harq.head; while (crc_pdu->harq_id != harq_pid || harq_pid < 0) { - LOG_W(MAC, + LOG_W(NR_MAC, "Unexpected ULSCH HARQ PID %d (have %d) for RNTI %04x (ignore this warning for RA)\n", crc_pdu->harq_id, harq_pid, @@ -523,20 +548,20 @@ void handle_nr_ul_harq(module_id_t mod_id, if (!crc_pdu->tb_crc_status) { harq->ndi ^= 1; harq->round = 0; - LOG_D(MAC, + LOG_D(NR_MAC, "Ulharq id %d crc passed for RNTI %04x\n", harq_pid, crc_pdu->rnti); add_tail_nr_list(&sched_ctrl->available_ul_harq, harq_pid); } else if (harq->round >= MAX_HARQ_ROUNDS - 1) { abort_nr_ul_harq(mod_id, UE_id, harq_pid); - LOG_D(MAC, + LOG_D(NR_MAC, "RNTI %04x: Ulharq id %d crc failed in all rounds\n", crc_pdu->rnti, harq_pid); } else { harq->round++; - LOG_D(MAC, + LOG_D(NR_MAC, "Ulharq id %d crc failed for RNTI %04x\n", harq_pid, crc_pdu->rnti); @@ -563,6 +588,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, const int current_rnti = rntiP; const int UE_id = find_nr_UE_id(gnb_mod_idP, current_rnti); const int target_snrx10 = gNB_mac->pusch_target_snrx10; + const int pusch_failure_thres = gNB_mac->pusch_failure_thres; if (UE_id != -1) { NR_UE_sched_ctrl_t *UE_scheduling_control = &UE_info->UE_sched_ctrl[UE_id]; @@ -574,7 +600,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, T_BUFFER(sduP, sdu_lenP)); UE_info->mac_stats[UE_id].ulsch_total_bytes_rx += sdu_lenP; - LOG_D(NR_MAC, "[gNB %d][PUSCH %d] CC_id %d %d.%d Received ULSCH sdu from PHY (rnti %x, UE_id %d) ul_cqi %d sduP %p\n", + LOG_D(NR_MAC, "[gNB %d][PUSCH %d] CC_id %d %d.%d Received ULSCH sdu from PHY (rnti %x, UE_id %d) ul_cqi %d TA %d sduP %p\n", gnb_mod_idP, harq_pid, CC_idP, @@ -583,6 +609,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, current_rnti, UE_id, ul_cqi, + timing_advance, sduP); // if not missed detection (10dB threshold for now) @@ -600,7 +627,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, #if defined(ENABLE_MAC_PAYLOAD_DEBUG) - LOG_I(MAC, "Printing received UL MAC payload at gNB side: %d \n"); + LOG_I(NR_MAC, "Printing received UL MAC payload at gNB side: %d \n"); for (int i = 0; i < sdu_lenP ; i++) { //harq_process_ul_ue->a[i] = (unsigned char) rand(); //printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]); @@ -613,6 +640,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, if (sduP != NULL){ LOG_D(NR_MAC, "Received PDU at MAC gNB \n"); + UE_info->UE_sched_ctrl[UE_id].pusch_consecutive_dtx_cnt = 0; const uint32_t tb_size = UE_scheduling_control->ul_harq_processes[harq_pid].sched_pusch.tb_size; UE_scheduling_control->sched_ul_bytes -= tb_size; if (UE_scheduling_control->sched_ul_bytes < 0) @@ -620,6 +648,24 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, nr_process_mac_pdu(gnb_mod_idP, UE_id, CC_idP, frameP, slotP, sduP, sdu_lenP); } + else { + NR_UE_ul_harq_t *cur_harq = &UE_scheduling_control->ul_harq_processes[harq_pid]; + /* reduce sched_ul_bytes when cur_harq->round == 3 */ + if (cur_harq->round == 3){ + const uint32_t tb_size = UE_scheduling_control->ul_harq_processes[harq_pid].sched_pusch.tb_size; + UE_scheduling_control->sched_ul_bytes -= tb_size; + if (UE_scheduling_control->sched_ul_bytes < 0) + UE_scheduling_control->sched_ul_bytes = 0; + } + if (ul_cqi <= 128) { + UE_info->UE_sched_ctrl[UE_id].pusch_consecutive_dtx_cnt++; + UE_info->mac_stats[UE_id].ulsch_DTX++; + } + if (UE_info->UE_sched_ctrl[UE_id].pusch_consecutive_dtx_cnt >= pusch_failure_thres) { + LOG_D(NR_MAC,"Detected UL Failure on PUSCH, stopping scheduling\n"); + UE_info->UE_sched_ctrl[UE_id].ul_failure = 1; + } + } } else if(sduP) { bool no_sig = true; @@ -647,7 +693,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, continue; if(no_sig) { - LOG_W(NR_MAC, "Random Access %i failed at state %i\n", i, ra->state); + LOG_W(NR_MAC, "Random Access %i failed at state %i (no signal)\n", i, ra->state); nr_mac_remove_ra_rnti(gnb_mod_idP, ra->rnti); nr_clear_ra_proc(gnb_mod_idP, CC_idP, frameP, ra); } else { @@ -660,7 +706,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, current_rnti); if( (frameP==ra->Msg3_frame) && (slotP==ra->Msg3_slot) ) { - LOG_W(NR_MAC, "Random Access %i failed at state %i\n", i, ra->state); + LOG_W(NR_MAC, "Random Access %i failed at state %i (TC_RNTI %04x RNTI %04x\n", i, ra->state,ra->rnti,current_rnti); nr_mac_remove_ra_rnti(gnb_mod_idP, ra->rnti); nr_clear_ra_proc(gnb_mod_idP, CC_idP, frameP, ra); } @@ -668,7 +714,9 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, continue; } - const int UE_id = add_new_nr_ue(gnb_mod_idP, ra->rnti, ra->secondaryCellGroup); + int UE_id=-1; + + UE_id = add_new_nr_ue(gnb_mod_idP, ra->rnti, ra->CellGroup); UE_info->UE_beam_index[UE_id] = ra->beam_id; // re-initialize ta update variables after RA procedure completion @@ -716,7 +764,6 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, } return; - } } } else { @@ -725,16 +772,18 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, if (ra->state != WAIT_Msg3) continue; - LOG_W(NR_MAC, "Random Access %i failed at state %i\n", i, ra->state); + LOG_W(NR_MAC, "Random Access %i failed at state %i (state is not WAIT_Msg3)\n", i, ra->state); nr_mac_remove_ra_rnti(gnb_mod_idP, ra->rnti); nr_clear_ra_proc(gnb_mod_idP, CC_idP, frameP, ra); } } } -long get_K2(const NR_BWP_Uplink_t *ubwp, int time_domain_assignment, int mu) { - DevAssert(ubwp); - const NR_PUSCH_TimeDomainResourceAllocation_t *tda_list = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[time_domain_assignment]; +long get_K2(NR_ServingCellConfigCommon_t *scc,NR_BWP_Uplink_t *ubwp, int time_domain_assignment, int mu) { + DevAssert(scc); + const NR_PUSCH_TimeDomainResourceAllocation_t *tda_list = ubwp ? + ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[time_domain_assignment]: + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[time_domain_assignment]; if (tda_list->k2) return *tda_list->k2; else if (mu < 2) @@ -793,13 +842,23 @@ bool allocate_ul_retransmission(module_id_t module_id, NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; NR_sched_pusch_t *retInfo = &sched_ctrl->ul_harq_processes[harq_pid].sched_pusch; - int rbStart = - NRRIV2PRBOFFSET(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - const uint16_t bwpSize = - NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; - const uint8_t num_dmrs_cdm_grps_no_data = 1; - const int tda = RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + // frame/slot in sched_pusch has been set previously. In the following, we + // overwrite the information in the retransmission information before storing + // as the new scheduling instruction + retInfo->frame = sched_ctrl->sched_pusch.frame; + retInfo->slot = sched_ctrl->sched_pusch.slot; + + // Get previous PUSCH filed info + sched_ctrl->sched_pusch = *retInfo; + + NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters : &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + int rbStart = sched_ctrl->active_ubwp ? NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE) : 0; + const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + + const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_bwp ? 1 : 2; + const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 1; if (tda == retInfo->time_domain_allocation) { /* Check the resource is enough for retransmission */ while (rbStart < bwpSize && !rballoc_mask[rbStart]) @@ -812,7 +871,7 @@ bool allocate_ul_retransmission(module_id_t module_id, * (re-)transmission */ NR_pusch_semi_static_t *ps = &sched_ctrl->pusch_semi_static; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; - const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; + const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -828,7 +887,7 @@ bool allocate_ul_retransmission(module_id_t module_id, rbSize++; NR_pusch_semi_static_t temp_ps; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; - const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; + const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, dci_format, tda, num_dmrs_cdm_grps_no_data, &temp_ps); uint32_t new_tbs; uint16_t new_rbSize; @@ -860,14 +919,6 @@ bool allocate_ul_retransmission(module_id_t module_id, return false; } - /* frame/slot in sched_pusch has been set previously. In the following, we - * overwrite the information in the retransmission information before storing - * as the new scheduling instruction */ - retInfo->frame = sched_ctrl->sched_pusch.frame; - retInfo->slot = sched_ctrl->sched_pusch.slot; - /* Get previous PSUCH field info */ - sched_ctrl->sched_pusch = *retInfo; - NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; LOG_D(MAC, "%4d.%2d Allocate UL retransmission UE %d/RNTI %04x sched %4d.%2d (%d RBs)\n", frame, @@ -893,8 +944,8 @@ void update_ul_ue_R_Qm(NR_sched_pusch_t *sched_pusch, const NR_pusch_semi_static const int mcs = sched_pusch->mcs; sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table); sched_pusch->Qm = nr_get_Qm_ul(mcs, ps->mcs_table); - if (ps->pusch_Config->tp_pi2BPSK - && ((ps->mcs_table == 3 && mcs < 2) || (ps->mcs_table == 4 && mcs < 6))) { + + if (ps->pusch_Config && ps->pusch_Config->tp_pi2BPSK && ((ps->mcs_table == 3 && mcs < 2) || (ps->mcs_table == 4 && mcs < 6))) { sched_pusch->R >>= 1; sched_pusch->Qm <<= 1; } @@ -911,7 +962,6 @@ void pf_ul(module_id_t module_id, uint8_t *rballoc_mask) { const int CC_id = 0; - const uint8_t num_dmrs_cdm_grps_no_data = 1; gNB_MAC_INST *nrmac = RC.nrmac[module_id]; NR_ServingCellConfigCommon_t *scc = nrmac->common_channels[CC_id].ServingCellConfigCommon; NR_UE_info_t *UE_info = &nrmac->UE_info; @@ -923,10 +973,13 @@ void pf_ul(module_id_t module_id, /* Loop UE_list to calculate throughput and coeff */ for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + + if (UE_info->Msg4_ACKed[UE_id] != true) continue; + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - int rbStart = - NRRIV2PRBOFFSET(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters : &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + int rbStart = sched_ctrl->active_ubwp ? NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE) : 0; + const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; NR_pusch_semi_static_t *ps = &sched_ctrl->pusch_semi_static; @@ -965,7 +1018,7 @@ void pf_ul(module_id_t module_id, /* if no data, pre-allocate 5RB */ bool freeCCE = find_free_CCE(module_id, slot, UE_id); if (!freeCCE) { - LOG_D(MAC, "%4d.%2d no free CCE for UL DCI UE %04x (BSR 0)\n", frame, slot, UE_info->rnti[UE_id]); + LOG_D(NR_MAC, "%4d.%2d no free CCE for UL DCI UE %04x (BSR 0)\n", frame, slot, UE_info->rnti[UE_id]); continue; } /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */ @@ -973,20 +1026,22 @@ void pf_ul(module_id_t module_id, if (max_num_ue < 0) return; + LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d\n", min_rb,rbStart); while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++; if (rbStart + min_rb >= bwpSize) { - LOG_D(MAC, "cannot allocate continuous data for UE %d/RNTI %04x: no resources\n", - UE_id, UE_info->rnti[UE_id]); - continue; + LOG_W(NR_MAC, "cannot allocate continuous UL data for UE %d/RNTI %04x: no resources (rbStart %d, min_rb %d, bwpSize %d\n", + UE_id, UE_info->rnti[UE_id],rbStart,min_rb,bwpSize); + return; } /* Save PUSCH field */ /* we want to avoid a lengthy deduction of DMRS and other parameters in * every TTI if we can save it, so check whether dci_format, TDA, or * num_dmrs_cdm_grps_no_data has changed and only then recompute */ + const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_ubwp ? 1 : 2; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; - const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 1; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1021,7 +1076,7 @@ void pf_ul(module_id_t module_id, sched_pusch->mcs = 9; const uint32_t tbs = ul_pf_tbs[ps->mcs_table][sched_pusch->mcs]; coeff_ue[UE_id] = (float) tbs / ul_thr_ue[UE_id]; - LOG_D(MAC,"b %d, ul_thr_ue[%d] %f, tbs %d, coeff_ue[%d] %f\n", + LOG_D(NR_MAC,"b %d, ul_thr_ue[%d] %f, tbs %d, coeff_ue[%d] %f\n", b, UE_id, ul_thr_ue[UE_id], tbs, UE_id, coeff_ue[UE_id]); } @@ -1047,7 +1102,7 @@ void pf_ul(module_id_t module_id, bool freeCCE = find_free_CCE(module_id, slot, UE_id); if (!freeCCE) { - LOG_D(MAC, "%4d.%2d no free CCE for UL DCI UE %04x\n", frame, slot, UE_info->rnti[UE_id]); + LOG_D(NR_MAC, "%4d.%2d no free CCE for UL DCI UE %04x\n", frame, slot, UE_info->rnti[UE_id]); continue; } @@ -1057,10 +1112,9 @@ void pf_ul(module_id_t module_id, return; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - /* for some reason, the UEs do not like when they are scheduled on the last - * RB. Hence, as a (hopefully temporary) fix, schedule one RB less. */ - const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE) - 1; + NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters : &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + int rbStart = sched_ctrl->active_ubwp ? NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE) : 0; + const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; NR_pusch_semi_static_t *ps = &sched_ctrl->pusch_semi_static; @@ -1070,13 +1124,20 @@ void pf_ul(module_id_t module_id, while (rbStart + max_rbSize < bwpSize && rballoc_mask[rbStart + max_rbSize]) max_rbSize++; + if (rbStart + min_rb >= bwpSize) { + LOG_W(NR_MAC, "cannot allocate UL data for UE %d/RNTI %04x: no resources (rbStart %d, min_rb %d, bwpSize %d\n", + UE_id, UE_info->rnti[UE_id],rbStart,min_rb,bwpSize); + return; + } + /* Save PUSCH field */ /* we want to avoid a lengthy deduction of DMRS and other parameters in * every TTI if we can save it, so check whether dci_format, TDA, or * num_dmrs_cdm_grps_no_data has changed and only then recompute */ + const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_ubwp ? 1 : 2; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; - const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const int tda = nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 1; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1126,10 +1187,10 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * schedule now (slot + k2 is not UL slot) */ int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot]; + const int tda = sched_ctrl->active_ubwp ? nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 1; if (tda < 0) return false; - int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu); + int K2 = get_K2(scc, sched_ctrl->active_ubwp, tda, mu); const int sched_frame = frame + (slot + K2 >= nr_slots_per_frame[mu]); const int sched_slot = (slot + K2) % nr_slots_per_frame[mu]; if (!is_xlsch_in_slot(nr_mac->ulsch_slot_bitmap[sched_slot / 64], sched_slot)) @@ -1139,6 +1200,8 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t sched_ctrl->sched_pusch.frame = sched_frame; for (UE_id = UE_info->list.next[UE_id]; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + AssertFatal(K2 == get_K2(scc,sched_ctrl->active_ubwp, tda, mu), + "Different K2, %d(UE%d) != %ld(UE%d)\n", K2, 0, get_K2(scc,sched_ctrl->active_ubwp, tda, mu), UE_id); sched_ctrl->sched_pusch.slot = sched_slot; sched_ctrl->sched_pusch.frame = sched_frame; } @@ -1148,13 +1211,18 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * Calculate largest contiguous RBs */ uint16_t *vrb_map_UL = &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[sched_slot * MAX_BWP_SIZE]; - const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = - sched_ctrl->active_ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp ? + sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth: + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, + MAX_BWP_SIZE); + const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = sched_ctrl->active_ubwp ? + sched_ctrl->active_ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList: + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength; int startSymbolIndex, nrOfSymbols; SLIV2SL(startSymbolAndLength, &startSymbolIndex, &nrOfSymbols); const uint16_t symb = ((1 << nrOfSymbols) - 1) << startSymbolIndex; + int st = 0, e = 0, len = 0; for (int i = 0; i < bwpSize; i++) { while ((vrb_map_UL[i] & symb) != 0 && i < bwpSize) @@ -1222,7 +1290,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) /* Uplink data ONLY can be scheduled when the current slot is downlink slot, * because we have to schedule the DCI0 first before schedule uplink data */ if (!is_xlsch_in_slot(nr_mac->dlsch_slot_bitmap[slot / 64], slot)) { - LOG_D(MAC, "Current slot %d is NOT DL slot, cannot schedule DCI0 for UL data\n", slot); + LOG_D(NR_MAC, "Current slot %d is NOT DL slot, cannot schedule DCI0 for UL data\n", slot); return; } bool do_sched = RC.nrmac[module_id]->pre_processor_ul(module_id, frame, slot); @@ -1243,11 +1311,13 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) const NR_list_t *UE_list = &UE_info->list; for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->ul_failure == 1 && get_softmodem_params()->phy_test==0) continue; UE_info->mac_stats[UE_id].ulsch_current_bytes = 0; /* dynamic PUSCH values (RB alloc, MCS, hence R, Qm, TBS) that change in * every TTI are pre-populated by the preprocessor and used below */ NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; + LOG_D(NR_MAC,"UE %x : sched_pusch->rbSize %d\n",UE_info->rnti[UE_id],sched_pusch->rbSize); if (sched_pusch->rbSize <= 0) continue; @@ -1297,7 +1367,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) cur_harq->sched_pusch.time_domain_allocation = ps->time_domain_allocation; sched_ctrl->sched_ul_bytes += sched_pusch->tb_size; } else { - LOG_D(MAC, + LOG_D(NR_MAC, "%d.%2d UL retransmission RNTI %04x sched %d.%2d HARQ PID %d round %d NDI %d\n", frame, slot, @@ -1312,7 +1382,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) sched_ctrl->last_ul_frame = sched_pusch->frame; sched_ctrl->last_ul_slot = sched_pusch->slot; - LOG_D(MAC, + LOG_D(NR_MAC, "%4d.%2d RNTI %04x UL sched %4d.%2d start %2d RBS %3d startSymbol %2d nb_symbol %2d MCS %2d TBS %4d HARQ PID %2d round %d NDI %d est %6d sched %6d est BSR %6d\n", frame, slot, @@ -1349,16 +1419,18 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) memset(pusch_pdu, 0, sizeof(nfapi_nr_pusch_pdu_t)); future_ul_tti_req->n_pdus += 1; - LOG_D(MAC, "%4d.%2d Scheduling UE specific PUSCH\n", frame, slot); + LOG_D(NR_MAC, "%4d.%2d Scheduling UE specific PUSCH for sched %d.%d, ul_tto_req %d.%d\n", frame, slot, + sched_pusch->frame,sched_pusch->slot,future_ul_tti_req->SFN,future_ul_tti_req->Slot); pusch_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; pusch_pdu->rnti = rnti; pusch_pdu->handle = 0; //not yet used /* FAPI: BWP */ - pusch_pdu->bwp_size = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pusch_pdu->bwp_start = NRRIV2PRBOFFSET(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pusch_pdu->subcarrier_spacing = sched_ctrl->active_ubwp->bwp_Common->genericParameters.subcarrierSpacing; + NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters:&scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + pusch_pdu->bwp_size = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + pusch_pdu->bwp_start = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); + pusch_pdu->subcarrier_spacing = genericParameters->subcarrierSpacing; pusch_pdu->cyclic_prefix = 0; /* FAPI: PUSCH information always included */ @@ -1367,7 +1439,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pusch_pdu->mcs_index = sched_pusch->mcs; pusch_pdu->mcs_table = ps->mcs_table; pusch_pdu->transform_precoding = ps->transform_precoding; - if (ps->pusch_Config->dataScramblingIdentityPUSCH) + if (ps->pusch_Config && + ps->pusch_Config->dataScramblingIdentityPUSCH) pusch_pdu->data_scrambling_id = *ps->pusch_Config->dataScramblingIdentityPUSCH; else pusch_pdu->data_scrambling_id = *scc->physCellId; @@ -1377,10 +1450,10 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pusch_pdu->ul_dmrs_symb_pos = ps->ul_dmrs_symb_pos; pusch_pdu->dmrs_config_type = ps->dmrs_config_type; if (pusch_pdu->transform_precoding) { // transform precoding disabled - long *scramblingid; - if (pusch_pdu->scid == 0) + long *scramblingid=NULL; + if (ps->NR_DMRS_UplinkConfig && pusch_pdu->scid == 0) scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0; - else + else if (ps->NR_DMRS_UplinkConfig) scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID1; if (scramblingid == NULL) pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId; @@ -1389,9 +1462,9 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) } else { pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId; - if (ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity != NULL) + if (ps->NR_DMRS_UplinkConfig && ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity != NULL) pusch_pdu->pusch_identity = *ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity; - else + else if (ps->NR_DMRS_UplinkConfig) pusch_pdu->pusch_identity = *scc->physCellId; } pusch_pdu->scid = 0; // DMRS sequence initialization [TS38.211, sec 6.4.1.1.1] @@ -1399,13 +1472,11 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pusch_pdu->dmrs_ports = 1; /* FAPI: Pusch Allocation in frequency domain */ - AssertFatal(ps->pusch_Config->resourceAllocation == NR_PUSCH_Config__resourceAllocation_resourceAllocationType1, - "Only frequency resource allocation type 1 is currently supported\n"); pusch_pdu->resource_alloc = 1; //type 1 pusch_pdu->rb_start = sched_pusch->rbStart; pusch_pdu->rb_size = sched_pusch->rbSize; pusch_pdu->vrb_to_prb_mapping = 0; - if (ps->pusch_Config->frequencyHopping==NULL) + if (ps->pusch_Config==NULL || ps->pusch_Config->frequencyHopping==NULL) pusch_pdu->frequency_hopping = 0; else pusch_pdu->frequency_hopping = 1; @@ -1429,8 +1500,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pusch_pdu->dfts_ofdm.low_papr_group_number = pusch_pdu->pusch_identity % 30; // V as specified in section 6.4.1.1.1.2 in 38.211 V = 0 if sequence hopping and group hopping are disabled - if ((ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceGroupHopping == NULL) && - (ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceHopping == NULL)) + if ((ps->NR_DMRS_UplinkConfig==NULL) || ((ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceGroupHopping == NULL) && + (ps->NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceHopping == NULL))) pusch_pdu->dfts_ofdm.low_papr_sequence_number = 0; else AssertFatal(1==0,"SequenceGroupHopping or sequenceHopping are NOT Supported\n"); @@ -1441,7 +1512,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) /*-----------------------------------------------------------------------------*/ /* PUSCH PTRS */ - if (ps->NR_DMRS_UplinkConfig->phaseTrackingRS != NULL) { + if (ps->NR_DMRS_UplinkConfig && ps->NR_DMRS_UplinkConfig->phaseTrackingRS != NULL) { bool valid_ptrs_setup = false; pusch_pdu->pusch_ptrs.ptrs_ports_list = (nfapi_nr_ptrs_ports_t *) malloc(2*sizeof(nfapi_nr_ptrs_ports_t)); valid_ptrs_setup = set_ul_ptrs_values(ps->NR_DMRS_UplinkConfig->phaseTrackingRS->choice.setup, @@ -1459,8 +1530,10 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) /* look up the PDCCH PDU for this BWP and CORESET. If it does not exist, * create it */ - const int bwpid = sched_ctrl->active_bwp->bwp_Id; - const int coresetid = sched_ctrl->coreset->controlResourceSetId; + const int bwpid = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; + NR_SearchSpace_t *ss = sched_ctrl->active_bwp ? sched_ctrl->search_space: RC.nrmac[module_id]->sched_ctrlCommon->search_space; + NR_ControlResourceSet_t *coreset = sched_ctrl->active_bwp? sched_ctrl->coreset: RC.nrmac[module_id]->sched_ctrlCommon->coreset; + const int coresetid = coreset->controlResourceSetId; nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; @@ -1469,19 +1542,19 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) ul_dci_request_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu)); pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; - nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, sched_ctrl->active_bwp); + nr_configure_pdcch(pdcch_pdu, ss, coreset, scc, sched_ctrl->active_bwp); pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu; } - LOG_D(MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); + LOG_D(NR_MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); /* Fill PDCCH DL DCI PDU */ nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu->dci_pdu[pdcch_pdu->numDlDci]; pdcch_pdu->numDlDci++; dci_pdu->RNTI = rnti; - if (sched_ctrl->coreset->pdcch_DMRS_ScramblingID && - sched_ctrl->search_space->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) { - dci_pdu->ScramblingId = *sched_ctrl->coreset->pdcch_DMRS_ScramblingID; + if (coreset->pdcch_DMRS_ScramblingID && + ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) { + dci_pdu->ScramblingId = *coreset->pdcch_DMRS_ScramblingID; dci_pdu->ScramblingRNTI = rnti; } else { dci_pdu->ScramblingId = *scc->physCellId; @@ -1494,24 +1567,30 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) dci_pdu_rel15_t uldci_payload; memset(&uldci_payload, 0, sizeof(uldci_payload)); - NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id]; - const int n_ubwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; + NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; + int n_ubwp=1; + if (CellGroup && CellGroup->spCellConfig && CellGroup->spCellConfig->spCellConfigDedicated && + CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig && + CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList) + n_ubwp = CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; + config_uldci(sched_ctrl->active_ubwp, + scc, pusch_pdu, &uldci_payload, ps->dci_format, ps->time_domain_allocation, UE_info->UE_sched_ctrl[UE_id].tpc0, n_ubwp, - sched_ctrl->active_bwp->bwp_Id); + bwpid); fill_dci_pdu_rel15(scc, - secondaryCellGroup, + CellGroup, dci_pdu, &uldci_payload, ps->dci_format, rnti_types[0], pusch_pdu->bwp_size, - sched_ctrl->active_bwp->bwp_Id); + bwpid); memset(sched_pusch, 0, sizeof(*sched_pusch)); } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 1010ebafda..ca12081a6f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -51,12 +51,10 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, - int pusch_tgt_snrx10, - int pucch_tgt_snrx10, NR_ServingCellConfigCommon_t *scc, - int nsa_flag, - uint32_t rnti, - NR_CellGroupConfig_t *secondaryCellGroup + int nsa_flag, + uint32_t rnti, + NR_CellGroupConfig_t *CellGroup ); void clear_nr_nfapi_information(gNB_MAC_INST * gNB, @@ -167,6 +165,7 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, void config_uldci(const NR_BWP_Uplink_t *ubwp, + const NR_ServingCellConfigCommon_t *scc, const nfapi_nr_pusch_pdu_t *pusch_pdu, dci_pdu_rel15_t *dci_pdu_rel15, int dci_format, @@ -187,9 +186,10 @@ void nr_csi_meas_reporting(int Mod_idP, sub_frame_t slotP); int nr_acknack_scheduling(int Mod_idP, - int UE_id, - frame_t frameP, - sub_frame_t slotP); + int UE_id, + frame_t frameP, + sub_frame_t slotP, + int r_pucch); void get_pdsch_to_harq_feedback(int Mod_idP, int UE_id, @@ -216,13 +216,15 @@ int nr_is_dci_opportunity(nfapi_nr_search_space_t search_space, */ void nr_configure_pucch(nfapi_nr_pucch_pdu_t* pucch_pdu, - NR_ServingCellConfigCommon_t *scc, - NR_BWP_Uplink_t *bwp, + NR_ServingCellConfigCommon_t *scc, + NR_CellGroupConfig_t *CellGroup, + NR_BWP_Uplink_t *bwp, uint16_t rnti, uint8_t pucch_resource, uint16_t O_csi, uint16_t O_ack, - uint8_t O_sr); + uint8_t O_sr, + int r_pucch); void find_search_space(int ss_type, NR_BWP_Downlink_t *bwp, @@ -235,7 +237,7 @@ void nr_configure_pdcch(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu, NR_BWP_Downlink_t *bwp); void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, - const NR_CellGroupConfig_t *secondaryCellGroup, + const NR_CellGroupConfig_t *CellGroup, nfapi_nr_dl_dci_pdu_t *pdcch_dci_pdu, dci_pdu_rel15_t *dci_pdu_rel15, int dci_formats, @@ -243,18 +245,23 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, int N_RB, int bwp_id); -void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup, +void prepare_dci(const NR_CellGroupConfig_t *CellGroup, dci_pdu_rel15_t *dci_pdu_rel15, nr_dci_format_t format, int bwp_id); /* find coreset within the search space */ -NR_ControlResourceSet_t *get_coreset(const NR_BWP_Downlink_t *bwp, const NR_SearchSpace_t *ss, int ss_type); +NR_ControlResourceSet_t *get_coreset(NR_ServingCellConfigCommon_t *scc, + NR_BWP_Downlink_t *bwp, + NR_SearchSpace_t *ss, + NR_SearchSpace__searchSpaceType_PR ss_type); /* find a search space within a BWP */ -NR_SearchSpace_t *get_searchspace(const NR_BWP_Downlink_t *bwp, NR_SearchSpace__searchSpaceType_PR target_ss); +NR_SearchSpace_t *get_searchspace(NR_ServingCellConfigCommon_t *scc, + NR_BWP_DownlinkDedicated_t *bwp_Dedicated, + NR_SearchSpace__searchSpaceType_PR target_ss); -long get_K2(const NR_BWP_Uplink_t *ubwp, int time_domain_assignment, int mu); +long get_K2(NR_ServingCellConfigCommon_t *scc, NR_BWP_Uplink_t *ubwp, int time_domain_assignment, int mu); void nr_set_pdsch_semi_static(const NR_ServingCellConfigCommon_t *scc, const NR_CellGroupConfig_t *secondaryCellGroup, @@ -306,7 +313,7 @@ int find_nr_UE_id(module_id_t mod_idP, rnti_t rntiP); int find_nr_RA_id(module_id_t mod_idP, int CC_idP, rnti_t rntiP); -int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *secondaryCellGroup); +int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellGroup); void mac_remove_nr_ue(module_id_t mod_id, rnti_t rnti); @@ -329,6 +336,8 @@ uint16_t compute_pucch_prb_size(uint8_t format, uint8_t n_symb, uint8_t n_re_ctrl); +int nr_get_default_pucch_res(int pucch_ResourceCommon); + void compute_csi_bitlen(NR_CSI_MeasConfig_t *csi_MeasConfig, NR_UE_info_t *UE_info, int UE_id, module_id_t Mod_idP); int get_dlscs(nfapi_nr_config_request_t *cfg); diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index 60b0cac215..9974c08a18 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -96,12 +96,12 @@ void mac_top_init_gNB(void) // These should be out of here later pdcp_layer_init(); - if(IS_SOFTMODEM_NOS1 && !(get_softmodem_params()->do_ra || get_softmodem_params()->sa) ) + if(IS_SOFTMODEM_NOS1 && get_softmodem_params()->phy_test) nr_DRB_preconfiguration(0x1234); rrc_init_nr_global_param(); - }else { + } else { RC.nrmac = NULL; } diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index bd7304ad8f..1f9cfff1e8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -160,8 +160,8 @@ typedef struct { NR_SearchSpace_t *ra_ss; // Beam index uint8_t beam_id; - /// secondaryCellGroup for UE in NSA that is to come - NR_CellGroupConfig_t *secondaryCellGroup; + /// CellGroup for UE that is to come (NSA is non-null, null for SA) + NR_CellGroupConfig_t *CellGroup; /// Preambles for contention-free access NR_preamble_ue_t preambles; /// NSA: the UEs C-RNTI to use @@ -303,6 +303,7 @@ typedef struct NR_sched_pucch { uint8_t dai_c; uint8_t timing_indicator; uint8_t resource_indicator; + int r_pucch; } NR_sched_pucch_t; /* PUSCH semi-static configuration: as long as the TDA and DCI format remain @@ -557,6 +558,7 @@ typedef struct { /// per-LC status data mac_rlc_status_resp_t rlc_status[MAX_NUM_LCID]; + int lcid_mask; uint16_t ta_frame; int16_t ta_update; bool ta_apply; @@ -565,7 +567,11 @@ typedef struct { int raw_rssi; int pusch_snrx10; int pucch_snrx10; - + uint16_t ul_rssi; + uint8_t current_harq_pid; + int pusch_consecutive_dtx_cnt; + int pucch_consecutive_dtx_cnt; + int ul_failure; struct CSI_Report CSI_report[MAX_CSI_REPORTS]; bool SR; @@ -602,6 +608,7 @@ typedef struct { int dlsch_current_bytes; int ulsch_rounds[8]; int ulsch_errors; + int ulsch_DTX; int ulsch_total_bytes_scheduled; int ulsch_total_bytes_rx; int ulsch_current_bytes; @@ -622,13 +629,14 @@ typedef struct { bool active[MAX_MOBILES_PER_GNB]; rnti_t rnti[MAX_MOBILES_PER_GNB]; - NR_CellGroupConfig_t *secondaryCellGroup[MAX_MOBILES_PER_GNB]; + NR_CellGroupConfig_t *CellGroup[MAX_MOBILES_PER_GNB]; /// CCE indexing int Y[MAX_MOBILES_PER_GNB][3][160]; int m[MAX_MOBILES_PER_GNB]; int num_pdcch_cand[MAX_MOBILES_PER_GNB][MAX_NUM_CORESET]; // UE selected beam index uint8_t UE_beam_index[MAX_MOBILES_PER_GNB]; + bool Msg4_ACKed[MAX_MOBILES_PER_GNB]; } NR_UE_info_t; typedef void (*nr_pp_impl_dl)(module_id_t mod_id, @@ -654,6 +662,10 @@ typedef struct gNB_MAC_INST_s { int pusch_target_snrx10; /// Pucch target SNR int pucch_target_snrx10; + /// PUCCH Failure threshold (compared to consecutive PUCCH DTX) + int pucch_failure_thres; + /// PUSCH Failure threshold (compared to consecutive PUSCH DTX) + int pusch_failure_thres; /// Subcarrier Offset int ssb_SubcarrierOffset; /// Common cell resources @@ -733,7 +745,6 @@ typedef struct gNB_MAC_INST_s { nr_pp_impl_ul pre_processor_ul; NR_UE_sched_ctrl_t *sched_ctrlCommon; - NR_CellGroupConfig_t *secondaryCellGroupCommon; NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config[64]; } gNB_MAC_INST; diff --git a/openair2/LAYER2/PROTO_AGENT/proto_agent.h b/openair2/LAYER2/PROTO_AGENT/proto_agent.h index 6898c9e447..32bf5aa13c 100644 --- a/openair2/LAYER2/PROTO_AGENT/proto_agent.h +++ b/openair2/LAYER2/PROTO_AGENT/proto_agent.h @@ -30,6 +30,8 @@ #define PROTO_AGENT_H_ #include "ENB_APP/enb_config.h" // for enb properties #include "proto_agent_common.h" +#include "LAYER2/NR_MAC_gNB/nr_mac_gNB.h" +#include "RRC/NR/nr_rrc_defs.h" void *proto_agent_receive(void *args); diff --git a/openair2/LAYER2/PROTO_AGENT/proto_agent_common.c b/openair2/LAYER2/PROTO_AGENT/proto_agent_common.c index e0adfefa03..f19d7526fe 100644 --- a/openair2/LAYER2/PROTO_AGENT/proto_agent_common.c +++ b/openair2/LAYER2/PROTO_AGENT/proto_agent_common.c @@ -33,10 +33,9 @@ #include "PHY/phy_extern.h" #include "proto_agent_common.h" #include "common/utils/LOG/log.h" +#include "common/ran_context.h" -#include "RRC/LTE/rrc_extern.h" -#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h" -#include "rrc_eNB_UE_context.h" +extern RAN_CONTEXT_t RC; /* * message primitives @@ -364,28 +363,23 @@ int proto_agent_pdcp_data_req_process(mod_id_t mod_id, const void *params, Proto pdcp_pdu_size = rlc_data->fsp_pdu->fsp_pdu_data.len; pdcp_pdu_p = get_free_mem_block(pdcp_pdu_size, __func__); - if (!pdcp_pdu_p) goto error; + if (!pdcp_pdu_p) { + LOG_E(PROTO_AGENT, "%s: an error occured\n", __FUNCTION__); + return -1; + } memcpy(pdcp_pdu_p->data, rlc_data->fsp_pdu->fsp_pdu_data.data, pdcp_pdu_size); - result = rlc_data_req(&ctxt_pP - ,srb_flagP - ,flag_MBMS - ,rb_idP - ,muiP - ,confirmP - ,pdcp_pdu_size - ,pdcp_pdu_p - ,NULL - ,NULL - ); + if (RC.nrrrc) { + LOG_D(PROTO_AGENT, "proto_agent received pdcp_data_req \n"); + // for (int i = 0; i < pdcp_pdu_size; i++) + // printf(" %2.2x", (unsigned char)pdcp_pdu_p->data[i]); + // printf("\n"); + du_rlc_data_req(&ctxt_pP, srb_flagP, flag_MBMS, rb_idP, muiP, confirmP, pdcp_pdu_size, pdcp_pdu_p); + result = 1; + } else { + result = rlc_data_req(&ctxt_pP, srb_flagP, flag_MBMS, rb_idP, muiP, confirmP, pdcp_pdu_size, pdcp_pdu_p, NULL, NULL); + } return result; -error: - - if (pdcp_pdu_p) - free_mem_block(pdcp_pdu_p, __func__); - - LOG_E(PROTO_AGENT, "%s: an error occured\n", __FUNCTION__); - return -1; } int proto_agent_destroy_pdcp_data_ind(Protocol__FlexsplitMessage *msg) { @@ -531,6 +525,8 @@ int proto_agent_pdcp_data_ind_process(mod_id_t mod_id, const void *params, Proto ctxt_pP.rnti = ctxt->fsp_rnti; ctxt_pP.frame = ctxt->fsp_frame; ctxt_pP.subframe = ctxt->fsp_subframe; + ctxt_pP.configured = 1; + ctxt_pP.brOption = 0; ctxt_pP.eNB_index = ctxt->fsp_enb_index; srb_flagP = rlc_data->fsp_srb_flag; flag_MBMS = rlc_data->fsp_mbms_flag; diff --git a/openair2/LAYER2/RLC/rlc.h b/openair2/LAYER2/RLC/rlc.h index ef5f824ba0..1e5b7947d7 100644 --- a/openair2/LAYER2/RLC/rlc.h +++ b/openair2/LAYER2/RLC/rlc.h @@ -624,6 +624,14 @@ rlc_op_status_t rlc_stat_req ( */ int rlc_module_init(int enb_flag); +void du_rlc_data_req(const protocol_ctxt_t *const ctxt_pP, + const srb_flag_t srb_flagP, + const MBMS_flag_t MBMS_flagP, + const rb_id_t rb_idP, + const mui_t muiP, + confirm_t confirmP, + sdu_size_t sdu_sizeP, + mem_block_t *sdu_pP); /** @} */ #define RLC_FG_COLOR_BLACK "\e[0;30m" diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp.h b/openair2/LAYER2/nr_pdcp/nr_pdcp.h new file mode 100644 index 0000000000..94939a0421 --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp.h @@ -0,0 +1,30 @@ +/* + * 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 + +#ifndef _NR_PDCP_H_ +#define _NR_PDCP_H_ + +void nr_pdcp_layer_init_ue(void); +void nr_DRB_preconfiguration(uint16_t crnti); + +#endif /* _NR_PDCP_H_ */ diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c index 748f1526eb..0b5d0ad486 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c @@ -49,7 +49,7 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, return; } - if (!(buffer[0] & 0x80)) { + if (entity->type != NR_PDCP_SRB && !(buffer[0] & 0x80)) { LOG_E(PDCP, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__); exit(1); } @@ -65,7 +65,8 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, header_size = 3; } - if (entity->has_integrity) { + /* SRBs always have MAC-I, even if integrity is not active */ + if (entity->has_integrity || entity->type == NR_PDCP_SRB) { integrity_size = 4; } else { integrity_size = 0; @@ -99,9 +100,8 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, entity->integrity(entity->integrity_context, integrity, buffer, size - integrity_size, entity->rb_id, rcvd_count, entity->is_gnb ? 0 : 1); - if (memcmp(integrity, buffer, 4) != 0) { + if (memcmp(integrity, buffer + size - integrity_size, 4) != 0) { LOG_E(PDCP, "discard NR PDU, integrity failed\n"); - return; } } @@ -180,7 +180,8 @@ static void nr_pdcp_entity_recv_sdu(nr_pdcp_entity_t *entity, header_size = 3; } - if (entity->has_integrity) { + /* SRBs always have MAC-I, even if integrity is not active */ + if (entity->has_integrity || entity->type == NR_PDCP_SRB) { integrity_size = 4; } else { integrity_size = 0; @@ -194,6 +195,10 @@ static void nr_pdcp_entity_recv_sdu(nr_pdcp_entity_t *entity, (unsigned char *)buf, header_size + size, entity->rb_id, count, entity->is_gnb ? 1 : 0); + // set MAC-I to 0 for SRBs with integrity not active + else if (integrity_size == 4) + memset(buf + header_size + size, 0, 4); + if (entity->has_ciphering) entity->cipher(entity->security_context, (unsigned char *)buf + header_size, size + integrity_size, @@ -205,12 +210,61 @@ static void nr_pdcp_entity_recv_sdu(nr_pdcp_entity_t *entity, header_size + size + integrity_size, sdu_id); } -static void nr_pdcp_entity_set_integrity_key(nr_pdcp_entity_t *entity, - char *key) +/* may be called several times, take care to clean previous settings */ +static void nr_pdcp_entity_set_security(nr_pdcp_entity_t *entity, + int integrity_algorithm, + char *integrity_key, + int ciphering_algorithm, + char *ciphering_key) { - LOG_E(PDCP, "%s: %d: %s: TODO? to remove?\n", __FILE__, __LINE__, __FUNCTION__); - exit(1); - //memcpy(entity->integrity_key, key, 16); + if (integrity_algorithm != -1) + entity->integrity_algorithm = integrity_algorithm; + if (ciphering_algorithm != -1) + entity->ciphering_algorithm = ciphering_algorithm; + if (integrity_key != NULL) + memcpy(entity->integrity_key, integrity_key, 16); + if (ciphering_key != NULL) + memcpy(entity->ciphering_key, ciphering_key, 16); + + if (integrity_algorithm == 0) { + entity->has_integrity = 0; + if (entity->free_integrity != NULL) + entity->free_integrity(entity->integrity_context); + entity->free_integrity = NULL; + } + + if (integrity_algorithm != 0 && integrity_algorithm != -1) { + if (integrity_algorithm != 2) { + LOG_E(PDCP, "FATAL: only nia2 supported for the moment\n"); + exit(1); + } + entity->has_integrity = 1; + if (entity->free_integrity != NULL) + entity->free_integrity(entity->integrity_context); + entity->integrity_context = nr_pdcp_integrity_nia2_init(entity->integrity_key); + entity->integrity = nr_pdcp_integrity_nia2_integrity; + entity->free_integrity = nr_pdcp_integrity_nia2_free_integrity; + } + + if (ciphering_algorithm == 0) { + entity->has_ciphering = 0; + if (entity->free_security != NULL) + entity->free_security(entity->security_context); + entity->free_security = NULL; + } + + if (ciphering_algorithm != 0 && ciphering_algorithm != -1) { + if (ciphering_algorithm != 2) { + LOG_E(PDCP, "FATAL: only nea2 supported for the moment\n"); + exit(1); + } + entity->has_ciphering = 1; + if (entity->free_security != NULL) + entity->free_security(entity->security_context); + entity->security_context = nr_pdcp_security_nea2_init(entity->ciphering_key); + entity->cipher = nr_pdcp_security_nea2_cipher; + entity->free_security = nr_pdcp_security_nea2_free_security; + } } static void check_t_reordering(nr_pdcp_entity_t *entity) @@ -282,7 +336,8 @@ void nr_pdcp_entity_delete(nr_pdcp_entity_t *entity) nr_pdcp_entity_t *new_nr_pdcp_entity( nr_pdcp_entity_type_t type, - int is_gnb, int rb_id, + int is_gnb, int rb_id, int pdusession_id,int has_sdap, + int has_sdapULheader, int has_sdapDLheader, void (*deliver_sdu)(void *deliver_sdu_data, struct nr_pdcp_entity_t *entity, char *buf, int size), void *deliver_sdu_data, @@ -307,10 +362,10 @@ nr_pdcp_entity_t *new_nr_pdcp_entity( ret->type = type; - ret->recv_pdu = nr_pdcp_entity_recv_pdu; - ret->recv_sdu = nr_pdcp_entity_recv_sdu; - ret->set_integrity_key = nr_pdcp_entity_set_integrity_key; - ret->set_time = nr_pdcp_entity_set_time; + ret->recv_pdu = nr_pdcp_entity_recv_pdu; + ret->recv_sdu = nr_pdcp_entity_recv_sdu; + ret->set_security = nr_pdcp_entity_set_security; + ret->set_time = nr_pdcp_entity_set_time; ret->delete = nr_pdcp_entity_delete; @@ -321,6 +376,10 @@ nr_pdcp_entity_t *new_nr_pdcp_entity( ret->deliver_pdu_data = deliver_pdu_data; ret->rb_id = rb_id; + ret->pdusession_id = pdusession_id; + ret->has_sdap = has_sdap; + ret->has_sdapULheader = has_sdapULheader; + ret->has_sdapDLheader = has_sdapDLheader; ret->sn_size = sn_size; ret->t_reordering = t_reordering; ret->discard_timer = discard_timer; @@ -328,34 +387,11 @@ nr_pdcp_entity_t *new_nr_pdcp_entity( ret->sn_max = (1 << sn_size) - 1; ret->window_size = 1 << (sn_size - 1); - if (ciphering_key != NULL && ciphering_algorithm != 0) { - if (ciphering_algorithm != 2) { - LOG_E(PDCP, "FATAL: only nea2 supported for the moment\n"); - exit(1); - } - ret->has_ciphering = 1; - ret->ciphering_algorithm = ciphering_algorithm; - memcpy(ret->ciphering_key, ciphering_key, 16); - - ret->security_context = nr_pdcp_security_nea2_init(ciphering_key); - ret->cipher = nr_pdcp_security_nea2_cipher; - ret->free_security = nr_pdcp_security_nea2_free_security; - } ret->is_gnb = is_gnb; - if (integrity_key != NULL && integrity_algorithm != 0) { - if (integrity_algorithm != 2) { - LOG_E(PDCP, "FATAL: only nia2 supported for the moment\n"); - exit(1); - } - ret->has_integrity = 1; - ret->integrity_algorithm = integrity_algorithm; - memcpy(ret->integrity_key, integrity_key, 16); - - ret->integrity_context = nr_pdcp_integrity_nia2_init(integrity_key); - ret->integrity = nr_pdcp_integrity_nia2_integrity; - ret->free_integrity = nr_pdcp_integrity_nia2_free_integrity; - } + nr_pdcp_entity_set_security(ret, + integrity_algorithm, (char *)integrity_key, + ciphering_algorithm, (char *)ciphering_key); return ret; } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h index 2d391eb3e0..fbcc2b96cd 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h @@ -40,7 +40,18 @@ typedef struct nr_pdcp_entity_t { void (*recv_sdu)(struct nr_pdcp_entity_t *entity, char *buffer, int size, int sdu_id); void (*delete)(struct nr_pdcp_entity_t *entity); - void (*set_integrity_key)(struct nr_pdcp_entity_t *entity, char *key); + + /* set_security: pass -1 to integrity_algorithm / ciphering_algorithm + * to keep the current algorithm + * pass NULL to integrity_key / ciphering_key + * to keep the current key + */ + void (*set_security)(struct nr_pdcp_entity_t *entity, + int integrity_algorithm, + char *integrity_key, + int ciphering_algorithm, + char *ciphering_key); + void (*set_time)(struct nr_pdcp_entity_t *entity, uint64_t now); /* callbacks provided to the PDCP module */ @@ -53,7 +64,10 @@ typedef struct nr_pdcp_entity_t { /* configuration variables */ int rb_id; - + int pdusession_id; + int has_sdap; + int has_sdapULheader; + int has_sdapDLheader; int sn_size; /* SN size, in bits */ int t_reordering; /* unit: ms, -1 for infinity */ int discard_timer; /* unit: ms, -1 for infinity */ @@ -104,7 +118,8 @@ typedef struct nr_pdcp_entity_t { nr_pdcp_entity_t *new_nr_pdcp_entity( nr_pdcp_entity_type_t type, - int is_gnb, int rb_id, + int is_gnb, int rb_id, int pdusession_id,int has_sdap, + int has_sdapULheader,int has_sdapDLheader, void (*deliver_sdu)(void *deliver_sdu_data, struct nr_pdcp_entity_t *entity, char *buf, int size), void *deliver_sdu_data, diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.c new file mode 100644 index 0000000000..73840aeb0a --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.c @@ -0,0 +1,75 @@ +/* + * 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 "nr_pdcp_entity_srb.h" + +#include +#include +#include + +void nr_pdcp_entity_srb_recv_pdu(nr_pdcp_entity_t *_entity, char *buffer, int size) +{ + nr_pdcp_entity_srb_t *entity = (nr_pdcp_entity_srb_t *)_entity; + + if (size < 2) abort(); + entity->common.deliver_sdu(entity->common.deliver_sdu_data, + (nr_pdcp_entity_t *)entity, buffer+2, size-6); +} + +void nr_pdcp_entity_srb_recv_sdu(nr_pdcp_entity_t *_entity, char *buffer, int size, + int sdu_id) +{ + nr_pdcp_entity_srb_t *entity = (nr_pdcp_entity_srb_t *)_entity; + int sn; + char buf[size+6]; + + sn = entity->common.next_nr_pdcp_tx_sn; + + entity->common.next_nr_pdcp_tx_sn++; + if (entity->common.next_nr_pdcp_tx_sn > entity->common.maximum_nr_pdcp_sn) { + entity->common.next_nr_pdcp_tx_sn = 0; + entity->common.tx_hfn++; + } + + buf[0] = (sn >> 8) & 0x0f; + buf[1] = sn & 0xff; + memcpy(buf+2, buffer, size); + + /* For now use padding for the MAC-I bytes (normally carrying message authentication code) + * which come after the data payload bytes (38.323, section 6.2.2.1) */ + for (int i=size+2; icommon.deliver_pdu(entity->common.deliver_pdu_data, + (nr_pdcp_entity_t *)entity, buf, size+6, sdu_id); +} + +void nr_pdcp_entity_srb_set_integrity_key(nr_pdcp_entity_t *_entity, char *key) +{ + /* nothing to do */ +} + +void nr_pdcp_entity_srb_delete(nr_pdcp_entity_t *_entity) +{ + nr_pdcp_entity_srb_t *entity = (nr_pdcp_entity_srb_t *)_entity; + free(entity); +} + diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.h new file mode 100644 index 0000000000..f0f9d3e28f --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity_srb.h @@ -0,0 +1,38 @@ + /* + * 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 + */ + +#ifndef _NR_PDCP_ENTITY_SRB_H_ +#define _NR_PDCP_ENTITY_SRB_H_ + +#include "nr_pdcp_entity.h" + +typedef struct { + nr_pdcp_entity_t common; + int srb_id; +} nr_pdcp_entity_srb_t; + +void nr_pdcp_entity_srb_recv_pdu(nr_pdcp_entity_t *_entity, char *buffer, int size); +void nr_pdcp_entity_srb_recv_sdu(nr_pdcp_entity_t *_entity, char *buffer, int size, int sdu_id); +void nr_pdcp_entity_srb_set_integrity_key(nr_pdcp_entity_t *_entity, char *key); +void nr_pdcp_entity_srb_delete(nr_pdcp_entity_t *_entity); + + +#endif /* _NR_PDCP_ENTITY_SRB_H_ */ diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c index 5553662ac5..86f0558611 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c @@ -45,7 +45,7 @@ static void compute_t(unsigned char *t, uint32_t count, int bearer, t[1] = (count >> 16) & 255; t[2] = (count >> 8) & 255; t[3] = (count ) & 255; - t[4] = (bearer << 3) | (direction << 2); + t[4] = ((bearer-1) << 3) | (direction << 2); memset(&t[5], 0, 8-5); } @@ -63,6 +63,7 @@ void nr_pdcp_integrity_nia2_integrity(void *integrity_context, * (which is identical to 128-NIA2, see 33.501 D.3.1.3) */ compute_t(t, count, bearer, direction); + CMAC_Init(ctx, NULL, 0, NULL, NULL); CMAC_Update(ctx, t, 8); CMAC_Update(ctx, buffer, length); CMAC_Final(ctx, mac, &maclen); diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index c106d8e4fd..1624c395ef 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -30,6 +30,7 @@ #include "NR_RLC-Config.h" #include "NR_CellGroupConfig.h" #include "openair2/RRC/NR/nr_rrc_proto.h" +#include /* from OAI */ #include "pdcp.h" @@ -52,6 +53,9 @@ static int nr_pdcp_current_time_last_subframe; hash_table_t *pdcp_coll_p; static uint64_t pdcp_optmask; +ngran_node_t node_type = ngran_gNB; +uint8_t proto_agent_flag = 0; + /****************************************************************************/ /* rlc_data_req queue - begin */ /****************************************************************************/ @@ -186,6 +190,26 @@ static void enqueue_rlc_data_req(const protocol_ctxt_t *const ctxt_pP, if (pthread_mutex_unlock(&q.m) != 0) abort(); } +void du_rlc_data_req(const protocol_ctxt_t *const ctxt_pP, + const srb_flag_t srb_flagP, + const MBMS_flag_t MBMS_flagP, + const rb_id_t rb_idP, + const mui_t muiP, + confirm_t confirmP, + sdu_size_t sdu_sizeP, + mem_block_t *sdu_pP) +{ + enqueue_rlc_data_req(ctxt_pP, + srb_flagP, + MBMS_flagP, + rb_idP, muiP, + confirmP, + sdu_sizeP, + sdu_pP, + NULL, + NULL); +} + /****************************************************************************/ /* rlc_data_req queue - end */ /****************************************************************************/ @@ -345,7 +369,10 @@ void pdcp_layer_init(void) if (pthread_mutex_unlock(&m) != 0) abort(); nr_pdcp_ue_manager = new_nr_pdcp_ue_manager(1); - init_nr_rlc_data_req_queue(); + + if ((RC.nrrrc == NULL) || (!NODE_IS_CU(RC.nrrrc[0]->node_type))) { + init_nr_rlc_data_req_queue(); + } nr_pdcp_init_timer_thread(nr_pdcp_ue_manager); } @@ -437,22 +464,25 @@ static void deliver_sdu_drb(void *_ue, nr_pdcp_entity_t *entity, exit(1); rb_found: - gtpu_buffer_p = itti_malloc(TASK_PDCP_ENB, TASK_GTPV1_U, - size + GTPU_HEADER_OVERHEAD_MAX); - AssertFatal(gtpu_buffer_p != NULL, "OUT OF MEMORY"); - memcpy(>pu_buffer_p[GTPU_HEADER_OVERHEAD_MAX], buf, size); - message_p = itti_alloc_new_message(TASK_PDCP_ENB, 0, GTPV1U_ENB_TUNNEL_DATA_REQ); - AssertFatal(message_p != NULL, "OUT OF MEMORY"); - GTPV1U_ENB_TUNNEL_DATA_REQ(message_p).buffer = gtpu_buffer_p; - GTPV1U_ENB_TUNNEL_DATA_REQ(message_p).length = size; - GTPV1U_ENB_TUNNEL_DATA_REQ(message_p).offset = GTPU_HEADER_OVERHEAD_MAX; - GTPV1U_ENB_TUNNEL_DATA_REQ(message_p).rnti = ue->rnti; - GTPV1U_ENB_TUNNEL_DATA_REQ(message_p).rab_id = rb_id + 4; - LOG_D(PDCP, "%s() (drb %d) sending message to gtp size %d\n", __func__, rb_id, size); - //for (i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]); - //printf("\n"); - itti_send_msg_to_task(TASK_VARIABLE, INSTANCE_DEFAULT, message_p); + { + int offset=0; + if (entity->has_sdap == 1 && entity->has_sdapULheader == 1) offset = 1; // this is the offset of the SDAP header in bytes + gtpu_buffer_p = itti_malloc(TASK_PDCP_ENB, TASK_GTPV1_U, + size + GTPU_HEADER_OVERHEAD_MAX - offset); + AssertFatal(gtpu_buffer_p != NULL, "OUT OF MEMORY"); + memcpy(>pu_buffer_p[GTPU_HEADER_OVERHEAD_MAX], buf+offset, size-offset); + message_p = itti_alloc_new_message(TASK_PDCP_ENB, 0, GTPV1U_GNB_TUNNEL_DATA_REQ); + AssertFatal(message_p != NULL, "OUT OF MEMORY"); + GTPV1U_GNB_TUNNEL_DATA_REQ(message_p).buffer = gtpu_buffer_p; + GTPV1U_GNB_TUNNEL_DATA_REQ(message_p).length = size-offset; + GTPV1U_GNB_TUNNEL_DATA_REQ(message_p).offset = GTPU_HEADER_OVERHEAD_MAX; + GTPV1U_GNB_TUNNEL_DATA_REQ(message_p).rnti = ue->rnti; + GTPV1U_GNB_TUNNEL_DATA_REQ(message_p).pdusession_id = entity->pdusession_id; + if (offset==1) LOG_I(PDCP, "%s() (drb %d) SDAP header %2x\n",__func__, rb_id, buf[0]); + LOG_D(PDCP, "%s() (drb %d) sending message to gtp size %d\n", __func__, rb_id, size-offset); + itti_send_msg_to_task(TASK_VARIABLE, INSTANCE_DEFAULT, message_p); + } } } @@ -497,6 +527,105 @@ rb_found: enqueue_rlc_data_req(&ctxt, 0, MBMS_FLAG_NO, rb_id, sdu_id, 0, size, memblock, NULL, NULL); } +static void deliver_sdu_srb(void *_ue, nr_pdcp_entity_t *entity, + char *buf, int size) +{ + nr_pdcp_ue_t *ue = _ue; + int srb_id; + int i; + + for (i = 0; i < 2; i++) { + if (entity == ue->srb[i]) { + srb_id = i+1; + goto srb_found; + } + } + + LOG_E(PDCP, "%s:%d:%s: fatal, no SRB found for ue %d\n", + __FILE__, __LINE__, __FUNCTION__, ue->rnti); + exit(1); + + srb_found: + { + uint8_t *rrc_buffer_p = entity->is_gnb ? + itti_malloc(TASK_PDCP_ENB, TASK_RRC_GNB, size): + itti_malloc(TASK_PDCP_UE, TASK_RRC_NRUE, size); + MessageDef *message_p; + + AssertFatal(rrc_buffer_p != NULL, "OUT OF MEMORY"); + memcpy(rrc_buffer_p, buf, size); + message_p = entity->is_gnb ? + itti_alloc_new_message(TASK_PDCP_ENB, 0, NR_RRC_DCCH_DATA_IND): + itti_alloc_new_message(TASK_PDCP_UE, 0, NR_RRC_DCCH_DATA_IND); + + AssertFatal(message_p != NULL, "OUT OF MEMORY"); + NR_RRC_DCCH_DATA_IND(message_p).dcch_index = srb_id; + NR_RRC_DCCH_DATA_IND(message_p).sdu_p = rrc_buffer_p; + NR_RRC_DCCH_DATA_IND(message_p).sdu_size = size; + NR_RRC_DCCH_DATA_IND(message_p).rnti = ue->rnti; + + itti_send_msg_to_task(entity->is_gnb ? TASK_RRC_GNB : TASK_RRC_NRUE, 0, message_p); + } +} + +static void deliver_pdu_srb(void *_ue, nr_pdcp_entity_t *entity, + char *buf, int size, int sdu_id) +{ + nr_pdcp_ue_t *ue = _ue; + int srb_id; + protocol_ctxt_t ctxt; + int i; + mem_block_t *memblock; + + for (i = 0; i < 2; i++) { + if (entity == ue->srb[i]) { + srb_id = i+1; + goto srb_found; + } + } + + LOG_E(PDCP, "%s:%d:%s: fatal, no SRB found for ue %d\n", + __FILE__, __LINE__, __FUNCTION__, ue->rnti); + exit(1); + +srb_found: + + + LOG_D(PDCP, "%s(): (srb %d) calling rlc_data_req size %d\n", __func__, srb_id, size); + //for (i = 0; i < size; i++) printf(" %2.2x", (unsigned char)memblock->data[i]); + //printf("\n"); + if ((RC.nrrrc == NULL) || (!NODE_IS_CU(RC.nrrrc[0]->node_type))) { + ctxt.module_id = 0; + ctxt.enb_flag = 1; + ctxt.instance = 0; + ctxt.frame = 0; + ctxt.subframe = 0; + ctxt.eNB_index = 0; + ctxt.configured = 1; + ctxt.brOption = 0; + + ctxt.rnti = ue->rnti; + + memblock = get_free_mem_block(size, __FUNCTION__); + memcpy(memblock->data, buf, size); + enqueue_rlc_data_req(&ctxt, 1, MBMS_FLAG_NO, srb_id, sdu_id, 0, size, memblock, NULL, NULL); + } + else { + MessageDef *message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, F1AP_DL_RRC_MESSAGE); + F1AP_DL_RRC_MESSAGE (message_p).rrc_container = (uint8_t*)buf; + F1AP_DL_RRC_MESSAGE (message_p).rrc_container_length = size; + F1AP_DL_RRC_MESSAGE (message_p).gNB_CU_ue_id = 0; + F1AP_DL_RRC_MESSAGE (message_p).gNB_DU_ue_id = 0; + F1AP_DL_RRC_MESSAGE (message_p).old_gNB_DU_ue_id = 0xFFFFFFFF; // unknown + F1AP_DL_RRC_MESSAGE (message_p).rnti = ue->rnti; + F1AP_DL_RRC_MESSAGE (message_p).srb_id = srb_id; + F1AP_DL_RRC_MESSAGE (message_p).execute_duplication = 1; + F1AP_DL_RRC_MESSAGE (message_p).RAT_frequency_priority_information.en_dc = 0; + itti_send_msg_to_task (TASK_CU_F1, 0, message_p); + LOG_D(PDCP, "Send F1AP_DL_RRC_MESSAGE with ITTI\n"); + } +} + boolean_t pdcp_data_ind( const protocol_ctxt_t *const ctxt_pP, const srb_flag_t srb_flagP, @@ -557,14 +686,26 @@ void pdcp_run(const protocol_ctxt_t *const ctxt_pP) { MessageDef *msg_p; int result; - protocol_ctxt_t ctxt; + protocol_ctxt_t ctxt={.module_id=0, + .enb_flag=1, + .instance=0, + .rnti=0, + .frame=-1, + .subframe=-1, + .eNB_index=0, + .configured=true, + .brOption=false + }; + while (1) { itti_poll_msg(ctxt_pP->enb_flag ? TASK_PDCP_ENB : TASK_PDCP_UE, &msg_p); - if (msg_p == NULL) - break; + if (msg_p == NULL){ + break; + } switch (ITTI_MSG_ID(msg_p)) { case RRC_DCCH_DATA_REQ: + LOG_D(PDCP, "Received RRC_DCCH_DATA_REQ type at PDCP task \n"); PROTOCOL_CTXT_SET_BY_MODULE_ID( &ctxt, RRC_DCCH_DATA_REQ(msg_p).module_id, @@ -595,9 +736,34 @@ void pdcp_run(const protocol_ctxt_t *const ctxt_pP) } } -static void add_srb(int rnti, struct NR_SRB_ToAddMod *s) +static void add_srb(int is_gnb, int rnti, struct NR_SRB_ToAddMod *s) { - TODO; + nr_pdcp_entity_t *pdcp_srb; + nr_pdcp_ue_t *ue; + int t_Reordering=3000; + + int srb_id = s->srb_Identity; + if (s->pdcp_Config == NULL || + s->pdcp_Config->t_Reordering == NULL) t_Reordering = 3000; + else t_Reordering = decode_t_reordering(*s->pdcp_Config->t_Reordering); + + nr_pdcp_manager_lock(nr_pdcp_ue_manager); + ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, rnti); + if (ue->srb[srb_id-1] != NULL) { + LOG_D(PDCP, "%s:%d:%s: warning SRB %d already exist for ue %d, do nothing\n", + __FILE__, __LINE__, __FUNCTION__, srb_id, rnti); + } else { + pdcp_srb = new_nr_pdcp_entity(NR_PDCP_SRB, is_gnb, srb_id, + 0, 0, 0, 0, // sdap parameters + deliver_sdu_srb, ue, deliver_pdu_srb, ue, + 12, t_Reordering, -1, + 0, 0, + NULL, NULL); + nr_pdcp_ue_add_srb_pdcp_entity(ue, srb_id, pdcp_srb); + + LOG_D(PDCP, "%s:%d:%s: added srb %d to ue rnti %x\n", __FILE__, __LINE__, __FUNCTION__, srb_id, rnti); + } + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); } static void add_drb_am(int is_gnb, int rnti, struct NR_DRB_ToAddMod *s, @@ -610,17 +776,41 @@ static void add_drb_am(int is_gnb, int rnti, struct NR_DRB_ToAddMod *s, nr_pdcp_ue_t *ue; int drb_id = s->drb_Identity; - int t_reordering; int sn_size_ul = decode_sn_size_ul(*s->pdcp_Config->drb->pdcp_SN_SizeUL); int sn_size_dl = decode_sn_size_dl(*s->pdcp_Config->drb->pdcp_SN_SizeDL); int discard_timer = decode_discard_timer(*s->pdcp_Config->drb->discardTimer); /* if pdcp_Config->t_Reordering is not present, it means infinity (-1) */ - if (s->pdcp_Config->t_Reordering != NULL) + int t_reordering = -1; + if (s->pdcp_Config->t_Reordering != NULL) { t_reordering = decode_t_reordering(*s->pdcp_Config->t_Reordering); - else - t_reordering = -1; + } + if ((!s->cnAssociation) || s->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_NOTHING) { + LOG_E(PDCP,"%s:%d:%s: fatal, cnAssociation is missing or present is NR_DRB_ToAddMod__cnAssociation_PR_NOTHING\n",__FILE__,__LINE__,__FUNCTION__); + exit(-1); + } + + int pdusession_id; + int has_sdap = 0; + int has_sdapULheader=0; + int has_sdapDLheader=0; + if (s->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity) + pdusession_id = s->cnAssociation->choice.eps_BearerIdentity; + else { + if (!s->cnAssociation->choice.sdap_Config) { + LOG_E(PDCP,"%s:%d:%s: fatal, sdap_Config is null",__FILE__,__LINE__,__FUNCTION__); + exit(-1); + } + pdusession_id = s->cnAssociation->choice.sdap_Config->pdu_Session; + has_sdap = 1; + has_sdapULheader = s->cnAssociation->choice.sdap_Config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present ? 1 : 0; + has_sdapDLheader = s->cnAssociation->choice.sdap_Config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present ? 1 : 0; + if (has_sdapDLheader==1) { + LOG_E(PDCP,"%s:%d:%s: fatal, no support for SDAP DL yet\n",__FILE__,__LINE__,__FUNCTION__); + exit(-1); + } + } /* TODO(?): accept different UL and DL SN sizes? */ if (sn_size_ul != sn_size_dl) { LOG_E(PDCP, "%s:%d:%s: fatal, bad SN sizes, must be same. ul=%d, dl=%d\n", @@ -640,7 +830,8 @@ static void add_drb_am(int is_gnb, int rnti, struct NR_DRB_ToAddMod *s, LOG_D(PDCP, "%s:%d:%s: warning DRB %d already exist for ue %d, do nothing\n", __FILE__, __LINE__, __FUNCTION__, drb_id, rnti); } else { - pdcp_drb = new_nr_pdcp_entity(NR_PDCP_DRB_AM, is_gnb, drb_id, + pdcp_drb = new_nr_pdcp_entity(NR_PDCP_DRB_AM, is_gnb, drb_id,pdusession_id,has_sdap, + has_sdapULheader,has_sdapDLheader, deliver_sdu_drb, ue, deliver_pdu_drb, ue, sn_size_dl, t_reordering, discard_timer, ciphering_algorithm, integrity_algorithm, @@ -717,7 +908,7 @@ boolean_t nr_rrc_pdcp_config_asn1_req( if (srb2add_list != NULL) { for (i = 0; i < srb2add_list->list.count; i++) { - add_srb(rnti, srb2add_list->list.array[i]); + add_srb(ctxt_pP->enb_flag,rnti, srb2add_list->list.array[i]); } } @@ -816,7 +1007,7 @@ void nr_DRB_preconfiguration(uint16_t crnti) } NR_RLC_BearerConfig_t *RLC_BearerConfig = calloc(1,sizeof(*RLC_BearerConfig)); - nr_rlc_bearer_init(RLC_BearerConfig); + nr_rlc_bearer_init(RLC_BearerConfig,NR_RLC_BearerConfig__servedRadioBearer_PR_drb_Identity); nr_drb_config(RLC_BearerConfig->rlc_Config, NR_RLC_Config_PR_um_Bi_Directional); nr_rlc_bearer_init_ul_spec(RLC_BearerConfig->mac_LogicalChannelConfig); @@ -835,7 +1026,7 @@ void nr_DRB_preconfiguration(uint16_t crnti) (NR_SRB_ToAddModList_t *) NULL, rbconfig->drb_ToAddModList , rbconfig->drb_ToReleaseList, - 0xff, + 0, NULL, NULL, NULL, @@ -882,37 +1073,87 @@ void pdcp_config_set_security( uint8_t *const kRRCint_pP, uint8_t *const kUPenc_pP) { - DevAssert(pdcp_pP != NULL); + nr_pdcp_ue_t *ue; + nr_pdcp_entity_t *rb; + int rnti = ctxt_pP->rnti; + int integrity_algorithm; + int ciphering_algorithm; - if ((security_modeP >= 0) && (security_modeP <= 0x77)) { - pdcp_pP->cipheringAlgorithm = security_modeP & 0x0f; - pdcp_pP->integrityProtAlgorithm = (security_modeP>>4) & 0xf; - LOG_D(PDCP, PROTOCOL_PDCP_CTXT_FMT" CONFIG_ACTION_SET_SECURITY_MODE: cipheringAlgorithm %d integrityProtAlgorithm %d\n", - PROTOCOL_PDCP_CTXT_ARGS(ctxt_pP,pdcp_pP), - pdcp_pP->cipheringAlgorithm, - pdcp_pP->integrityProtAlgorithm); - pdcp_pP->kRRCenc = kRRCenc_pP; - pdcp_pP->kRRCint = kRRCint_pP; - pdcp_pP->kUPenc = kUPenc_pP; - /* Activate security */ - pdcp_pP->security_activated = 1; - MSC_LOG_EVENT( - (ctxt_pP->enb_flag == ENB_FLAG_YES) ? MSC_PDCP_ENB:MSC_PDCP_UE, - "0 Set security ciph %X integ %x UE %"PRIx16" ", - pdcp_pP->cipheringAlgorithm, - pdcp_pP->integrityProtAlgorithm, - ctxt_pP->rnti); + nr_pdcp_manager_lock(nr_pdcp_ue_manager); + + ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, rnti); + + /* TODO: proper handling of DRBs, for the moment only SRBs are handled */ + + if (rb_id >= 1 && rb_id <= 2) { + rb = ue->srb[rb_id - 1]; + + if (rb == NULL) { + LOG_E(PDCP, "%s:%d:%s: no SRB found (rnti %d, rb_id %ld)\n", + __FILE__, __LINE__, __FUNCTION__, rnti, rb_id); + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + return; + } + + integrity_algorithm = (security_modeP>>4) & 0xf; + ciphering_algorithm = security_modeP & 0x0f; + rb->set_security(rb, integrity_algorithm, (char *)kRRCint_pP, + ciphering_algorithm, (char *)kRRCenc_pP); } else { - MSC_LOG_EVENT( - (ctxt_pP->enb_flag == ENB_FLAG_YES) ? MSC_PDCP_ENB:MSC_PDCP_UE, - "0 Set security failed UE %"PRIx16" ", - ctxt_pP->rnti); - LOG_E(PDCP,PROTOCOL_PDCP_CTXT_FMT" bad security mode %d", - PROTOCOL_PDCP_CTXT_ARGS(ctxt_pP,pdcp_pP), - security_modeP); + LOG_E(PDCP, "%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); + exit(1); } + + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); } +static boolean_t pdcp_data_req_srb( + protocol_ctxt_t *ctxt_pP, + const rb_id_t rb_id, + const mui_t muiP, + const confirm_t confirmP, + const sdu_size_t sdu_buffer_size, + unsigned char *const sdu_buffer) +{ + LOG_D(PDCP, "%s() called, size %d\n", __func__, sdu_buffer_size); + nr_pdcp_ue_t *ue; + nr_pdcp_entity_t *rb; + int rnti = ctxt_pP->rnti; + + if (ctxt_pP->module_id != 0 || + //ctxt_pP->enb_flag != 1 || + ctxt_pP->instance != 0 || + ctxt_pP->eNB_index != 0 /*|| + ctxt_pP->configured != 1 || + ctxt_pP->brOption != 0*/) { + LOG_E(PDCP, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__); + exit(1); + } + + nr_pdcp_manager_lock(nr_pdcp_ue_manager); + + ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, rnti); + + if (rb_id < 1 || rb_id > 2) + rb = NULL; + else + rb = ue->srb[rb_id - 1]; + + if (rb == NULL) { + LOG_E(PDCP, "%s:%d:%s: no SRB found (rnti %d, rb_id %ld)\n", + __FILE__, __LINE__, __FUNCTION__, rnti, rb_id); + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + return 0; + } + + rb->recv_sdu(rb, (char *)sdu_buffer, sdu_buffer_size, muiP); + + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + + return 1; +} + + static boolean_t pdcp_data_req_drb( protocol_ctxt_t *ctxt_pP, const rb_id_t rb_id, @@ -973,9 +1214,12 @@ boolean_t pdcp_data_req( #endif ) { - if (srb_flagP) { TODO; } - return pdcp_data_req_drb(ctxt_pP, rb_id, muiP, confirmP, sdu_buffer_size, - sdu_buffer); + if (srb_flagP) { + return pdcp_data_req_srb(ctxt_pP, rb_id, muiP, confirmP, sdu_buffer_size, sdu_buffer); + } + else{ + return pdcp_data_req_drb(ctxt_pP, rb_id, muiP, confirmP, sdu_buffer_size, sdu_buffer); + } } void pdcp_set_pdcp_data_ind_func(pdcp_data_ind_func_t pdcp_data_ind) diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c index f89a18f94e..160c5ebb22 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c @@ -85,7 +85,7 @@ nr_pdcp_ue_t *nr_pdcp_manager_get_ue(nr_pdcp_ue_manager_t *_m, int rnti) if (m->ue_list[i]->rnti == rnti) return m->ue_list[i]; - LOG_D(PDCP, "%s:%d:%s: new UE %d\n", __FILE__, __LINE__, __FUNCTION__, rnti); + LOG_D(PDCP, "%s:%d:%s: new UE 0x%x\n", __FILE__, __LINE__, __FUNCTION__, rnti); m->ue_count++; m->ue_list = realloc(m->ue_list, sizeof(nr_pdcp_ue_t *) * m->ue_count); diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 7cd1a87784..2a6bcf74ae 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -35,6 +35,15 @@ #include "NR_DRB-ToReleaseList.h" #include "NR_CellGroupConfig.h" #include "NR_RLC-Config.h" +#include "common/ran_context.h" +#include "NR_UL-CCCH-Message.h" + +#undef C_RNTI // C_RNTI is used in F1AP generated code, prevent preprocessor replace +#include "openair2/F1AP/f1ap_du_rrc_message_transfer.h" + +#include "openair2/LAYER2/PROTO_AGENT/proto_agent.h" + +extern RAN_CONTEXT_t RC; #include @@ -45,18 +54,25 @@ static uint64_t nr_rlc_current_time; static int nr_rlc_current_time_last_frame; static int nr_rlc_current_time_last_subframe; -void nr_rlc_bearer_init(NR_RLC_BearerConfig_t *RLC_BearerConfig){ +void nr_rlc_bearer_init(NR_RLC_BearerConfig_t *RLC_BearerConfig, NR_RLC_BearerConfig__servedRadioBearer_PR rb_type){ RLC_BearerConfig->servedRadioBearer = calloc(1, sizeof(*RLC_BearerConfig->servedRadioBearer)); RLC_BearerConfig->reestablishRLC = calloc(1, sizeof(*RLC_BearerConfig->reestablishRLC)); RLC_BearerConfig->rlc_Config = calloc(1, sizeof(*RLC_BearerConfig->rlc_Config)); RLC_BearerConfig->mac_LogicalChannelConfig = calloc(1, sizeof(*RLC_BearerConfig->mac_LogicalChannelConfig)); - RLC_BearerConfig->logicalChannelIdentity = 4; - RLC_BearerConfig->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_drb_Identity; - RLC_BearerConfig->servedRadioBearer->choice.drb_Identity = 1; *RLC_BearerConfig->reestablishRLC = NR_RLC_BearerConfig__reestablishRLC_true; + if(rb_type == NR_RLC_BearerConfig__servedRadioBearer_PR_drb_Identity){ + RLC_BearerConfig->logicalChannelIdentity = 4; + RLC_BearerConfig->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_drb_Identity; + RLC_BearerConfig->servedRadioBearer->choice.drb_Identity = 1; + } + else{ + RLC_BearerConfig->logicalChannelIdentity = 1; + RLC_BearerConfig->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity; + RLC_BearerConfig->servedRadioBearer->choice.srb_Identity = 1; + } } @@ -160,7 +176,7 @@ void mac_rlc_data_ind ( } else { LOG_E(RLC, "%s:%d:%s: fatal: no RB found (channel ID %d)\n", __FILE__, __LINE__, __FUNCTION__, channel_idP); - exit(1); + // exit(1); } nr_rlc_manager_unlock(nr_rlc_ue_manager); @@ -461,6 +477,27 @@ rb_found: T(T_ENB_RLC_UL, T_INT(0 /*ctxt_pP->module_id*/), T_INT(ue->rnti), T_INT(rb_id), T_INT(size)); + + const ngran_node_t type = RC.nrrrc[0 /*ctxt_pP->module_id*/]->node_type; + AssertFatal(type != ngran_eNB_CU && type != ngran_ng_eNB_CU && type != ngran_gNB_CU, + "Can't be CU, bad node type %d\n", type); + + // if (NODE_IS_DU(type) && is_srb == 0) { + // LOG_D(RLC, "call proto_agent_send_pdcp_data_ind() \n"); + // proto_agent_send_pdcp_data_ind(&ctx, is_srb, 0, rb_id, size, memblock); + // return; + // } + + if (NODE_IS_DU(type) && is_srb == 1) { + MessageDef *msg; + msg = itti_alloc_new_message(TASK_RLC_ENB, 0, F1AP_UL_RRC_MESSAGE); + F1AP_UL_RRC_MESSAGE(msg).rnti = ue->rnti; + F1AP_UL_RRC_MESSAGE(msg).srb_id = rb_id; + F1AP_UL_RRC_MESSAGE(msg).rrc_container = (unsigned char *)buf; + F1AP_UL_RRC_MESSAGE(msg).rrc_container_length = size; + itti_send_msg_to_task(TASK_DU_F1, ENB_MODULE_ID_TO_INSTANCE(0 /*ctxt_pP->module_id*/), msg); + return; + } } if (!pdcp_data_ind(&ctx, is_srb, 0, rb_id, size, memblock)) { @@ -584,14 +621,15 @@ rb_found: #endif } -static void add_srb(int rnti, struct LTE_SRB_ToAddMod *s) +static void add_rlc_srb(int rnti, struct NR_SRB_ToAddMod *s, NR_RLC_BearerConfig_t *rlc_BearerConfig) { nr_rlc_entity_t *nr_rlc_am; nr_rlc_ue_t *ue; - struct LTE_SRB_ToAddMod__rlc_Config *r = s->rlc_Config; - struct LTE_SRB_ToAddMod__logicalChannelConfig *l = s->logicalChannelConfig; + struct NR_RLC_Config *r = rlc_BearerConfig->rlc_Config; + struct NR_LogicalChannelConfig *l = rlc_BearerConfig->mac_LogicalChannelConfig; int srb_id = s->srb_Identity; + int channel_id = rlc_BearerConfig->logicalChannelIdentity; int logical_channel_group; int t_status_prohibit; @@ -602,25 +640,21 @@ static void add_srb(int rnti, struct LTE_SRB_ToAddMod *s) int t_reassembly; int sn_field_length; + LOG_D(RLC,"Trying to add SRB %d\n",srb_id); if (srb_id != 1 && srb_id != 2) { LOG_E(RLC, "%s:%d:%s: fatal, bad srb id %d\n", - __FILE__, __LINE__, __FUNCTION__, srb_id); + __FILE__, __LINE__, __FUNCTION__, srb_id); exit(1); } - switch (l->present) { - case LTE_SRB_ToAddMod__logicalChannelConfig_PR_explicitValue: - logical_channel_group = *l->choice.explicitValue.ul_SpecificParameters->logicalChannelGroup; - break; - case LTE_SRB_ToAddMod__logicalChannelConfig_PR_defaultValue: - /* default value from 36.331 9.2.1 */ - logical_channel_group = 0; - break; - default: - LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__); + if (channel_id != srb_id) { + LOG_E(RLC, "%s:%d:%s: todo, remove this limitation\n", + __FILE__, __LINE__, __FUNCTION__); exit(1); } + logical_channel_group = *l->ul_SpecificParameters->logicalChannelGroup; + /* TODO: accept other values? */ if (logical_channel_group != 0) { LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__); @@ -628,29 +662,22 @@ static void add_srb(int rnti, struct LTE_SRB_ToAddMod *s) } switch (r->present) { - case LTE_SRB_ToAddMod__rlc_Config_PR_explicitValue: { - struct LTE_RLC_Config__am *am; - if (r->choice.explicitValue.present != LTE_RLC_Config_PR_am) { - LOG_E(RLC, "%s:%d:%s: fatal error, must be RLC AM\n", - __FILE__, __LINE__, __FUNCTION__); - exit(1); - } - am = &r->choice.explicitValue.choice.am; + case NR_RLC_Config_PR_am: { + struct NR_RLC_Config__am *am; + am = r->choice.am; + t_reassembly = decode_t_reassembly(am->dl_AM_RLC.t_Reassembly); t_status_prohibit = decode_t_status_prohibit(am->dl_AM_RLC.t_StatusProhibit); t_poll_retransmit = decode_t_poll_retransmit(am->ul_AM_RLC.t_PollRetransmit); poll_pdu = decode_poll_pdu(am->ul_AM_RLC.pollPDU); poll_byte = decode_poll_byte(am->ul_AM_RLC.pollByte); max_retx_threshold = decode_max_retx_threshold(am->ul_AM_RLC.maxRetxThreshold); + if (*am->dl_AM_RLC.sn_FieldLength != *am->ul_AM_RLC.sn_FieldLength) { + LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__); + exit(1); + } + sn_field_length = decode_sn_field_length_am(*am->dl_AM_RLC.sn_FieldLength); break; } - case LTE_SRB_ToAddMod__rlc_Config_PR_defaultValue: - /* default values from 36.331 9.2.1 */ - t_status_prohibit = 0; - t_poll_retransmit = 45; - poll_pdu = -1; - poll_byte = -1; - max_retx_threshold = 4; - break; default: LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__); exit(1); @@ -659,7 +686,7 @@ static void add_srb(int rnti, struct LTE_SRB_ToAddMod *s) nr_rlc_manager_lock(nr_rlc_ue_manager); ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rnti); if (ue->srb[srb_id-1] != NULL) { - LOG_W(RLC, "%s:%d:%s: SRB %d already exists for UE with RNTI %x, do nothing\n", __FILE__, __LINE__, __FUNCTION__, srb_id, rnti); + LOG_W(RLC, "%s:%d:%s: SRB %d already exists for UE with RNTI 0x%x, do nothing\n", __FILE__, __LINE__, __FUNCTION__, srb_id, rnti); } else { /* hack: hardcode values for NR */ t_poll_retransmit = 45; @@ -680,8 +707,7 @@ static void add_srb(int rnti, struct LTE_SRB_ToAddMod *s) sn_field_length); nr_rlc_ue_add_srb_rlc_entity(ue, srb_id, nr_rlc_am); - LOG_D(RLC, "%s:%d:%s: added srb %d to ue %d\n", - __FILE__, __LINE__, __FUNCTION__, srb_id, rnti); + LOG_D(RLC, "%s:%d:%s: added srb %d to UE with RNTI 0x%x\n", __FILE__, __LINE__, __FUNCTION__, srb_id, rnti); } nr_rlc_manager_unlock(nr_rlc_ue_manager); } @@ -722,7 +748,7 @@ static void add_drb_am(int rnti, struct NR_DRB_ToAddMod *s, NR_RLC_BearerConfig_ /* TODO: accept other values? */ if (logical_channel_group != 1) { LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__); - exit(1); + //exit(1); } switch (r->present) { @@ -763,7 +789,7 @@ static void add_drb_am(int rnti, struct NR_DRB_ToAddMod *s, NR_RLC_BearerConfig_ sn_field_length); nr_rlc_ue_add_drb_rlc_entity(ue, drb_id, nr_rlc_am); - LOG_D(RLC, "%s:%d:%s: added drb %d to UE with RNTI %x\n", __FILE__, __LINE__, __FUNCTION__, drb_id, rnti); + LOG_D(RLC, "%s:%d:%s: added drb %d to UE with RNTI 0x%x\n", __FILE__, __LINE__, __FUNCTION__, drb_id, rnti); } nr_rlc_manager_unlock(nr_rlc_ue_manager); } @@ -831,7 +857,7 @@ static void add_drb_um(int rnti, struct NR_DRB_ToAddMod *s, NR_RLC_BearerConfig_ sn_field_length); nr_rlc_ue_add_drb_rlc_entity(ue, drb_id, nr_rlc_um); - LOG_D(RLC, "%s:%d:%s: added drb %d to UE with RNTI %x\n", __FILE__, __LINE__, __FUNCTION__, drb_id, rnti); + LOG_D(RLC, "%s:%d:%s: added drb %d to UE with RNTI 0x%x\n", __FILE__, __LINE__, __FUNCTION__, drb_id, rnti); } nr_rlc_manager_unlock(nr_rlc_ue_manager); } @@ -850,7 +876,7 @@ static void add_drb(int rnti, struct NR_DRB_ToAddMod *s, struct NR_RLC_BearerCon __FILE__, __LINE__, __FUNCTION__); exit(1); } - LOG_I(RLC, "%s:%s:%d: added DRB to UE with RNTI %x\n", __FILE__, __FUNCTION__, __LINE__, rnti); + LOG_I(RLC, "%s:%s:%d: added DRB to UE with RNTI 0x%x\n", __FILE__, __FUNCTION__, __LINE__, rnti); } /* Dummy function due to dependency from LTE libraries */ @@ -866,7 +892,7 @@ rlc_op_status_t rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP } rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP, - const LTE_SRB_ToAddModList_t * const srb2add_listP, + const NR_SRB_ToAddModList_t * const srb2add_listP, const NR_DRB_ToAddModList_t * const drb2add_listP, const NR_DRB_ToReleaseList_t * const drb2release_listP, const LTE_PMCH_InfoList_r9_t * const pmch_InfoList_r9_pP, @@ -874,6 +900,7 @@ rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt { int rnti = ctxt_pP->rnti; int i; + int j; if (/*ctxt_pP->enb_flag != 1 ||*/ ctxt_pP->module_id != 0 /*|| ctxt_pP->instance != 0 || ctxt_pP->eNB_index != 0 || @@ -896,13 +923,34 @@ rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt if (srb2add_listP != NULL) { for (i = 0; i < srb2add_listP->list.count; i++) { - add_srb(rnti, srb2add_listP->list.array[i]); + if (rlc_bearer2add_list != NULL) { + for(j = 0; j < rlc_bearer2add_list->list.count; j++){ + if(rlc_bearer2add_list->list.array[j]->servedRadioBearer != NULL){ + if(rlc_bearer2add_list->list.array[j]->servedRadioBearer->present == NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity){ + if(srb2add_listP->list.array[i]->srb_Identity == rlc_bearer2add_list->list.array[j]->servedRadioBearer->choice.srb_Identity){ + add_rlc_srb(rnti, srb2add_listP->list.array[i], rlc_bearer2add_list->list.array[j]); + } + } + } + } + } + } } - if (drb2add_listP != NULL) { + if ((drb2add_listP != NULL) && (rlc_bearer2add_list != NULL)) { for (i = 0; i < drb2add_listP->list.count; i++) { - add_drb(rnti, drb2add_listP->list.array[i], rlc_bearer2add_list->list.array[i]); + if (rlc_bearer2add_list != NULL) { + for(j = 0; j < rlc_bearer2add_list->list.count; j++){ + if(rlc_bearer2add_list->list.array[j]->servedRadioBearer != NULL){ + if(rlc_bearer2add_list->list.array[j]->servedRadioBearer->present == NR_RLC_BearerConfig__servedRadioBearer_PR_drb_Identity){ + if(drb2add_listP->list.array[i]->drb_Identity == rlc_bearer2add_list->list.array[j]->servedRadioBearer->choice.drb_Identity){ + add_drb(rnti, drb2add_listP->list.array[i], rlc_bearer2add_list->list.array[j]); + } + } + } + } + } } } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h index 26dee0590b..6b3c228163 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h @@ -42,7 +42,7 @@ struct NR_RLC_Config; struct NR_LogicalChannelConfig; -void nr_rlc_bearer_init(NR_RLC_BearerConfig_t *RLC_BearerConfig); +void nr_rlc_bearer_init(NR_RLC_BearerConfig_t *RLC_BearerConfig, NR_RLC_BearerConfig__servedRadioBearer_PR rb_type); void nr_drb_config(struct NR_RLC_Config *rlc_Config, NR_RLC_Config_PR rlc_config_pr); diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c index 8c46628333..dc2baa1839 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c @@ -85,7 +85,7 @@ nr_rlc_ue_t *nr_rlc_manager_get_ue(nr_rlc_ue_manager_t *_m, int rnti) if (m->ue_list[i]->rnti == rnti) return m->ue_list[i]; - LOG_D(RLC, "%s:%d:%s: new UE with RNTI %x\n", __FILE__, __LINE__, __FUNCTION__, rnti); + LOG_D(RLC, "%s:%d:%s: new UE with RNTI 0x%x\n", __FILE__, __LINE__, __FUNCTION__, rnti); m->ue_count++; m->ue_list = realloc(m->ue_list, sizeof(nr_rlc_ue_t *) * m->ue_count); diff --git a/openair2/LAYER2/rlc_v2/rlc_oai_api.c b/openair2/LAYER2/rlc_v2/rlc_oai_api.c index 18285f6563..0cf4b85fa2 100644 --- a/openair2/LAYER2/rlc_v2/rlc_oai_api.c +++ b/openair2/LAYER2/rlc_v2/rlc_oai_api.c @@ -1031,3 +1031,14 @@ void rlc_tick(int frame, int subframe) rlc_current_time_last_subframe = subframe; } } + +void du_rlc_data_req(const protocol_ctxt_t *const ctxt_pP, + const srb_flag_t srb_flagP, + const MBMS_flag_t MBMS_flagP, + const rb_id_t rb_idP, + const mui_t muiP, + confirm_t confirmP, + sdu_size_t sdu_sizeP, + mem_block_t *sdu_pP){ + +} \ No newline at end of file diff --git a/openair2/NETWORK_DRIVER/MESH/mesh.c b/openair2/NETWORK_DRIVER/MESH/mesh.c index ed87bc05f8..7e147feb68 100644 --- a/openair2/NETWORK_DRIVER/MESH/mesh.c +++ b/openair2/NETWORK_DRIVER/MESH/mesh.c @@ -886,6 +886,7 @@ int nas_mesh_DC_receive(struct cx_entity *cx,struct nas_priv *gpriv) switch (cx->state) { case NAS_CX_RELEASING_FAILURE: cx->retry=0; + /* fallthrough */ case NAS_CX_DCH: nas_mesh_DC_decode_cx_loss_ind(cx,p); // process message diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index 7bf738e043..982edf2f9d 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -108,7 +108,6 @@ void handle_nr_uci(NR_UL_IND_t *UL_info) } - void handle_nr_ulsch(NR_UL_IND_t *UL_info) { if (UL_info->rx_ind.number_of_pdus > 0 && UL_info->crc_ind.number_crcs > 0) { diff --git a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c index 51d279b7d1..4576dbcc9b 100644 --- a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c @@ -47,7 +47,11 @@ const char *dl_indication_type[] = {"MIB", "SIB", "DLSCH", "DCI", "RAR"}; static nr_ue_if_module_t *nr_ue_if_module_inst[MAX_IF_MODULES]; // L2 Abstraction Layer -int handle_bcch_bch(module_id_t module_id, int cc_id, unsigned int gNB_index, uint8_t *pduP, unsigned int additional_bits, uint32_t ssb_index, uint32_t ssb_length, uint16_t cell_id){ +int handle_bcch_bch(module_id_t module_id, int cc_id, + unsigned int gNB_index, uint8_t *pduP, + unsigned int additional_bits, + uint32_t ssb_index, uint32_t ssb_length, + uint16_t ssb_start_subcarrier, uint16_t cell_id){ return nr_ue_decode_mib(module_id, cc_id, @@ -55,7 +59,8 @@ int handle_bcch_bch(module_id_t module_id, int cc_id, unsigned int gNB_index, ui additional_bits, ssb_length, // Lssb = 64 is not support ssb_index, - pduP, + pduP, + ssb_start_subcarrier, cell_id); } @@ -90,7 +95,9 @@ int nr_ue_ul_indication(nr_uplink_indication_t *ul_info){ ret = nr_ue_scheduler(NULL, ul_info); - if (is_nr_UL_slot(mac->scc, ul_info->slot_tx, mac->frame_type) && get_softmodem_params()->do_ra) + NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon = mac->scc != NULL ? mac->scc->tdd_UL_DL_ConfigurationCommon : mac->scc_SIB->tdd_UL_DL_ConfigurationCommon; + + if (is_nr_UL_slot(tdd_UL_DL_ConfigurationCommon, ul_info->slot_tx, mac->frame_type) && !get_softmodem_params()->phy_test) nr_ue_prach_scheduler(module_id, ul_info->frame_tx, ul_info->slot_tx, ul_info->thread_id); switch(ret){ @@ -155,14 +162,15 @@ int nr_ue_dl_indication(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_ switch(dl_info->rx_ind->rx_indication_body[i].pdu_type){ - case FAPI_NR_RX_PDU_TYPE_MIB: + case FAPI_NR_RX_PDU_TYPE_SSB: ret_mask |= (handle_bcch_bch(dl_info->module_id, dl_info->cc_id, dl_info->gNB_index, - (dl_info->rx_ind->rx_indication_body+i)->mib_pdu.pdu, - (dl_info->rx_ind->rx_indication_body+i)->mib_pdu.additional_bits, - (dl_info->rx_ind->rx_indication_body+i)->mib_pdu.ssb_index, - (dl_info->rx_ind->rx_indication_body+i)->mib_pdu.ssb_length, - (dl_info->rx_ind->rx_indication_body+i)->mib_pdu.cell_id)) << FAPI_NR_RX_PDU_TYPE_MIB; + (dl_info->rx_ind->rx_indication_body+i)->ssb_pdu.pdu, + (dl_info->rx_ind->rx_indication_body+i)->ssb_pdu.additional_bits, + (dl_info->rx_ind->rx_indication_body+i)->ssb_pdu.ssb_index, + (dl_info->rx_ind->rx_indication_body+i)->ssb_pdu.ssb_length, + (dl_info->rx_ind->rx_indication_body+i)->ssb_pdu.ssb_start_subcarrier, + (dl_info->rx_ind->rx_indication_body+i)->ssb_pdu.cell_id)) << FAPI_NR_RX_PDU_TYPE_SSB; break; diff --git a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h index e78529d437..449c7bf890 100755 --- a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h +++ b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.h @@ -221,7 +221,15 @@ int nr_ue_dcireq(nr_dcireq_t *dcireq); \param ssb_index SSB index within 0 - (L_ssb-1) corresponding to 38.331 ch.13 parameter i \param ssb_length corresponding to L1 parameter L_ssb \param cell_id cell id */ -int handle_bcch_bch(module_id_t module_id, int cc_id, unsigned int gNB_index, uint8_t *pduP, unsigned int additional_bits, uint32_t ssb_index, uint32_t ssb_length, uint16_t cell_id); +int handle_bcch_bch(module_id_t module_id, + int cc_id, + unsigned int gNB_index, + uint8_t *pduP, + unsigned int additional_bits, + uint32_t ssb_index, + uint32_t ssb_length, + uint16_t ssb_start_subcarrier, + uint16_t cell_id); // TODO check /**\brief handle BCCH-DL-SCH message from dl_indication diff --git a/openair2/RRC/LITE/rrc_common.c b/openair2/RRC/LITE/rrc_common.c index 027a629992..f0279f0ef2 100644 --- a/openair2/RRC/LITE/rrc_common.c +++ b/openair2/RRC/LITE/rrc_common.c @@ -392,7 +392,7 @@ rrc_rx_tx( current_timestamp_ms, ctxt_pP->frame, estimated_distance); - LOG_D(LOCALIZE, " RRC status %d\n", ue_context_p->ue_context.Status); + LOG_D(LOCALIZE, " RRC status %d\n", ue_context_p->ue_context.StatusRrc); push_front(&RC.rrc[ctxt_pP->module_id]->loc_list, estimated_distance); RC.rrc[ctxt_pP->module_id]->reference_timestamp_ms = current_timestamp_ms; diff --git a/openair2/RRC/LTE/L2_interface.c b/openair2/RRC/LTE/L2_interface.c index 0bc63237b3..921c1f3bcb 100644 --- a/openair2/RRC/LTE/L2_interface.c +++ b/openair2/RRC/LTE/L2_interface.c @@ -316,8 +316,9 @@ mac_rrc_data_ind( UE_id, rntiP, sduP, - sdu_lenP - ); + sdu_lenP, + NULL, + 0); return(0); } diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c index f86b0ddbae..006f03a63e 100644 --- a/openair2/RRC/LTE/rrc_eNB.c +++ b/openair2/RRC/LTE/rrc_eNB.c @@ -997,6 +997,7 @@ void put_UE_in_freelist(module_id_t mod_id, rnti_t rnti, boolean_t removeFlag) { free_list = &eNB_MAC->UE_free_list; free_list->UE_free_ctrl[free_list->tail_freelist].rnti = rnti; free_list->UE_free_ctrl[free_list->tail_freelist].removeContextFlg = removeFlag; + free_list->UE_free_ctrl[free_list->tail_freelist].raFlag = 0; free_list->num_UEs++; eNB_MAC->UE_release_req.ue_release_request_body.ue_release_request_TLVs_list[eNB_MAC->UE_release_req.ue_release_request_body.number_of_TLVs].rnti = rnti; eNB_MAC->UE_release_req.ue_release_request_body.number_of_TLVs++; @@ -1046,12 +1047,12 @@ void release_UE_in_freeList(module_id_t mod_id) { eNB_PHY = RC.eNB[mod_id][CC_id]; int id; // clean ULSCH entries for rnti - id = find_ulsch(rnti,eNB_PHY,SEARCH_EXIST); + id = find_ulsch(rnti,eNB_PHY,eNB_MAC->UE_free_list.UE_free_ctrl[ue_num].raFlag ? SEARCH_EXIST_RA : SEARCH_EXIST); if (id>=0) clean_eNb_ulsch(eNB_PHY->ulsch[id]); // clean DLSCH entries for rnti - id = find_dlsch(rnti,eNB_PHY,SEARCH_EXIST); + id = find_dlsch(rnti,eNB_PHY,eNB_MAC->UE_free_list.UE_free_ctrl[ue_num].raFlag ? SEARCH_EXIST_RA : SEARCH_EXIST); if (id>=0) clean_eNb_dlsch(eNB_PHY->dlsch[id][0]); @@ -7613,7 +7614,7 @@ rrc_eNB_decode_ccch( #define NCE nonCriticalExtension -static void +static void get_ue_Category( LTE_UE_EUTRA_Capability_t *c, long *catDL, @@ -7649,7 +7650,7 @@ get_ue_Category( if (c125->ue_CategoryUL_r12) *catUL=*c125->ue_CategoryUL_r12; struct LTE_UE_EUTRA_Capability_v1260_IEs *c126=c125->NCE; if (c126 != NULL) { // v12.6 - if (c126->ue_CategoryDL_v1260) *catDL=*c126->ue_CategoryDL_v1260; + if (c126->ue_CategoryDL_v1260) *catDL=*c126->ue_CategoryDL_v1260; struct LTE_UE_EUTRA_Capability_v1270_IEs *c127=c126->NCE; if (c127 != NULL) { // v12.7 struct LTE_UE_EUTRA_Capability_v1280_IEs *c128=c127->NCE; @@ -7739,7 +7740,7 @@ is_ul_64QAM_supported( && c->NCE->NCE->NCE->NCE != NULL // R106 && c->NCE->NCE->NCE->NCE->NCE != NULL // R109 && c->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R113 - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R118 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R11a && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R125 @@ -7763,7 +7764,7 @@ is_dl_256QAM_supported( && c->NCE->NCE->NCE->NCE != NULL // R106 && c->NCE->NCE->NCE->NCE->NCE != NULL // R109 && c->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R113 - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R118 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R11a && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R125 @@ -7777,7 +7778,7 @@ is_dl_256QAM_supported( static int is_ul_256QAM_supported( LTE_UE_EUTRA_Capability_t *c -) +) //----------------------------------------------------------------------------- { return c != NULL // R8 @@ -7787,14 +7788,14 @@ is_ul_256QAM_supported( && c->NCE->NCE->NCE->NCE != NULL // R106 && c->NCE->NCE->NCE->NCE->NCE != NULL // R109 && c->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R113 - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R118 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R11a && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R125 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R126 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // 127 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // 128 - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //131 + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //131 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //132 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //133 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //134 @@ -7803,7 +7804,7 @@ is_ul_256QAM_supported( && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //143 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->rf_Parameters_v1430 != NULL && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->rf_Parameters_v1430->supportedBandCombination_v1430 != NULL - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->rf_Parameters_v1430->supportedBandCombination_v1430->list.array != NULL + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->rf_Parameters_v1430->supportedBandCombination_v1430->list.array != NULL && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->rf_Parameters_v1430->supportedBandCombination_v1430->list.array[0]->bandParameterList_v1430 != NULL && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->rf_Parameters_v1430->supportedBandCombination_v1430->list.array[0]->bandParameterList_v1430->list.array != NULL && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->rf_Parameters_v1430->supportedBandCombination_v1430->list.array[0]->bandParameterList_v1430->list.array[0]->ul_256QAM_r14!=NULL @@ -7826,14 +7827,14 @@ is_en_dc_supported( && c->NCE->NCE->NCE->NCE != NULL // R106 && c->NCE->NCE->NCE->NCE->NCE != NULL // R109 && c->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R113 - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R117 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R118 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R11a && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R125 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // R126 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // 127 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL // 128 - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //131 + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //131 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //132 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //133 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //134 @@ -7841,7 +7842,7 @@ is_en_dc_supported( && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //136 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //143 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //144 - && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //145 + && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //145 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //146 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL //151 && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->irat_ParametersNR_r15 != NULL @@ -7868,7 +7869,7 @@ int to_nr_rsrpq(long rsrpq_result,int nr_band) { return((rsrpq_result*10)-1175); case 77: // C case 78: - case 79: + case 79: return((rsrpq_result*10)-1170); case 28: // D return((rsrpq_result*10)-1165); @@ -8836,23 +8837,23 @@ void handle_f1_setup_req(f1ap_setup_req_t *f1_setup_req) { } F1AP_SETUP_RESP (msg_p).gNB_CU_name = rrc->node_name; - F1AP_SETUP_RESP (msg_p).mcc[cu_cell_ind] = rrc->configuration.mcc[0]; - F1AP_SETUP_RESP (msg_p).mnc[cu_cell_ind] = rrc->configuration.mnc[0]; - F1AP_SETUP_RESP (msg_p).mnc_digit_length[cu_cell_ind] = rrc->configuration.mnc_digit_length[0]; - F1AP_SETUP_RESP (msg_p).nr_cellid[cu_cell_ind] = rrc->nr_cellid; - F1AP_SETUP_RESP (msg_p).nrpci[cu_cell_ind] = f1_setup_req->nr_pci[i]; + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].mcc = rrc->configuration.mcc[0]; + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].mnc = rrc->configuration.mnc[0]; + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].mnc_digit_length = rrc->configuration.mnc_digit_length[0]; + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].nr_cellid = rrc->nr_cellid; + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].nrpci = f1_setup_req->nr_pci[i]; int num_SI= 0; if (rrc->carrier[0].SIB23) { - F1AP_SETUP_RESP (msg_p).SI_container[cu_cell_ind][2+num_SI] = rrc->carrier[0].SIB23; - F1AP_SETUP_RESP (msg_p).SI_container_length[cu_cell_ind][2+num_SI] = rrc->carrier[0].sizeof_SIB23; - printf("SI %d size %d: ", 0, F1AP_SETUP_RESP(msg_p).SI_container_length[j][2+num_SI]); - for (int n = 0; n < F1AP_SETUP_RESP(msg_p).SI_container_length[j][2+num_SI]; n++) - printf("%02x ", F1AP_SETUP_RESP(msg_p).SI_container[0][2+num_SI][n]); - printf("\n"); + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].SI_container[2+num_SI] = rrc->carrier[0].SIB23; + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].SI_container_length[2+num_SI] = rrc->carrier[0].sizeof_SIB23; + //printf("SI %d size %d: ", 0, F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].SI_container_length[2+num_SI]); + //for (int n = 0; n < F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].SI_container_length[2+num_SI]; n++) + // printf("%02x ", F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].SI_container[2+num_SI][n]); + //printf("\n"); num_SI++; } - F1AP_SETUP_RESP (msg_p).num_SI[cu_cell_ind] = num_SI; + F1AP_SETUP_RESP (msg_p).cells_to_activate[cu_cell_ind].num_SI = num_SI; cu_cell_ind++; found_cell=1; F1AP_SETUP_RESP (msg_p).num_cells_to_activate = cu_cell_ind; @@ -9059,7 +9060,7 @@ void rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { // check for UL failure or for UE to be released FILE *fd=NULL; - if ((ctxt_pP->frame&127) == 0 && ctxt_pP->subframe ==0) + if ((ctxt_pP->frame&127) == 0 && ctxt_pP->subframe ==0) fd=fopen("RRC_stats.log","w+"); RB_FOREACH(ue_context_p, rrc_ue_tree_s, &(RC.rrc[ctxt_pP->module_id]->rrc_ue_head)) { @@ -9091,15 +9092,15 @@ void rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { if (ue_context_p->ue_context.measResults) { fprintf(fd, "RRC PCell RSRP %ld, RSRQ %ld\n", ue_context_p->ue_context.measResults->measResultPCell.rsrpResult-140, ue_context_p->ue_context.measResults->measResultPCell.rsrqResult/2 - 20); - if (ue_context_p->ue_context.measResults->measResultNeighCells && - ue_context_p->ue_context.measResults->measResultNeighCells->present == LTE_MeasResults__measResultNeighCells_PR_measResultNeighCellListNR_r15) { + if (ue_context_p->ue_context.measResults->measResultNeighCells && + ue_context_p->ue_context.measResults->measResultNeighCells->present == LTE_MeasResults__measResultNeighCells_PR_measResultNeighCellListNR_r15) { fprintf(fd,"NR_pci %ld\n",ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->pci_r15); if(ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultCell_r15.rsrpResult_r15) fprintf(fd,"NR_rsrp %f dB\n",to_nr_rsrpq(*ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultCell_r15.rsrpResult_r15,RC.rrc[ctxt_pP->module_id]->nr_gnb_freq_band[0][0])/10.0); - if (ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultCell_r15.rsrqResult_r15) + if (ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultCell_r15.rsrqResult_r15) fprintf(fd,"NR_rsrq %f dB\n",to_nr_rsrpq(*ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultCell_r15.rsrqResult_r15,RC.rrc[ctxt_pP->module_id]->nr_gnb_freq_band[0][0])/10.0); - if (ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultRS_IndexList_r15) + if (ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultRS_IndexList_r15) fprintf(fd,"NR_ssb_index %ld\n",ue_context_p->ue_context.measResults->measResultNeighCells->choice.measResultNeighCellListNR_r15.list.array[0]->measResultRS_IndexList_r15->list.array[0]->ssb_Index_r15); } } diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index c9ab3a3a97..1f4af2a98e 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -28,6 +28,7 @@ * \email: raymond.knopp@eurecom.fr, kroempa@gmail.com */ +#include #include "platform_types.h" #include "nr_rrc_defs.h" #include "nr_rrc_extern.h" @@ -43,6 +44,7 @@ #include "NR_MIB.h" #include "NR_BCCH-BCH-Message.h" #include "rrc_gNB_UE_context.h" +#include "openair2/RRC/NR/MESSAGES/asn1_msg.h" extern RAN_CONTEXT_t RC; @@ -164,12 +166,12 @@ nr_rrc_data_req( //------------------------------------------------------------------------------ { if(sdu_sizeP == 255) { - LOG_I(RRC,"sdu_sizeP == 255"); + LOG_D(RRC,"sdu_sizeP == 255"); return FALSE; } MSC_LOG_TX_MESSAGE( - ctxt_pP->enb_flag ? MSC_RRC_ENB : MSC_RRC_UE, + ctxt_pP->enb_flag ? MSC_RRC_GNB : MSC_RRC_UE, ctxt_pP->enb_flag ? MSC_PDCP_ENB : MSC_PDCP_UE, buffer_pP, sdu_sizeP, @@ -182,11 +184,11 @@ nr_rrc_data_req( // Uses a new buffer to avoid issue with PDCP buffer content that could be changed by PDCP (asynchronous message handling). uint8_t *message_buffer; message_buffer = itti_malloc ( - ctxt_pP->enb_flag ? TASK_RRC_ENB : TASK_RRC_UE, + ctxt_pP->enb_flag ? TASK_RRC_GNB : TASK_RRC_UE, ctxt_pP->enb_flag ? TASK_PDCP_ENB : TASK_PDCP_UE, sdu_sizeP); memcpy (message_buffer, buffer_pP, sdu_sizeP); - message_p = itti_alloc_new_message (ctxt_pP->enb_flag ? TASK_RRC_ENB : TASK_RRC_UE, 0, RRC_DCCH_DATA_REQ); + message_p = itti_alloc_new_message (ctxt_pP->enb_flag ? TASK_RRC_GNB : TASK_RRC_UE, 0, RRC_DCCH_DATA_REQ); RRC_DCCH_DATA_REQ (message_p).frame = ctxt_pP->frame; RRC_DCCH_DATA_REQ (message_p).enb_flag = ctxt_pP->enb_flag; RRC_DCCH_DATA_REQ (message_p).rb_id = rb_idP; @@ -194,7 +196,7 @@ nr_rrc_data_req( RRC_DCCH_DATA_REQ (message_p).confirmp = confirmP; RRC_DCCH_DATA_REQ (message_p).sdu_size = sdu_sizeP; RRC_DCCH_DATA_REQ (message_p).sdu_p = message_buffer; - //memcpy (RRC_DCCH_DATA_REQ (message_p).sdu_p, buffer_pP, sdu_sizeP); + //memcpy (NR_RRC_DCCH_DATA_REQ (message_p).sdu_p, buffer_pP, sdu_sizeP); RRC_DCCH_DATA_REQ (message_p).mode = modeP; RRC_DCCH_DATA_REQ (message_p).module_id = ctxt_pP->module_id; RRC_DCCH_DATA_REQ (message_p).rnti = ctxt_pP->rnti; @@ -203,12 +205,12 @@ nr_rrc_data_req( ctxt_pP->enb_flag ? TASK_PDCP_ENB : TASK_PDCP_UE, ctxt_pP->instance, message_p); - LOG_I(RRC,"sent RRC_DCCH_DATA_REQ to TASK_PDCP_ENB\n"); + LOG_I(NR_RRC,"send RRC_DCCH_DATA_REQ to PDCP\n"); /* Hack: only trigger PDCP if in CU, otherwise it is triggered by RU threads * Ideally, PDCP would not neet to be triggered like this but react to ITTI * messages automatically */ - if (ctxt_pP->enb_flag && NODE_IS_CU(RC.rrc[ctxt_pP->module_id]->node_type)) + if (ctxt_pP->enb_flag && NODE_IS_CU(RC.nrrrc[ctxt_pP->module_id]->node_type)) pdcp_run(ctxt_pP); return TRUE; // TODO should be changed to a CNF message later, currently RRC lite does not used the returned value anyway. @@ -281,6 +283,7 @@ int8_t mac_rrc_nr_data_req(const module_id_t Mod_idP, LOG_D(NR_RRC,"[gNB %d] Frame %d CCCH request (Srb_id %ld)\n", Mod_idP, frameP, Srb_id); + AssertFatal(ue_context_p!=NULL,"failed to get ue_context\n"); char payload_size = ue_context_p->ue_context.Srb0.Tx_buffer.payload_size; // check if data is there for MAC @@ -310,6 +313,42 @@ int8_t nr_mac_rrc_data_ind(const module_id_t module_idP, const sdu_size_t sdu_lenP, const boolean_t brOption) { + if (NODE_IS_DU(RC.nrrrc[module_idP]->node_type)) { + LOG_W(RRC,"[DU %d][RAPROC] Received SDU for CCCH on SRB %ld length %d for UE id %d RNTI %x \n", + module_idP, srb_idP, sdu_lenP, UE_id, rntiP); + + // Generate DUtoCURRCContainer + // call do_RRCSetup like full procedure and extract masterCellGroup + NR_CellGroupConfig_t cellGroupConfig; + NR_ServingCellConfigCommon_t *scc=RC.nrrrc[module_idP]->carrier.servingcellconfigcommon; + uint8_t sdu2[100]; + memset(&cellGroupConfig,0,sizeof(cellGroupConfig)); + fill_initial_cellGroupConfig(rntiP,&cellGroupConfig,scc); + asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, + NULL, + (void *)&cellGroupConfig, + sdu2, + 100); + + int sdu2_len = (enc_rval.encoded+7)/8; + if (enc_rval.encoded == -1) { + LOG_E(F1AP,"Could not encoded cellGroupConfig, failed element %s\n",enc_rval.failed_type->name); + exit(-1); + } + /* do ITTI message */ + DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER( + module_idP, + CC_id, + UE_id, + rntiP, + sduP, + sdu_lenP, + (const int8_t*)sdu2, + sdu2_len + ); + return(0); + } + protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, GNB_FLAG_YES, rntiP, frameP, sub_frameP,0); @@ -317,7 +356,7 @@ int8_t nr_mac_rrc_data_ind(const module_id_t module_idP, LOG_D(NR_RRC, "[gNB %d] Received SDU for CCCH on SRB %ld\n", module_idP, srb_idP); ctxt.brOption = brOption; if (sdu_lenP > 0) { - nr_rrc_gNB_decode_ccch(&ctxt, sduP, sdu_lenP, CC_id); + nr_rrc_gNB_decode_ccch(&ctxt, sduP, sdu_lenP, NULL, CC_id); } } diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c old mode 100644 new mode 100755 index 39222f814d..789fb84a28 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -70,6 +70,7 @@ #include "NR_RRCReconfigurationComplete-IEs.h" #include "NR_DLInformationTransfer.h" #include "NR_RRCReestablishmentRequest.h" +#include "NR_UE-CapabilityRequestFilterNR.h" #include "PHY/defs_nr_common.h" #if defined(NR_Rel16) #include "NR_SCS-SpecificCarrier.h" @@ -308,12 +309,12 @@ uint8_t do_SIB1_NR(rrc_gNB_carrier_data_t *carrier, // TODO : Add support for more than one PLMN int num_plmn = 1; // int num_plmn = configuration->num_plmn; - struct NR_PLMN_Identity nr_plmn[num_plmn]; - NR_MCC_MNC_Digit_t nr_mcc_digit[num_plmn][3]; - NR_MCC_MNC_Digit_t nr_mnc_digit[num_plmn][3]; - memset(nr_plmn,0,sizeof(nr_plmn)); - memset(nr_mcc_digit,0,sizeof(nr_mcc_digit)); - memset(nr_mnc_digit,0,sizeof(nr_mnc_digit)); + struct NR_PLMN_Identity *nr_plmn = CALLOC(1, sizeof(struct NR_PLMN_Identity) * num_plmn); + NR_MCC_MNC_Digit_t (*nr_mcc_digit)[3] = (NR_MCC_MNC_Digit_t(*)[3])CALLOC(1, sizeof(NR_MCC_MNC_Digit_t)*num_plmn*3); + NR_MCC_MNC_Digit_t (*nr_mnc_digit)[3] = (NR_MCC_MNC_Digit_t(*)[3])CALLOC(1, sizeof(NR_MCC_MNC_Digit_t)*num_plmn*3);; + memset(nr_plmn,0,sizeof(struct NR_PLMN_Identity) * num_plmn); + memset(nr_mcc_digit,0,sizeof(NR_MCC_MNC_Digit_t)*num_plmn*3); + memset(nr_mnc_digit,0,sizeof(NR_MCC_MNC_Digit_t)*num_plmn*3); NR_BCCH_DL_SCH_Message_t *sib1_message = CALLOC(1,sizeof(NR_BCCH_DL_SCH_Message_t)); carrier->siblock1 = sib1_message; @@ -444,7 +445,7 @@ uint8_t do_SIB1_NR(rrc_gNB_carrier_data_t *carrier, ss1->nrofCandidates = calloc(1,sizeof(*ss1->nrofCandidates)); ss1->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; ss1->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; - ss1->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; + ss1->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n4; ss1->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; ss1->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; ss1->searchSpaceType = calloc(1,sizeof(*ss1->searchSpaceType)); @@ -658,6 +659,56 @@ uint8_t do_SIB1_NR(rrc_gNB_carrier_data_t *carrier, return((enc_rval.encoded+7)/8); } +uint8_t do_SIB23_NR(rrc_gNB_carrier_data_t *carrier, + gNB_RrcConfigurationReq *configuration) { + asn_enc_rval_t enc_rval; + SystemInformation_IEs__sib_TypeAndInfo__Member *sib2 = NULL; + SystemInformation_IEs__sib_TypeAndInfo__Member *sib3 = NULL; + + NR_BCCH_DL_SCH_Message_t *sib_message = CALLOC(1,sizeof(NR_BCCH_DL_SCH_Message_t)); + sib_message->message.present = NR_BCCH_DL_SCH_MessageType_PR_c1; + sib_message->message.choice.c1 = CALLOC(1,sizeof(struct NR_BCCH_DL_SCH_MessageType__c1)); + sib_message->message.choice.c1->present = NR_BCCH_DL_SCH_MessageType__c1_PR_systemInformation; + sib_message->message.choice.c1->choice.systemInformation = CALLOC(1,sizeof(struct NR_SystemInformation)); + + struct NR_SystemInformation *sib = sib_message->message.choice.c1->choice.systemInformation; + sib->criticalExtensions.present = NR_SystemInformation__criticalExtensions_PR_systemInformation; + sib->criticalExtensions.choice.systemInformation = CALLOC(1, sizeof(struct NR_SystemInformation_IEs)); + + struct NR_SystemInformation_IEs *ies = sib->criticalExtensions.choice.systemInformation; + sib2 = CALLOC(1, sizeof(SystemInformation_IEs__sib_TypeAndInfo__Member)); + sib2->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib2; + sib2->choice.sib2 = CALLOC(1, sizeof(struct NR_SIB2)); + sib2->choice.sib2->cellReselectionInfoCommon.q_Hyst = NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB1; + sib2->choice.sib2->cellReselectionServingFreqInfo.threshServingLowP = 2; // INTEGER (0..31) + sib2->choice.sib2->cellReselectionServingFreqInfo.cellReselectionPriority = 2; // INTEGER (0..7) + sib2->choice.sib2->intraFreqCellReselectionInfo.q_RxLevMin = -50; // INTEGER (-70..-22) + sib2->choice.sib2->intraFreqCellReselectionInfo.s_IntraSearchP = 2; // INTEGER (0..31) + sib2->choice.sib2->intraFreqCellReselectionInfo.t_ReselectionNR = 2; // INTEGER (0..7) + sib2->choice.sib2->intraFreqCellReselectionInfo.deriveSSB_IndexFromCell = true; + ASN_SEQUENCE_ADD(&ies->sib_TypeAndInfo.list, sib2); + + sib3 = CALLOC(1, sizeof(SystemInformation_IEs__sib_TypeAndInfo__Member)); + sib3->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib3; + sib3->choice.sib3 = CALLOC(1, sizeof(struct NR_SIB3)); + ASN_SEQUENCE_ADD(&ies->sib_TypeAndInfo.list, sib3); + + //encode SIB to data + // carrier->SIB23 = (uint8_t *) malloc16(128); + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_BCCH_DL_SCH_Message, + NULL, + (void *)sib_message, + carrier->SIB23, + 100); + AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + + if (enc_rval.encoded==-1) { + return(-1); + } + + return((enc_rval.encoded+7)/8); +} void do_RLC_BEARER(uint8_t Mod_id, int CC_id, @@ -926,29 +977,360 @@ uint8_t do_RRCReject(uint8_t Mod_id, return((enc_rval.encoded+7)/8); } +void fill_initial_SpCellConfig(rnti_t rnti, + NR_SpCellConfig_t *SpCellConfig, + NR_ServingCellConfigCommon_t *scc) { + + SpCellConfig->servCellIndex = NULL; + SpCellConfig->reconfigurationWithSync = NULL; + SpCellConfig->rlmInSyncOutOfSyncThreshold = NULL; + SpCellConfig->rlf_TimersAndConstants = NULL; + SpCellConfig->spCellConfigDedicated = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated)); + SpCellConfig->spCellConfigDedicated->uplinkConfig = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig)); + NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); + SpCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; + initialUplinkBWP->pucch_Config = calloc(1,sizeof(*initialUplinkBWP->pucch_Config)); + initialUplinkBWP->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; + NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); + initialUplinkBWP->pucch_Config->choice.setup=pucch_Config; + pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); + pucch_Config->resourceSetToReleaseList = NULL; + NR_PUCCH_ResourceSet_t *pucchresset0=calloc(1,sizeof(*pucchresset0)); + pucchresset0->pucch_ResourceSetId = 0; + NR_PUCCH_ResourceId_t *pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); + *pucchresset0id0=0; + ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); + pucchresset0->maxPayloadSize=NULL; + ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); + + pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); + pucch_Config->resourceToReleaseList = NULL; + // configure one single PUCCH0 opportunity for initial connection procedure + // one symbol (13) + NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); + pucchres0->pucch_ResourceId=0; + pucchres0->startingPRB=0; + pucchres0->intraSlotFrequencyHopping=NULL; + pucchres0->secondHopPRB=NULL; + pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; + pucchres0->format.choice.format0=calloc(1,sizeof(*pucchres0->format.choice.format0)); + pucchres0->format.choice.format0->initialCyclicShift=0; + pucchres0->format.choice.format0->nrofSymbols=1; + pucchres0->format.choice.format0->startingSymbolIndex=13; + ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); + + // configure Scheduling request + // 40 slot period + pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); + NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); + schedulingRequestResourceConfig->schedulingRequestResourceId = 1; + schedulingRequestResourceConfig->schedulingRequestID= 0; + schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); + schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; + // note: make sure that there is no issue here. Later choose the RNTI accordingly. + // Here we would be limited to 8 UEs on this resource (2 Frames 30 kHz SCS, 5 ms TDD periodicity => slots 8,9,18,19,28,29,38,39). + // This should be a temporary resource until the first RRCReconfiguration gives new pucch resources. + // Check for above configuration and exit for now if it is not the case + AssertFatal(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing==NR_SubcarrierSpacing_kHz30, + "SCS != 30kHz\n"); + AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity==NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms5, + "TDD period != 5ms : %ld\n",scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); + + schedulingRequestResourceConfig->periodicityAndOffset->choice.sl40 = 10*((rnti>>1)&3) + (rnti&2); + schedulingRequestResourceConfig->resource = calloc(1,sizeof(*schedulingRequestResourceConfig->resource)); + *schedulingRequestResourceConfig->resource = 0; + ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); + + SpCellConfig->spCellConfigDedicated->initialDownlinkBWP = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->initialDownlinkBWP)); + NR_BWP_DownlinkDedicated_t *bwp_Dedicated = SpCellConfig->spCellConfigDedicated->initialDownlinkBWP; + bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp_Dedicated->pdcch_Config)); + bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; + bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp_Dedicated->pdcch_Config->choice.setup)); + + bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); + + NR_SearchSpace_t *ss2 = calloc(1,sizeof(*ss2)); + + ss2->searchSpaceId=2; + ss2->controlResourceSetId=calloc(1,sizeof(*ss2->controlResourceSetId)); + *ss2->controlResourceSetId=0; + ss2->monitoringSlotPeriodicityAndOffset=calloc(1,sizeof(*ss2->monitoringSlotPeriodicityAndOffset)); + ss2->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; + ss2->monitoringSlotPeriodicityAndOffset->choice.sl1=(NULL_t)0; + ss2->duration=NULL; + ss2->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss2->monitoringSymbolsWithinSlot)); + ss2->monitoringSymbolsWithinSlot->buf = calloc(1,2); + ss2->monitoringSymbolsWithinSlot->size = 2; + ss2->monitoringSymbolsWithinSlot->bits_unused = 2; + ss2->monitoringSymbolsWithinSlot->buf[0]=0x80; + ss2->monitoringSymbolsWithinSlot->buf[1]=0x0; + ss2->nrofCandidates=calloc(1,sizeof(*ss2->nrofCandidates)); + ss2->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; + ss2->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; + ss2->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; + ss2->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; + ss2->searchSpaceType=calloc(1,sizeof(*ss2->searchSpaceType)); + ss2->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_ue_Specific; + ss2->searchSpaceType->choice.ue_Specific = calloc(1,sizeof(*ss2->searchSpaceType->choice.ue_Specific)); + ss2->searchSpaceType->choice.ue_Specific->dci_Formats=NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_0_And_1_0; + + ASN_SEQUENCE_ADD(&bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, + ss2); + + SpCellConfig->spCellConfigDedicated->tag_Id=0; +} + +void fill_mastercellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, NR_CellGroupConfig_t *ue_context_mastercellGroup) { + + cellGroupConfig->cellGroupId = 0; + cellGroupConfig->rlc_BearerToReleaseList = NULL; + cellGroupConfig->rlc_BearerToAddModList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToAddModList)); + + // RLC Bearer Config + // TS38.331 9.2.1 Default SRB configurations + NR_RLC_BearerConfig_t *rlc_BearerConfig = NULL; + NR_RLC_Config_t *rlc_Config = NULL; + NR_LogicalChannelConfig_t *logicalChannelConfig = NULL; + long *logicalChannelGroup = NULL; + rlc_BearerConfig = calloc(1, sizeof(NR_RLC_BearerConfig_t)); + rlc_BearerConfig->logicalChannelIdentity = 2; + rlc_BearerConfig->servedRadioBearer = calloc(1, sizeof(*rlc_BearerConfig->servedRadioBearer)); + rlc_BearerConfig->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity; + rlc_BearerConfig->servedRadioBearer->choice.srb_Identity = 2; + rlc_BearerConfig->reestablishRLC = NULL; + rlc_Config = calloc(1, sizeof(NR_RLC_Config_t)); + rlc_Config->present = NR_RLC_Config_PR_am; + rlc_Config->choice.am = calloc(1, sizeof(*rlc_Config->choice.am)); + rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; + rlc_Config->choice.am->dl_AM_RLC.t_Reassembly = NR_T_Reassembly_ms35; + rlc_Config->choice.am->dl_AM_RLC.t_StatusProhibit = NR_T_StatusProhibit_ms0; + rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; + rlc_Config->choice.am->ul_AM_RLC.t_PollRetransmit = NR_T_PollRetransmit_ms45; + rlc_Config->choice.am->ul_AM_RLC.pollPDU = NR_PollPDU_infinity; + rlc_Config->choice.am->ul_AM_RLC.pollByte = NR_PollByte_infinity; + rlc_Config->choice.am->ul_AM_RLC.maxRetxThreshold = NR_UL_AM_RLC__maxRetxThreshold_t8; + rlc_BearerConfig->rlc_Config = rlc_Config; + logicalChannelConfig = calloc(1, sizeof(NR_LogicalChannelConfig_t)); + logicalChannelConfig->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig->ul_SpecificParameters)); + logicalChannelConfig->ul_SpecificParameters->priority = 3; + logicalChannelConfig->ul_SpecificParameters->prioritisedBitRate = NR_LogicalChannelConfig__ul_SpecificParameters__prioritisedBitRate_infinity; + logicalChannelConfig->ul_SpecificParameters->bucketSizeDuration = NR_LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms5; + logicalChannelGroup = CALLOC(1, sizeof(long)); + *logicalChannelGroup = 0; + logicalChannelConfig->ul_SpecificParameters->logicalChannelGroup = logicalChannelGroup; + logicalChannelConfig->ul_SpecificParameters->schedulingRequestID = CALLOC(1, sizeof(*logicalChannelConfig->ul_SpecificParameters->schedulingRequestID)); + *logicalChannelConfig->ul_SpecificParameters->schedulingRequestID = 0; + logicalChannelConfig->ul_SpecificParameters->logicalChannelSR_Mask = 0; + logicalChannelConfig->ul_SpecificParameters->logicalChannelSR_DelayTimerApplied = 0; + rlc_BearerConfig->mac_LogicalChannelConfig = logicalChannelConfig; + ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig); + ASN_SEQUENCE_ADD(&ue_context_mastercellGroup->rlc_BearerToAddModList->list, rlc_BearerConfig); + + // DRB Configuration + NR_RLC_BearerConfig_t *rlc_BearerConfig_drb = NULL; + NR_RLC_Config_t *rlc_Config_drb = NULL; + NR_LogicalChannelConfig_t *logicalChannelConfig_drb = NULL; + long *logicalChannelGroup_drb = NULL; + rlc_BearerConfig_drb = calloc(1, sizeof(NR_RLC_BearerConfig_t)); + rlc_BearerConfig_drb->logicalChannelIdentity = 4; + rlc_BearerConfig_drb->servedRadioBearer = calloc(1, sizeof(*rlc_BearerConfig_drb->servedRadioBearer)); + rlc_BearerConfig_drb->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_drb_Identity; + rlc_BearerConfig_drb->servedRadioBearer->choice.drb_Identity = 1; + rlc_BearerConfig_drb->reestablishRLC = NULL; + rlc_Config_drb = calloc(1, sizeof(NR_RLC_Config_t)); + rlc_Config_drb->present = NR_RLC_Config_PR_am; + rlc_Config_drb->choice.am = calloc(1, sizeof(*rlc_Config_drb->choice.am)); + rlc_Config_drb->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config_drb->choice.am->dl_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size18; + rlc_Config_drb->choice.am->dl_AM_RLC.t_Reassembly = NR_T_Reassembly_ms80; + rlc_Config_drb->choice.am->dl_AM_RLC.t_StatusProhibit = NR_T_StatusProhibit_ms10; + rlc_Config_drb->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config_drb->choice.am->ul_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size18; + rlc_Config_drb->choice.am->ul_AM_RLC.t_PollRetransmit = NR_T_PollRetransmit_ms80; + rlc_Config_drb->choice.am->ul_AM_RLC.pollPDU = NR_PollPDU_p64; + rlc_Config_drb->choice.am->ul_AM_RLC.pollByte = NR_PollByte_kB125; + rlc_Config_drb->choice.am->ul_AM_RLC.maxRetxThreshold = NR_UL_AM_RLC__maxRetxThreshold_t4; + rlc_BearerConfig_drb->rlc_Config = rlc_Config_drb; + logicalChannelConfig_drb = calloc(1, sizeof(NR_LogicalChannelConfig_t)); + logicalChannelConfig_drb->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig_drb->ul_SpecificParameters)); + logicalChannelConfig_drb->ul_SpecificParameters->priority = 13; + logicalChannelConfig_drb->ul_SpecificParameters->prioritisedBitRate = NR_LogicalChannelConfig__ul_SpecificParameters__prioritisedBitRate_kBps8; + logicalChannelConfig_drb->ul_SpecificParameters->bucketSizeDuration = NR_LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms100; + logicalChannelGroup_drb = CALLOC(1, sizeof(long)); + *logicalChannelGroup_drb = 1; + logicalChannelConfig_drb->ul_SpecificParameters->logicalChannelGroup = logicalChannelGroup_drb; + logicalChannelConfig_drb->ul_SpecificParameters->schedulingRequestID = CALLOC(1, sizeof(*logicalChannelConfig_drb->ul_SpecificParameters->schedulingRequestID)); + *logicalChannelConfig_drb->ul_SpecificParameters->schedulingRequestID = 0; + logicalChannelConfig_drb->ul_SpecificParameters->logicalChannelSR_Mask = 0; + logicalChannelConfig_drb->ul_SpecificParameters->logicalChannelSR_DelayTimerApplied = 0; + rlc_BearerConfig_drb->mac_LogicalChannelConfig = logicalChannelConfig_drb; + ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig_drb); + ASN_SEQUENCE_ADD(&ue_context_mastercellGroup->rlc_BearerToAddModList->list, rlc_BearerConfig_drb); +} + +void fill_initial_cellGroupConfig(rnti_t rnti, + NR_CellGroupConfig_t *cellGroupConfig, + NR_ServingCellConfigCommon_t *scc) { + + NR_RLC_BearerConfig_t *rlc_BearerConfig = NULL; + NR_RLC_Config_t *rlc_Config = NULL; + NR_LogicalChannelConfig_t *logicalChannelConfig = NULL; + NR_MAC_CellGroupConfig_t *mac_CellGroupConfig = NULL; + NR_PhysicalCellGroupConfig_t *physicalCellGroupConfig = NULL; + long *logicalChannelGroup = NULL; + + cellGroupConfig->cellGroupId = 0; + + /* Rlc Bearer Config */ + /* TS38.331 9.2.1 Default SRB configurations */ + cellGroupConfig->rlc_BearerToAddModList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToAddModList)); + rlc_BearerConfig = calloc(1, sizeof(NR_RLC_BearerConfig_t)); + rlc_BearerConfig->logicalChannelIdentity = 1; + rlc_BearerConfig->servedRadioBearer = calloc(1, sizeof(*rlc_BearerConfig->servedRadioBearer)); + rlc_BearerConfig->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity; + rlc_BearerConfig->servedRadioBearer->choice.srb_Identity = 1; + rlc_BearerConfig->reestablishRLC = NULL; + rlc_Config = calloc(1, sizeof(NR_RLC_Config_t)); + rlc_Config->present = NR_RLC_Config_PR_am; + rlc_Config->choice.am = calloc(1, sizeof(*rlc_Config->choice.am)); + rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; + rlc_Config->choice.am->dl_AM_RLC.t_Reassembly = NR_T_Reassembly_ms35; + rlc_Config->choice.am->dl_AM_RLC.t_StatusProhibit = NR_T_StatusProhibit_ms0; + rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; + rlc_Config->choice.am->ul_AM_RLC.t_PollRetransmit = NR_T_PollRetransmit_ms45; + rlc_Config->choice.am->ul_AM_RLC.pollPDU = NR_PollPDU_infinity; + rlc_Config->choice.am->ul_AM_RLC.pollByte = NR_PollByte_infinity; + rlc_Config->choice.am->ul_AM_RLC.maxRetxThreshold = NR_UL_AM_RLC__maxRetxThreshold_t8; + rlc_BearerConfig->rlc_Config = rlc_Config; + logicalChannelConfig = calloc(1, sizeof(NR_LogicalChannelConfig_t)); + logicalChannelConfig->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig->ul_SpecificParameters)); + logicalChannelConfig->ul_SpecificParameters->priority = 1; + logicalChannelConfig->ul_SpecificParameters->prioritisedBitRate = NR_LogicalChannelConfig__ul_SpecificParameters__prioritisedBitRate_infinity; + logicalChannelGroup = CALLOC(1, sizeof(long)); + *logicalChannelGroup = 0; + logicalChannelConfig->ul_SpecificParameters->logicalChannelGroup = logicalChannelGroup; + logicalChannelConfig->ul_SpecificParameters->schedulingRequestID = CALLOC(1, sizeof(*logicalChannelConfig->ul_SpecificParameters->schedulingRequestID)); + *logicalChannelConfig->ul_SpecificParameters->schedulingRequestID = 0; + logicalChannelConfig->ul_SpecificParameters->logicalChannelSR_Mask = 0; + logicalChannelConfig->ul_SpecificParameters->logicalChannelSR_DelayTimerApplied = 0; + rlc_BearerConfig->mac_LogicalChannelConfig = logicalChannelConfig; + ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig); + + /* + // SRB2 + NR_RLC_BearerConfig_t *rlc_BearerConfig2 = NULL; + NR_RLC_Config_t *rlc_Config2 = NULL; + NR_LogicalChannelConfig_t *logicalChannelConfig2= NULL; + long *logicalChannelGroup2 = NULL; + rlc_BearerConfig2 = calloc(1, sizeof(NR_RLC_BearerConfig_t)); + rlc_BearerConfig2->logicalChannelIdentity = 2; + rlc_BearerConfig2->servedRadioBearer = calloc(1, sizeof(*rlc_BearerConfig2->servedRadioBearer)); + rlc_BearerConfig2->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity; + rlc_BearerConfig2->servedRadioBearer->choice.srb_Identity = 2; + rlc_BearerConfig2->reestablishRLC = NULL; + rlc_Config2 = calloc(1, sizeof(NR_RLC_Config_t)); + rlc_Config2->present = NR_RLC_Config_PR_am; + rlc_Config2->choice.am = calloc(1, sizeof(*rlc_Config2->choice.am)); + rlc_Config2->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config2->choice.am->dl_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; + rlc_Config2->choice.am->dl_AM_RLC.t_Reassembly = NR_T_Reassembly_ms35; + rlc_Config2->choice.am->dl_AM_RLC.t_StatusProhibit = NR_T_StatusProhibit_ms0; + rlc_Config2->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config2->choice.am->ul_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; + rlc_Config2->choice.am->ul_AM_RLC.t_PollRetransmit = NR_T_PollRetransmit_ms45; + rlc_Config2->choice.am->ul_AM_RLC.pollPDU = NR_PollPDU_infinity; + rlc_Config2->choice.am->ul_AM_RLC.pollByte = NR_PollByte_infinity; + rlc_Config2->choice.am->ul_AM_RLC.maxRetxThreshold = NR_UL_AM_RLC__maxRetxThreshold_t8; + rlc_BearerConfig2->rlc_Config = rlc_Config2; + logicalChannelConfig2 = calloc(1, sizeof(NR_LogicalChannelConfig_t)); + logicalChannelConfig2->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig2->ul_SpecificParameters)); + logicalChannelConfig2->ul_SpecificParameters->priority = 1; + logicalChannelConfig2->ul_SpecificParameters->prioritisedBitRate = NR_LogicalChannelConfig__ul_SpecificParameters__prioritisedBitRate_infinity; + logicalChannelGroup2 = CALLOC(1, sizeof(long)); + *logicalChannelGroup2 = 0; + logicalChannelConfig2->ul_SpecificParameters->logicalChannelGroup = logicalChannelGroup2; + logicalChannelConfig2->ul_SpecificParameters->schedulingRequestID = CALLOC(1, sizeof(*logicalChannelConfig2->ul_SpecificParameters->schedulingRequestID)); + *logicalChannelConfig2->ul_SpecificParameters->schedulingRequestID = 0; + logicalChannelConfig2->ul_SpecificParameters->logicalChannelSR_Mask = 0; + logicalChannelConfig2->ul_SpecificParameters->logicalChannelSR_DelayTimerApplied = 0; + rlc_BearerConfig2->mac_LogicalChannelConfig = logicalChannelConfig2; + ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig2); + */ + + cellGroupConfig->rlc_BearerToReleaseList = NULL; + + /* mac CellGroup Config */ + if (1) { + mac_CellGroupConfig = calloc(1, sizeof(*mac_CellGroupConfig)); + mac_CellGroupConfig->schedulingRequestConfig = calloc(1, sizeof(*mac_CellGroupConfig->schedulingRequestConfig)); + mac_CellGroupConfig->schedulingRequestConfig->schedulingRequestToAddModList = CALLOC(1,sizeof(*mac_CellGroupConfig->schedulingRequestConfig->schedulingRequestToAddModList)); + struct NR_SchedulingRequestToAddMod *schedulingrequestlist; + schedulingrequestlist = CALLOC(1,sizeof(*schedulingrequestlist)); + schedulingrequestlist->schedulingRequestId = 0; + schedulingrequestlist->sr_ProhibitTimer = CALLOC(1,sizeof(*schedulingrequestlist->sr_ProhibitTimer)); + *(schedulingrequestlist->sr_ProhibitTimer) = 0; + schedulingrequestlist->sr_TransMax = 0; + ASN_SEQUENCE_ADD(&(mac_CellGroupConfig->schedulingRequestConfig->schedulingRequestToAddModList->list),schedulingrequestlist); + mac_CellGroupConfig->bsr_Config = calloc(1, sizeof(*mac_CellGroupConfig->bsr_Config)); + mac_CellGroupConfig->bsr_Config->periodicBSR_Timer = NR_BSR_Config__periodicBSR_Timer_sf10; + mac_CellGroupConfig->bsr_Config->retxBSR_Timer = NR_BSR_Config__retxBSR_Timer_sf80; + mac_CellGroupConfig->tag_Config = calloc(1, sizeof(*mac_CellGroupConfig->tag_Config)); + mac_CellGroupConfig->tag_Config->tag_ToReleaseList = NULL; + mac_CellGroupConfig->tag_Config->tag_ToAddModList = calloc(1,sizeof(*mac_CellGroupConfig->tag_Config->tag_ToAddModList)); + struct NR_TAG *tag=calloc(1,sizeof(*tag)); + tag->tag_Id = 0; + tag->timeAlignmentTimer = NR_TimeAlignmentTimer_infinity; + ASN_SEQUENCE_ADD(&mac_CellGroupConfig->tag_Config->tag_ToAddModList->list,tag); + mac_CellGroupConfig->phr_Config = calloc(1, sizeof(*mac_CellGroupConfig->phr_Config)); + mac_CellGroupConfig->phr_Config->present = NR_SetupRelease_PHR_Config_PR_setup; + mac_CellGroupConfig->phr_Config->choice.setup = calloc(1, sizeof(*mac_CellGroupConfig->phr_Config->choice.setup)); + mac_CellGroupConfig->phr_Config->choice.setup->phr_PeriodicTimer = NR_PHR_Config__phr_PeriodicTimer_sf10; + mac_CellGroupConfig->phr_Config->choice.setup->phr_ProhibitTimer = NR_PHR_Config__phr_ProhibitTimer_sf10; + mac_CellGroupConfig->phr_Config->choice.setup->phr_Tx_PowerFactorChange = NR_PHR_Config__phr_Tx_PowerFactorChange_dB1; + } + cellGroupConfig->mac_CellGroupConfig = mac_CellGroupConfig; + + physicalCellGroupConfig = calloc(1,sizeof(*physicalCellGroupConfig)); + physicalCellGroupConfig->p_NR_FR1 = calloc(1,sizeof(*physicalCellGroupConfig->p_NR_FR1)); + *physicalCellGroupConfig->p_NR_FR1 = 20; + physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook = NR_PhysicalCellGroupConfig__pdsch_HARQ_ACK_Codebook_dynamic; + cellGroupConfig->physicalCellGroupConfig = physicalCellGroupConfig; + + cellGroupConfig->spCellConfig = calloc(1,sizeof(*cellGroupConfig->spCellConfig)); + + fill_initial_SpCellConfig(rnti,cellGroupConfig->spCellConfig,scc); + + cellGroupConfig->sCellToAddModList = NULL; + cellGroupConfig->sCellToReleaseList = NULL; +} + //------------------------------------------------------------------------------ -uint8_t do_RRCSetup(const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *const ue_context_pP, - int CC_id, +uint8_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, uint8_t *const buffer, const uint8_t transaction_id, - NR_SRB_ToAddModList_t **SRB_configList) + OCTET_STRING_t *masterCellGroup_from_DU, + NR_ServingCellConfigCommon_t *scc) //------------------------------------------------------------------------------ { - asn_enc_rval_t enc_rval;; + asn_enc_rval_t enc_rval; NR_DL_CCCH_Message_t dl_ccch_msg; NR_RRCSetup_t *rrcSetup; NR_RRCSetup_IEs_t *ie; NR_SRB_ToAddMod_t *SRB1_config = NULL; NR_PDCP_Config_t *pdcp_Config = NULL; NR_CellGroupConfig_t *cellGroupConfig = NULL; - NR_RLC_BearerConfig_t *rlc_BearerConfig = NULL; - NR_RLC_Config_t *rlc_Config = NULL; - NR_LogicalChannelConfig_t *logicalChannelConfig = NULL; - NR_MAC_CellGroupConfig_t *mac_CellGroupConfig = NULL; - char masterCellGroup_buf[1000]; - long *logicalChannelGroup = NULL; + + AssertFatal(ue_context_pP != NULL,"ue_context_p is null\n"); + gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; + NR_SRB_ToAddModList_t **SRB_configList = &ue_p->SRB_configList; + + memset((void *)&dl_ccch_msg, 0, sizeof(NR_DL_CCCH_Message_t)); dl_ccch_msg.message.present = NR_DL_CCCH_MessageType_PR_c1; @@ -963,6 +1345,7 @@ uint8_t do_RRCSetup(const protocol_ctxt_t *const ctxt_pP, ie = rrcSetup->criticalExtensions.choice.rrcSetup; /****************************** radioBearerConfig ******************************/ + /* Configure SRB1 */ if (*SRB_configList) { free(*SRB_configList); @@ -982,97 +1365,59 @@ uint8_t do_RRCSetup(const protocol_ctxt_t *const ctxt_pP, ie->radioBearerConfig.drb_ToAddModList = NULL; ie->radioBearerConfig.drb_ToReleaseList = NULL; ie->radioBearerConfig.securityConfig = NULL; - + /****************************** masterCellGroup ******************************/ /* TODO */ - cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t)); - cellGroupConfig->cellGroupId = 0; - - /* Rlc Bearer Config */ - /* TS38.331 9.2.1 Default SRB configurations */ - cellGroupConfig->rlc_BearerToAddModList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToAddModList)); - rlc_BearerConfig = calloc(1, sizeof(NR_RLC_BearerConfig_t)); - rlc_BearerConfig->logicalChannelIdentity = 1; - rlc_BearerConfig->servedRadioBearer = calloc(1, sizeof(*rlc_BearerConfig->servedRadioBearer)); - rlc_BearerConfig->servedRadioBearer->present = NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity; - rlc_BearerConfig->servedRadioBearer->choice.srb_Identity = 1; - rlc_BearerConfig->reestablishRLC = NULL; - rlc_Config = calloc(1, sizeof(NR_RLC_Config_t)); - rlc_Config->present = NR_RLC_Config_PR_am; - rlc_Config->choice.am = calloc(1, sizeof(*rlc_Config->choice.am)); - rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); - *(rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; - rlc_Config->choice.am->dl_AM_RLC.t_Reassembly = NR_T_Reassembly_ms35; - rlc_Config->choice.am->dl_AM_RLC.t_StatusProhibit = NR_T_StatusProhibit_ms0; - rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); - *(rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength) = NR_SN_FieldLengthAM_size12; - rlc_Config->choice.am->ul_AM_RLC.t_PollRetransmit = NR_T_PollRetransmit_ms45; - rlc_Config->choice.am->ul_AM_RLC.pollPDU = NR_PollPDU_infinity; - rlc_Config->choice.am->ul_AM_RLC.pollByte = NR_PollByte_infinity; - rlc_Config->choice.am->ul_AM_RLC.maxRetxThreshold = NR_UL_AM_RLC__maxRetxThreshold_t8; - rlc_BearerConfig->rlc_Config = rlc_Config; - logicalChannelConfig = calloc(1, sizeof(NR_LogicalChannelConfig_t)); - logicalChannelConfig->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig->ul_SpecificParameters)); - logicalChannelConfig->ul_SpecificParameters->priority = 1; - logicalChannelConfig->ul_SpecificParameters->prioritisedBitRate = NR_LogicalChannelConfig__ul_SpecificParameters__prioritisedBitRate_infinity; - logicalChannelGroup = CALLOC(1, sizeof(long)); - *logicalChannelGroup = 0; - logicalChannelConfig->ul_SpecificParameters->logicalChannelGroup = logicalChannelGroup; - rlc_BearerConfig->mac_LogicalChannelConfig = logicalChannelConfig; - ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig); - - cellGroupConfig->rlc_BearerToReleaseList = NULL; - cellGroupConfig->sCellToAddModList = NULL; - cellGroupConfig->sCellToReleaseList = NULL; - - /* mac CellGroup Config */ - mac_CellGroupConfig = calloc(1, sizeof(NR_MAC_CellGroupConfig_t)); - mac_CellGroupConfig->bsr_Config = calloc(1, sizeof(*mac_CellGroupConfig->bsr_Config)); - mac_CellGroupConfig->bsr_Config->periodicBSR_Timer = NR_BSR_Config__periodicBSR_Timer_sf10; - mac_CellGroupConfig->bsr_Config->retxBSR_Timer = NR_BSR_Config__retxBSR_Timer_sf80; - mac_CellGroupConfig->phr_Config = calloc(1, sizeof(*mac_CellGroupConfig->phr_Config)); - mac_CellGroupConfig->phr_Config->present = NR_SetupRelease_PHR_Config_PR_setup; - mac_CellGroupConfig->phr_Config->choice.setup = calloc(1, sizeof(*mac_CellGroupConfig->phr_Config->choice.setup)); - mac_CellGroupConfig->phr_Config->choice.setup->phr_PeriodicTimer = NR_PHR_Config__phr_PeriodicTimer_sf10; - mac_CellGroupConfig->phr_Config->choice.setup->phr_ProhibitTimer = NR_PHR_Config__phr_ProhibitTimer_sf10; - mac_CellGroupConfig->phr_Config->choice.setup->phr_Tx_PowerFactorChange = NR_PHR_Config__phr_Tx_PowerFactorChange_dB1; - cellGroupConfig->mac_CellGroupConfig = mac_CellGroupConfig; - - // cellGroupConfig.physicalCellGroupConfig; - - enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, - NULL, - (void *)cellGroupConfig, - masterCellGroup_buf, - 100); - - if(enc_rval.encoded == -1) { - LOG_E(NR_RRC, "ASN1 message CellGroupConfig encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; + if (masterCellGroup_from_DU) { + memcpy(&ie->masterCellGroup,masterCellGroup_from_DU,sizeof(*masterCellGroup_from_DU)); + // decode masterCellGroup OCTET_STRING received from DU and place in ue context + uper_decode(NULL, + &asn_DEF_NR_CellGroupConfig, //might be added prefix later + (void **)&cellGroupConfig, + (uint8_t *)masterCellGroup_from_DU->buf, + masterCellGroup_from_DU->size, 0, 0); + + xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)cellGroupConfig); } + else { + cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t)); + fill_initial_cellGroupConfig(ue_context_pP->ue_context.rnti,cellGroupConfig,scc); - if (OCTET_STRING_fromBuf(&ie->masterCellGroup, masterCellGroup_buf, (enc_rval.encoded+7)/8) == -1) { + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, + NULL, + (void *)cellGroupConfig, + masterCellGroup_buf, + 1000); + + if(enc_rval.encoded == -1) { + LOG_E(NR_RRC, "ASN1 message CellGroupConfig encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + return -1; + } + + if (OCTET_STRING_fromBuf(&ie->masterCellGroup, masterCellGroup_buf, (enc_rval.encoded+7)/8) == -1) { LOG_E(NR_RRC, "fatal: OCTET_STRING_fromBuf failed\n"); return -1; + } } + ue_p->masterCellGroup = cellGroupConfig; + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { - xer_fprint(stdout, &asn_DEF_NR_DL_CCCH_Message, (void *)&dl_ccch_msg); + xer_fprint(stdout, &asn_DEF_NR_DL_CCCH_Message, (void *)&dl_ccch_msg); } - enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_CCCH_Message, - NULL, - (void *)&dl_ccch_msg, - buffer, - 100); - + NULL, + (void *)&dl_ccch_msg, + buffer, + 1000); + if(enc_rval.encoded == -1) { - LOG_E(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; + LOG_E(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + return -1; } - + LOG_D(NR_RRC,"RRCSetup Encoded %zd bits (%zd bytes)\n", enc_rval.encoded,(enc_rval.encoded+7)/8); return((enc_rval.encoded+7)/8); @@ -1146,6 +1491,11 @@ uint8_t do_NR_SA_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP, const uint8_t Transaction_id) //------------------------------------------------------------------------------ { + NR_UE_CapabilityRequestFilterNR_t *sa_band_filter; + NR_FreqBandList_t *sa_band_list; + NR_FreqBandInformation_t *sa_band_info; + NR_FreqBandInformationNR_t *sa_band_infoNR; + NR_DL_DCCH_Message_t dl_dcch_msg; NR_UE_CapabilityRAT_Request_t *ue_capabilityrat_request; @@ -1162,6 +1512,35 @@ uint8_t do_NR_SA_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP, memset(ue_capabilityrat_request,0,sizeof(NR_UE_CapabilityRAT_Request_t)); ue_capabilityrat_request->rat_Type = NR_RAT_Type_nr; + sa_band_infoNR = (NR_FreqBandInformationNR_t*)calloc(1,sizeof(NR_FreqBandInformationNR_t)); + sa_band_infoNR->bandNR = 78; + sa_band_info = (NR_FreqBandInformation_t*)calloc(1,sizeof(NR_FreqBandInformation_t)); + sa_band_info->present = NR_FreqBandInformation_PR_bandInformationNR; + sa_band_info->choice.bandInformationNR = sa_band_infoNR; + + sa_band_list = (NR_FreqBandList_t *)calloc(1, sizeof(NR_FreqBandList_t)); + ASN_SEQUENCE_ADD(&sa_band_list->list, sa_band_info); + + sa_band_filter = (NR_UE_CapabilityRequestFilterNR_t*)calloc(1,sizeof(NR_UE_CapabilityRequestFilterNR_t)); + sa_band_filter->frequencyBandListFilter = sa_band_list; + + OCTET_STRING_t req_freq; + unsigned char req_freq_buf[1024]; + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UE_CapabilityRequestFilterNR, + NULL, + (void *)sa_band_filter, + req_freq_buf, + 1024); + + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_UE_CapabilityRequestFilterNR, (void *)sa_band_filter); + } + + req_freq.buf = req_freq_buf; + req_freq.size = (enc_rval.encoded+7)/8; + + ue_capabilityrat_request->capabilityRequestFilter = &req_freq; + ASN_SEQUENCE_ADD(&dl_dcch_msg.message.choice.c1->choice.ueCapabilityEnquiry->criticalExtensions.choice.ueCapabilityEnquiry->ue_CapabilityRAT_RequestList.list, ue_capabilityrat_request); @@ -1235,7 +1614,7 @@ uint8_t do_NR_RRCRelease(uint8_t *buffer, } //------------------------------------------------------------------------------ -uint16_t do_RRCReconfiguration( +int16_t do_RRCReconfiguration( const protocol_ctxt_t *const ctxt_pP, uint8_t *buffer, uint8_t Transaction_id, @@ -1245,14 +1624,15 @@ uint16_t do_RRCReconfiguration( NR_SecurityConfig_t *security_config, NR_SDAP_Config_t *sdap_config, NR_MeasConfig_t *meas_config, - struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList - *dedicatedNAS_MessageList, - NR_MAC_CellGroupConfig_t *mac_CellGroupConfig) + struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList, + NR_MAC_CellGroupConfig_t *mac_CellGroupConfig, + NR_CellGroupConfig_t *cellGroupConfig) //------------------------------------------------------------------------------ { NR_DL_DCCH_Message_t dl_dcch_msg; asn_enc_rval_t enc_rval; NR_RRCReconfiguration_IEs_t *ie; + unsigned char masterCellGroup_buf[1000]; memset(&dl_dcch_msg, 0, sizeof(NR_DL_DCCH_Message_t)); dl_dcch_msg.message.present = NR_DL_DCCH_MessageType_PR_c1; @@ -1296,20 +1676,42 @@ uint16_t do_RRCReconfiguration( // lateNonCriticalExtension ie->lateNonCriticalExtension = NULL; // nonCriticalExtension - ie->nonCriticalExtension = calloc(1, sizeof(NR_RRCReconfiguration_v1530_IEs_t)); - ie->nonCriticalExtension->dedicatedNAS_MessageList = dedicatedNAS_MessageList; + + if (cellGroupConfig || dedicatedNAS_MessageList) { + ie->nonCriticalExtension = calloc(1, sizeof(NR_RRCReconfiguration_v1530_IEs_t)); + if (dedicatedNAS_MessageList) + ie->nonCriticalExtension->dedicatedNAS_MessageList = dedicatedNAS_MessageList; + } + + if(cellGroupConfig!=NULL){ + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, + NULL, + (void *)cellGroupConfig, + masterCellGroup_buf, + 1000); + if(enc_rval.encoded == -1) { + LOG_E(NR_RRC, "ASN1 message CellGroupConfig encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + return -1; + } + xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)cellGroupConfig); + ie->nonCriticalExtension->masterCellGroup = calloc(1,sizeof(OCTET_STRING_t)); + + ie->nonCriticalExtension->masterCellGroup->buf = masterCellGroup_buf; + ie->nonCriticalExtension->masterCellGroup->size = (enc_rval.encoded+7)/8; + } dl_dcch_msg.message.choice.c1->choice.rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration = ie; - if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + //if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg); - } + //} enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, (void *)&dl_dcch_msg, buffer, - 100); + 1000); if(enc_rval.encoded == -1) { LOG_I(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", @@ -1509,6 +1911,7 @@ do_NR_DLInformationTransfer( AssertFatal(encoded > 0,"ASN1 message encoding failed (%s, %ld)!\n", "DLInformationTransfer", encoded); LOG_D(NR_RRC,"DLInformationTransfer Encoded %zd bytes\n", encoded); + //for (int i=0;icarrier.MIB = (uint8_t *) malloc16(4); - rrc->carrier.sizeof_MIB = do_MIB_NR(rrc,0); + if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { + rrc->carrier.MIB = (uint8_t *) malloc16(4); + rrc->carrier.sizeof_MIB = do_MIB_NR(rrc,0); + } - if(get_softmodem_params()->sa) { + if((get_softmodem_params()->sa) && ( (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)))) { rrc->carrier.sizeof_SIB1 = do_SIB1_NR(&rrc->carrier,configuration); } - LOG_I(NR_RRC,"Done init_NR_SI\n"); - rrc_mac_config_req_gNB(rrc->module_id, - rrc->carrier.ssb_SubcarrierOffset, - rrc->carrier.pdsch_AntennaPorts, - rrc->carrier.pusch_AntennaPorts, - rrc->carrier.pusch_TargetSNRx10, - rrc->carrier.pucch_TargetSNRx10, - (NR_ServingCellConfigCommon_t *)rrc->carrier.servingcellconfigcommon, - 0, - 0, // WIP hardcoded rnti - (NR_CellGroupConfig_t *)NULL - ); + if (!NODE_IS_DU(rrc->node_type)) { + rrc->carrier.SIB23 = (uint8_t *) malloc16(100); + AssertFatal(rrc->carrier.SIB23 != NULL, "cannot allocate memory for SIB"); + rrc->carrier.sizeof_SIB23 = do_SIB23_NR(&rrc->carrier, configuration); + LOG_I(NR_RRC,"do_SIB23_NR, size %d \n ", rrc->carrier.sizeof_SIB23); + AssertFatal(rrc->carrier.sizeof_SIB23 != 255,"FATAL, RC.nrrrc[mod].carrier[CC_id].sizeof_SIB23 == 255"); + } - if (get_softmodem_params()->phy_test > 0 || get_softmodem_params()->do_ra > 0 || get_softmodem_params()->sa > 0) { + LOG_I(NR_RRC,"Done init_NR_SI\n"); + + if (NODE_IS_MONOLITHIC(rrc->node_type)){ + rrc_mac_config_req_gNB(rrc->module_id, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, + (NR_ServingCellConfigCommon_t *)rrc->carrier.servingcellconfigcommon, + 0, + 0, // WIP hardcoded rnti + (NR_CellGroupConfig_t *)NULL + ); + } + + /* set flag to indicate that cell information is configured. This is required + * in DU to trigger F1AP_SETUP procedure */ + pthread_mutex_lock(&rrc->cell_info_mutex); + rrc->cell_info_configured=1; + pthread_mutex_unlock(&rrc->cell_info_mutex); + + if (get_softmodem_params()->phy_test > 0 || get_softmodem_params()->do_ra > 0) { // This is for phytest only, emulate first X2 message if uecap.raw file is present FILE *fd; fd = fopen("uecap.raw","r"); @@ -281,12 +299,13 @@ static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration ue_context_p->ue_context.spCellConfig = calloc(1, sizeof(struct NR_SpCellConfig)); ue_context_p->ue_context.spCellConfig->spCellConfigDedicated = configuration->scd; LOG_I(NR_RRC,"Adding new user (%p)\n",ue_context_p); - rrc_add_nsa_user(rrc,ue_context_p,NULL); + if (!NODE_IS_CU(RC.nrrrc[0]->node_type)) { + rrc_add_nsa_user(rrc,ue_context_p,NULL); + } } } } - char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigurationReq *configuration) { protocol_ctxt_t ctxt; gNB_RRC_INST *rrc=RC.nrrrc[gnb_mod_idP]; @@ -311,9 +330,9 @@ char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigu rrc->carrier.ssb_SubcarrierOffset = configuration->ssb_SubcarrierOffset; rrc->carrier.pdsch_AntennaPorts = configuration->pdsch_AntennaPorts; rrc->carrier.pusch_AntennaPorts = configuration->pusch_AntennaPorts; - rrc->carrier.pusch_TargetSNRx10 = configuration->pusch_TargetSNRx10; - rrc->carrier.pucch_TargetSNRx10 = configuration->pucch_TargetSNRx10; - /// System Information INIT + /// System Information INIT + pthread_mutex_init(&rrc->cell_info_mutex,NULL); + rrc->cell_info_configured = 0; LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_FMT" Checking release \n",PROTOCOL_NR_RRC_CTXT_ARGS(&ctxt)); init_NR_SI(rrc, configuration); rrc_init_nr_global_param(); @@ -362,14 +381,16 @@ rrc_gNB_get_next_transaction_identifier( //----------------------------------------------------------------------------- void rrc_gNB_generate_RRCSetup( - const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *const ue_context_pP, - const int CC_id + const protocol_ctxt_t *const ctxt_pP, + rrc_gNB_ue_context_t *const ue_context_pP, + OCTET_STRING_t *masterCellGroup_from_DU, + NR_ServingCellConfigCommon_t *scc, + const int CC_id ) //----------------------------------------------------------------------------- { LOG_I(NR_RRC, "rrc_gNB_generate_RRCSetup \n"); - NR_SRB_ToAddModList_t **SRB_configList = NULL; + MessageDef *message_p; // T(T_GNB_RRC_SETUP, // T_INT(ctxt_pP->module_id), @@ -377,55 +398,131 @@ rrc_gNB_generate_RRCSetup( // T_INT(ctxt_pP->subframe), // T_INT(ctxt_pP->rnti)); gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; - SRB_configList = &ue_p->SRB_configList; - ue_p->Srb0.Tx_buffer.payload_size = do_RRCSetup(ctxt_pP, - ue_context_pP, - CC_id, - (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, - rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), - SRB_configList); + ue_p->Srb0.Tx_buffer.payload_size = do_RRCSetup(ue_context_pP, + (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, + rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), + masterCellGroup_from_DU, + scc); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), ue_p->Srb0.Tx_buffer.payload_size, "[MSG] RRC Setup\n"); + gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; + switch (rrc->node_type) { + case ngran_gNB_CU: + // create an ITTI message + /* TODO: F1 IDs ar missing in RRC */ + nr_rrc_pdcp_config_asn1_req(ctxt_pP, + ue_context_pP->ue_context.SRB_configList, + NULL, + NULL, + 0xff, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, F1AP_DL_RRC_MESSAGE); + F1AP_DL_RRC_MESSAGE (message_p).rrc_container = (uint8_t *)ue_p->Srb0.Tx_buffer.Payload; + F1AP_DL_RRC_MESSAGE (message_p).rrc_container_length = ue_p->Srb0.Tx_buffer.payload_size; + F1AP_DL_RRC_MESSAGE (message_p).gNB_CU_ue_id = 0; + F1AP_DL_RRC_MESSAGE (message_p).gNB_DU_ue_id = 0; + F1AP_DL_RRC_MESSAGE (message_p).old_gNB_DU_ue_id = 0xFFFFFFFF; // unknown + F1AP_DL_RRC_MESSAGE (message_p).rnti = ue_p->rnti; + F1AP_DL_RRC_MESSAGE (message_p).srb_id = CCCH; + F1AP_DL_RRC_MESSAGE (message_p).execute_duplication = 1; + F1AP_DL_RRC_MESSAGE (message_p).RAT_frequency_priority_information.en_dc = 0; + itti_send_msg_to_task (TASK_CU_F1, ctxt_pP->module_id, message_p); + LOG_D(NR_RRC, "Send F1AP_DL_RRC_MESSAGE with ITTI\n"); - LOG_D(NR_RRC, - PROTOCOL_NR_RRC_CTXT_UE_FMT" RRC_gNB --- MAC_CONFIG_REQ (SRB1) ---> MAC_gNB\n", - PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP)); + break; - // rrc_mac_config_req_eNB + case ngran_gNB_DU: + // nothing to do for DU + AssertFatal(1==0,"nothing to do for DU\n"); + break; + + case ngran_gNB: + { + // rrc_mac_config_req_gNB - MSC_LOG_TX_MESSAGE( - MSC_RRC_GNB, - MSC_RRC_UE, - ue_p->Srb0.Tx_buffer.Header, // LG WARNING - ue_p->Srb0.Tx_buffer.payload_size, - MSC_AS_TIME_FMT" RRCSetup UE %x size %u", - MSC_AS_TIME_ARGS(ctxt_pP), - ue_context_pP->ue_context.rnti, - ue_p->Srb0.Tx_buffer.payload_size); - LOG_I(NR_RRC, - PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", - PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), - ue_p->Srb0.Tx_buffer.payload_size); - // activate release timer, if RRCSetupComplete not received after 100 frames, remove UE - ue_context_pP->ue_context.ue_release_timer = 1; - // remove UE after 10 frames after RRCConnectionRelease is triggered - ue_context_pP->ue_context.ue_release_timer_thres = 1000; - /* init timers */ - // ue_context_pP->ue_context.ue_rrc_inactivity_timer = 0; #ifdef ITTI_SIM - MessageDef *message_p; - uint8_t *message_buffer; - message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, + LOG_I(NR_RRC, + PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", + PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), ue_p->Srb0.Tx_buffer.payload_size); - memcpy (message_buffer, (uint8_t*)ue_p->Srb0.Tx_buffer.Payload, ue_p->Srb0.Tx_buffer.payload_size); - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_CCCH_DATA_IND); - GNB_RRC_CCCH_DATA_IND (message_p).sdu = message_buffer; - GNB_RRC_CCCH_DATA_IND (message_p).size = ue_p->Srb0.Tx_buffer.payload_size; - itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, + ue_p->Srb0.Tx_buffer.payload_size); + memcpy (message_buffer, (uint8_t*)ue_p->Srb0.Tx_buffer.Payload, ue_p->Srb0.Tx_buffer.payload_size); + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_CCCH_DATA_IND); + GNB_RRC_CCCH_DATA_IND (message_p).sdu = message_buffer; + GNB_RRC_CCCH_DATA_IND (message_p).size = ue_p->Srb0.Tx_buffer.payload_size; + itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); +#else + LOG_D(NR_RRC, + PROTOCOL_NR_RRC_CTXT_UE_FMT" RRC_gNB --- MAC_CONFIG_REQ (SRB1) ---> MAC_gNB\n", + PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP)); + MSC_LOG_TX_MESSAGE( + MSC_RRC_GNB, + MSC_RRC_UE, + ue_p->Srb0.Tx_buffer.Header, // LG WARNING + ue_p->Srb0.Tx_buffer.payload_size, + MSC_AS_TIME_FMT" RRCSetup UE %x size %u", + MSC_AS_TIME_ARGS(ctxt_pP), + ue_context_pP->ue_context.rnti, + ue_p->Srb0.Tx_buffer.payload_size); + LOG_I(NR_RRC, + PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", + PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), + ue_p->Srb0.Tx_buffer.payload_size); + // activate release timer, if RRCSetupComplete not received after 100 frames, remove UE + ue_context_pP->ue_context.ue_release_timer = 1; + // remove UE after 10 frames after RRCConnectionRelease is triggered + ue_context_pP->ue_context.ue_release_timer_thres = 1000; + /* init timers */ + // ue_context_pP->ue_context.ue_rrc_inactivity_timer = 0; + + // configure MAC + rrc_mac_config_req_gNB(rrc->module_id, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, + NULL, + 0, + ue_context_pP->ue_context.rnti, + get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup : (NR_CellGroupConfig_t *)NULL); + + nr_rrc_rlc_config_asn1_req(ctxt_pP, + ue_context_pP->ue_context.SRB_configList, + NULL, + NULL, + NULL, + get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup->rlc_BearerToAddModList : NULL); + + nr_rrc_pdcp_config_asn1_req(ctxt_pP, + ue_context_pP->ue_context.SRB_configList, + NULL, + NULL, + 0xff, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup->rlc_BearerToAddModList : NULL); #endif + } + break; + + default: + LOG_W(NR_RRC, "Unknown node type %d\n", rrc->node_type); + break; + } } //----------------------------------------------------------------------------- @@ -437,20 +534,18 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( //----------------------------------------------------------------------------- { LOG_I(NR_RRC, "generate RRCSetup for RRCReestablishmentRequest \n"); - NR_SRB_ToAddModList_t **SRB_configList = NULL; rrc_gNB_ue_context_t *ue_context_pP = NULL; gNB_RRC_INST *rrc_instance_p = RC.nrrrc[ctxt_pP->module_id]; + NR_ServingCellConfigCommon_t *scc=rrc_instance_p->carrier.servingcellconfigcommon; ue_context_pP = rrc_gNB_get_next_free_ue_context(ctxt_pP, rrc_instance_p, 0); gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; - SRB_configList = &ue_p->SRB_configList; - ue_p->Srb0.Tx_buffer.payload_size = do_RRCSetup(ctxt_pP, - ue_context_pP, - CC_id, - (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, - rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), - SRB_configList); + ue_p->Srb0.Tx_buffer.payload_size = do_RRCSetup(ue_context_pP, + (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, + rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), + NULL, + scc); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), @@ -465,43 +560,41 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( rrc_instance_p->carrier.ssb_SubcarrierOffset, rrc_instance_p->carrier.pdsch_AntennaPorts, rrc_instance_p->carrier.pusch_AntennaPorts, - rrc_instance_p->carrier.pusch_TargetSNRx10, - rrc_instance_p->carrier.pucch_TargetSNRx10, (NR_ServingCellConfigCommon_t *)rrc_instance_p->carrier.servingcellconfigcommon, 0, ue_context_pP->ue_context.rnti, (NR_CellGroupConfig_t *)NULL - ); + ); - MSC_LOG_TX_MESSAGE( - MSC_RRC_GNB, - MSC_RRC_UE, - ue_p->Srb0.Tx_buffer.Header, // LG WARNING - ue_p->Srb0.Tx_buffer.payload_size, - MSC_AS_TIME_FMT" RRCSetup UE %x size %u", - MSC_AS_TIME_ARGS(ctxt_pP), - ue_context_pP->ue_context.rnti, - ue_p->Srb0.Tx_buffer.payload_size); - LOG_I(NR_RRC, + MSC_LOG_TX_MESSAGE( + MSC_RRC_GNB, + MSC_RRC_UE, + ue_p->Srb0.Tx_buffer.Header, // LG WARNING + ue_p->Srb0.Tx_buffer.payload_size, + MSC_AS_TIME_FMT" RRCSetup UE %x size %u", + MSC_AS_TIME_ARGS(ctxt_pP), + ue_context_pP->ue_context.rnti, + ue_p->Srb0.Tx_buffer.payload_size); + LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), ue_p->Srb0.Tx_buffer.payload_size); - // activate release timer, if RRCSetupComplete not received after 100 frames, remove UE - ue_context_pP->ue_context.ue_release_timer = 1; - // remove UE after 10 frames after RRCConnectionRelease is triggered - ue_context_pP->ue_context.ue_release_timer_thres = 1000; - /* init timers */ - // ue_context_pP->ue_context.ue_rrc_inactivity_timer = 0; + // activate release timer, if RRCSetupComplete not received after 100 frames, remove UE + ue_context_pP->ue_context.ue_release_timer = 1; + // remove UE after 10 frames after RRCConnectionRelease is triggered + ue_context_pP->ue_context.ue_release_timer_thres = 1000; + /* init timers */ + // ue_context_pP->ue_context.ue_rrc_inactivity_timer = 0; #ifdef ITTI_SIM - MessageDef *message_p; - uint8_t *message_buffer; - message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, - ue_p->Srb0.Tx_buffer.payload_size); - memcpy (message_buffer, (uint8_t*)ue_p->Srb0.Tx_buffer.Payload, ue_p->Srb0.Tx_buffer.payload_size); - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_CCCH_DATA_IND); - GNB_RRC_CCCH_DATA_IND (message_p).sdu = message_buffer; - GNB_RRC_CCCH_DATA_IND (message_p).size = ue_p->Srb0.Tx_buffer.payload_size; - itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); + MessageDef *message_p; + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, + ue_p->Srb0.Tx_buffer.payload_size); + memcpy (message_buffer, (uint8_t*)ue_p->Srb0.Tx_buffer.Payload, ue_p->Srb0.Tx_buffer.payload_size); + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_CCCH_DATA_IND); + GNB_RRC_CCCH_DATA_IND (message_p).sdu = message_buffer; + GNB_RRC_CCCH_DATA_IND (message_p).size = ue_p->Srb0.Tx_buffer.payload_size; + itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); #endif } @@ -515,6 +608,8 @@ rrc_gNB_generate_RRCReject( { LOG_I(NR_RRC, "rrc_gNB_generate_RRCReject \n"); gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; + MessageDef *message_p; + ue_p->Srb0.Tx_buffer.payload_size = do_RRCReject(ctxt_pP->module_id, (uint8_t *)ue_p->Srb0.Tx_buffer.Payload); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, @@ -534,17 +629,47 @@ rrc_gNB_generate_RRCReject( PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), ue_p->Srb0.Tx_buffer.payload_size); + switch (RC.nrrrc[ctxt_pP->module_id]->node_type) { + case ngran_gNB_CU: + // create an ITTI message + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, F1AP_DL_RRC_MESSAGE); + F1AP_DL_RRC_MESSAGE (message_p).rrc_container = (uint8_t *)ue_p->Srb0.Tx_buffer.Payload; + F1AP_DL_RRC_MESSAGE (message_p).rrc_container_length = ue_p->Srb0.Tx_buffer.payload_size; + F1AP_DL_RRC_MESSAGE (message_p).gNB_CU_ue_id = 0; + F1AP_DL_RRC_MESSAGE (message_p).gNB_DU_ue_id = 0; + F1AP_DL_RRC_MESSAGE (message_p).old_gNB_DU_ue_id = 0xFFFFFFFF; // unknown + F1AP_DL_RRC_MESSAGE (message_p).rnti = ue_p->rnti; + F1AP_DL_RRC_MESSAGE (message_p).srb_id = CCCH; + F1AP_DL_RRC_MESSAGE (message_p).execute_duplication = 1; + F1AP_DL_RRC_MESSAGE (message_p).RAT_frequency_priority_information.en_dc = 0; + itti_send_msg_to_task (TASK_CU_F1, ctxt_pP->module_id, message_p); + LOG_D(NR_RRC, "Send F1AP_DL_RRC_MESSAGE with ITTI\n"); + break; + + case ngran_gNB_DU: + // nothing to do for DU + AssertFatal(1==0,"nothing to do for DU\n"); + break; + + case ngran_gNB: + { #ifdef ITTI_SIM - MessageDef *message_p; - uint8_t *message_buffer; - message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, - ue_p->Srb0.Tx_buffer.payload_size); - memcpy (message_buffer, (uint8_t*)ue_p->Srb0.Tx_buffer.Payload, ue_p->Srb0.Tx_buffer.payload_size); - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_CCCH_DATA_IND); - GNB_RRC_CCCH_DATA_IND (message_p).sdu = message_buffer; - GNB_RRC_CCCH_DATA_IND (message_p).size = ue_p->Srb0.Tx_buffer.payload_size; - itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, + ue_p->Srb0.Tx_buffer.payload_size); + memcpy (message_buffer, (uint8_t*)ue_p->Srb0.Tx_buffer.Payload, ue_p->Srb0.Tx_buffer.payload_size); + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_CCCH_DATA_IND); + GNB_RRC_CCCH_DATA_IND (message_p).sdu = message_buffer; + GNB_RRC_CCCH_DATA_IND (message_p).size = ue_p->Srb0.Tx_buffer.payload_size; + itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); #endif + // rrc_mac_config_req_gNB + } + break; + + default : + LOG_W(NR_RRC, "Unknown node type %d\n", RC.nrrrc[ctxt_pP->module_id]->node_type); + } } //----------------------------------------------------------------------------- @@ -562,7 +687,8 @@ rrc_gNB_process_RRCSetupComplete( LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel UL-DCCH, " "processing NR_RRCSetupComplete from UE (SRB1 Active)\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP)); ue_context_pP->ue_context.Srb1.Active = 1; - ue_context_pP->ue_context.status = NR_RRC_CONNECTED; + ue_context_pP->ue_context.Srb1.Srb_info.Srb_id = 1; + ue_context_pP->ue_context.StatusRrc = NR_RRC_CONNECTED; if (AMF_MODE_ENABLED) { rrc_gNB_send_NGAP_NAS_FIRST_REQ(ctxt_pP, ue_context_pP, rrcSetupComplete); @@ -581,13 +707,13 @@ rrc_gNB_generate_defaultRRCReconfiguration( { uint8_t buffer[RRC_BUF_SIZE]; uint16_t size; - NR_SRB_ToAddModList_t **SRB_configList2 = NULL; + /*NR_SRB_ToAddModList_t **SRB_configList2 = NULL; NR_SRB_ToAddModList_t *SRB_configList = ue_context_pP->ue_context.SRB_configList; NR_DRB_ToAddModList_t **DRB_configList = NULL; NR_DRB_ToAddModList_t **DRB_configList2 = NULL; NR_SRB_ToAddMod_t *SRB2_config = NULL; NR_DRB_ToAddMod_t *DRB_config = NULL; - NR_SDAP_Config_t *sdap_config = NULL; + NR_SDAP_Config_t *sdap_config = NULL;*/ struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = NULL; NR_DedicatedNAS_Message_t *dedicatedNAS_Message = NULL; @@ -596,7 +722,7 @@ rrc_gNB_generate_defaultRRCReconfiguration( /******************** Radio Bearer Config ********************/ /* Configure SRB2 */ - SRB_configList2 = &ue_context_pP->ue_context.SRB_configList2[xid]; + /*SRB_configList2 = &ue_context_pP->ue_context.SRB_configList2[xid]; if (*SRB_configList2) { free(*SRB_configList2); } @@ -605,10 +731,10 @@ rrc_gNB_generate_defaultRRCReconfiguration( SRB2_config = CALLOC(1, sizeof(*SRB2_config)); SRB2_config->srb_Identity = 2; ASN_SEQUENCE_ADD(&(*SRB_configList2)->list, SRB2_config); - ASN_SEQUENCE_ADD(&SRB_configList->list, SRB2_config); + ASN_SEQUENCE_ADD(&SRB_configList->list, SRB2_config);*/ /* Configure DRB */ - DRB_configList = &ue_context_pP->ue_context.DRB_configList; + /*DRB_configList = &ue_context_pP->ue_context.DRB_configList; if (*DRB_configList) { free(*DRB_configList); } @@ -654,7 +780,7 @@ rrc_gNB_generate_defaultRRCReconfiguration( DRB_config->pdcp_Config->ext1 = NULL; ASN_SEQUENCE_ADD(&(*DRB_configList)->list, DRB_config); - ASN_SEQUENCE_ADD(&(*DRB_configList2)->list, DRB_config); + ASN_SEQUENCE_ADD(&(*DRB_configList2)->list, DRB_config);*/ dedicatedNAS_MessageList = CALLOC(1, sizeof(struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList)); @@ -676,13 +802,14 @@ rrc_gNB_generate_defaultRRCReconfiguration( memset(buffer, 0, RRC_BUF_SIZE); size = do_RRCReconfiguration(ctxt_pP, buffer, xid, - *SRB_configList2, - *DRB_configList, + NULL, //*SRB_configList2, + NULL, //*DRB_configList, NULL, NULL, NULL, NULL, dedicatedNAS_MessageList, + NULL, NULL); free(ue_context_pP->ue_context.nas_pdu.buffer); @@ -693,7 +820,38 @@ rrc_gNB_generate_defaultRRCReconfiguration( ctxt_pP->frame, size, ue_context_pP->ue_context.rnti); - LOG_D(NR_RRC, "[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n", + switch (RC.nrrrc[ctxt_pP->module_id]->node_type) { + case ngran_gNB_CU: + nr_rrc_data_req(ctxt_pP, + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + // rrc_pdcp_config_asn1_req + + break; + + case ngran_gNB_DU: + // nothing to do for DU + AssertFatal(1==0,"nothing to do for DU\n"); + break; + + case ngran_gNB: + { +#ifdef ITTI_SIM + MessageDef *message_p; + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, size); + memcpy (message_buffer, buffer, size); + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_DCCH_DATA_IND); + GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; + GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; + GNB_RRC_DCCH_DATA_IND (message_p).size = size; + itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); +#else + LOG_D(NR_RRC, "[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n", ctxt_pP->frame, ctxt_pP->module_id, size, @@ -701,7 +859,7 @@ rrc_gNB_generate_defaultRRCReconfiguration( rrc_gNB_mui, ctxt_pP->module_id, DCCH); - MSC_LOG_TX_MESSAGE(MSC_RRC_GNB, + MSC_LOG_TX_MESSAGE(MSC_RRC_GNB, MSC_RRC_UE, buffer, size, @@ -710,27 +868,247 @@ rrc_gNB_generate_defaultRRCReconfiguration( ue_context_pP->ue_context.rnti, rrc_gNB_mui, size); + nr_rrc_data_req(ctxt_pP, + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + // rrc_pdcp_config_asn1_req +#endif + // rrc_rlc_config_asn1_req + } + break; + + default : + LOG_W(NR_RRC, "Unknown node type %d\n", RC.nrrrc[ctxt_pP->module_id]->node_type); + } +} + +//----------------------------------------------------------------------------- +void +rrc_gNB_generate_dedicatedRRCReconfiguration( + const protocol_ctxt_t *const ctxt_pP, + rrc_gNB_ue_context_t *ue_context_pP +) +//----------------------------------------------------------------------------- +{ + NR_DRB_ToAddMod_t *DRB_config = NULL; + NR_SRB_ToAddMod_t *SRB2_config = NULL; + NR_SDAP_Config_t *sdap_config = NULL; + NR_DRB_ToAddModList_t **DRB_configList = NULL; + NR_DRB_ToAddModList_t **DRB_configList2 = NULL; + NR_SRB_ToAddModList_t **SRB_configList2 = NULL; + NR_SRB_ToAddModList_t *SRB_configList = ue_context_pP->ue_context.SRB_configList; + struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList + *dedicatedNAS_MessageList = NULL; + NR_DedicatedNAS_Message_t *dedicatedNAS_Message = NULL; + uint8_t buffer[RRC_BUF_SIZE]; + uint16_t size; + int qos_flow_index = 0; + NR_QFI_t qfi = 0; + int pdu_sessions_done = 0; + int i; + NR_CellGroupConfig_t *cellGroupConfig; + + uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); + + /* Configure SRB2 */ + SRB_configList2 = &ue_context_pP->ue_context.SRB_configList2[xid]; + if (*SRB_configList2) { + free(*SRB_configList2); + } + *SRB_configList2 = CALLOC(1, sizeof(**SRB_configList2)); + memset(*SRB_configList2, 0, sizeof(**SRB_configList2)); + SRB2_config = CALLOC(1, sizeof(*SRB2_config)); + SRB2_config->srb_Identity = 2; + ASN_SEQUENCE_ADD(&(*SRB_configList2)->list, SRB2_config); + ASN_SEQUENCE_ADD(&SRB_configList->list, SRB2_config); + + DRB_configList = &ue_context_pP->ue_context.DRB_configList; + if (*DRB_configList) { + free(*DRB_configList); + } + *DRB_configList = CALLOC(1, sizeof(**DRB_configList)); + memset(*DRB_configList, 0, sizeof(**DRB_configList)); + + DRB_configList2 = &ue_context_pP->ue_context.DRB_configList2[xid]; + if (*DRB_configList2) { + free(*DRB_configList2); + } + *DRB_configList2 = CALLOC(1, sizeof(**DRB_configList2)); + memset(*DRB_configList2, 0, sizeof(**DRB_configList2)); + + dedicatedNAS_MessageList = CALLOC(1, sizeof(struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList)); + + for (i = 0; i < ue_context_pP->ue_context.setup_pdu_sessions; i++) { + if (pdu_sessions_done >= ue_context_pP->ue_context.nb_of_pdusessions) { + break; + } + + if (ue_context_pP->ue_context.pdusession[i].status >= PDU_SESSION_STATUS_DONE) { + continue; + } + + DRB_config = CALLOC(1, sizeof(*DRB_config)); + DRB_config->drb_Identity = i+1; + DRB_config->cnAssociation = CALLOC(1, sizeof(*DRB_config->cnAssociation)); + DRB_config->cnAssociation->present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config; + // sdap_Config + sdap_config = CALLOC(1, sizeof(NR_SDAP_Config_t)); + memset(sdap_config, 0, sizeof(NR_SDAP_Config_t)); + sdap_config->pdu_Session = ue_context_pP->ue_context.pdusession[i].param.pdusession_id; + sdap_config->sdap_HeaderDL = NR_SDAP_Config__sdap_HeaderDL_absent; + sdap_config->sdap_HeaderUL = NR_SDAP_Config__sdap_HeaderUL_present; + sdap_config->defaultDRB = TRUE; + sdap_config->mappedQoS_FlowsToAdd = calloc(1, sizeof(struct NR_SDAP_Config__mappedQoS_FlowsToAdd)); + memset(sdap_config->mappedQoS_FlowsToAdd, 0, sizeof(struct NR_SDAP_Config__mappedQoS_FlowsToAdd)); + + for (qos_flow_index = 0; qos_flow_index < ue_context_pP->ue_context.pdusession[i].param.nb_qos; qos_flow_index++) { + qfi = ue_context_pP->ue_context.pdusession[i].param.qos[qos_flow_index].qfi; + ASN_SEQUENCE_ADD(&sdap_config->mappedQoS_FlowsToAdd->list, &qfi); + } + sdap_config->mappedQoS_FlowsToRelease = NULL; + DRB_config->cnAssociation->choice.sdap_Config = sdap_config; + + // pdcp_Config + DRB_config->reestablishPDCP = NULL; + DRB_config->recoverPDCP = NULL; + DRB_config->pdcp_Config = calloc(1, sizeof(*DRB_config->pdcp_Config)); + DRB_config->pdcp_Config->drb = calloc(1,sizeof(*DRB_config->pdcp_Config->drb)); + DRB_config->pdcp_Config->drb->discardTimer = calloc(1, sizeof(*DRB_config->pdcp_Config->drb->discardTimer)); + *DRB_config->pdcp_Config->drb->discardTimer = NR_PDCP_Config__drb__discardTimer_infinity; + DRB_config->pdcp_Config->drb->pdcp_SN_SizeUL = calloc(1, sizeof(*DRB_config->pdcp_Config->drb->pdcp_SN_SizeUL)); + *DRB_config->pdcp_Config->drb->pdcp_SN_SizeUL = NR_PDCP_Config__drb__pdcp_SN_SizeUL_len18bits; + DRB_config->pdcp_Config->drb->pdcp_SN_SizeDL = calloc(1, sizeof(*DRB_config->pdcp_Config->drb->pdcp_SN_SizeDL)); + *DRB_config->pdcp_Config->drb->pdcp_SN_SizeDL = NR_PDCP_Config__drb__pdcp_SN_SizeDL_len18bits; + DRB_config->pdcp_Config->drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed; + DRB_config->pdcp_Config->drb->headerCompression.choice.notUsed = 0; + + DRB_config->pdcp_Config->drb->integrityProtection = NULL; + DRB_config->pdcp_Config->drb->statusReportRequired = NULL; + DRB_config->pdcp_Config->drb->outOfOrderDelivery = NULL; + DRB_config->pdcp_Config->moreThanOneRLC = NULL; + + DRB_config->pdcp_Config->t_Reordering = calloc(1, sizeof(*DRB_config->pdcp_Config->t_Reordering)); + *DRB_config->pdcp_Config->t_Reordering = NR_PDCP_Config__t_Reordering_ms750; + DRB_config->pdcp_Config->ext1 = NULL; + + // Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping + for (qos_flow_index = 0; qos_flow_index < ue_context_pP->ue_context.pdusession[i].param.nb_qos; qos_flow_index++) { + switch (ue_context_pP->ue_context.pdusession[i].param.qos[qos_flow_index].fiveQI) { + case 1: //100ms + case 2: //150ms + case 3: //50ms + case 4: //300ms + case 5: //100ms + case 6: //300ms + case 7: //100ms + case 8: //300ms + case 9: //300ms Video (Buffered Streaming)TCP-based (e.g., www, e-mail, chat, ftp, p2p file sharing, progressive video, etc.) + // TODO + break; + + default: + LOG_E(NR_RRC,"not supported 5qi %lu\n", ue_context_pP->ue_context.pdusession[i].param.qos[qos_flow_index].fiveQI); + ue_context_pP->ue_context.pdusession[i].status = PDU_SESSION_STATUS_FAILED; + ue_context_pP->ue_context.pdusession[i].xid = xid; + pdu_sessions_done++; + free(DRB_config); + continue; + } + } + + ASN_SEQUENCE_ADD(&(*DRB_configList)->list, DRB_config); + ASN_SEQUENCE_ADD(&(*DRB_configList2)->list, DRB_config); + + ue_context_pP->ue_context.pdusession[i].status = PDU_SESSION_STATUS_DONE; + ue_context_pP->ue_context.pdusession[i].xid = xid; + + if (ue_context_pP->ue_context.pdusession[i].param.nas_pdu.buffer != NULL) { + dedicatedNAS_Message = CALLOC(1, sizeof(NR_DedicatedNAS_Message_t)); + memset(dedicatedNAS_Message, 0, sizeof(OCTET_STRING_t)); + OCTET_STRING_fromBuf(dedicatedNAS_Message, + (char *)ue_context_pP->ue_context.pdusession[i].param.nas_pdu.buffer, + ue_context_pP->ue_context.pdusession[i].param.nas_pdu.length); + ASN_SEQUENCE_ADD(&dedicatedNAS_MessageList->list, dedicatedNAS_Message); + + LOG_I(NR_RRC,"add NAS info with size %d (pdusession id %d)\n",ue_context_pP->ue_context.pdusession[i].param.nas_pdu.length, i); + } else { + // TODO + LOG_E(NR_RRC,"no NAS info (pdusession id %d)\n", i); + } + } + + /* If list is empty free the list and reset the address */ + if (dedicatedNAS_MessageList->list.count == 0) { + free(dedicatedNAS_MessageList); + dedicatedNAS_MessageList = NULL; + } + + memset(buffer, 0, RRC_BUF_SIZE); + cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t)); + fill_mastercellGroupConfig(cellGroupConfig, ue_context_pP->ue_context.masterCellGroup); + size = do_RRCReconfiguration(ctxt_pP, buffer, + xid, + *SRB_configList2, + *DRB_configList, + NULL, + NULL, + NULL, + NULL, + dedicatedNAS_MessageList, + NULL, + cellGroupConfig); + LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); + + /* Free all NAS PDUs */ + for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { + if (ue_context_pP->ue_context.pdusession[i].param.nas_pdu.buffer != NULL) { + /* Free the NAS PDU buffer and invalidate it */ + free(ue_context_pP->ue_context.pdusession[i].param.nas_pdu.buffer); + ue_context_pP->ue_context.pdusession[i].param.nas_pdu.buffer = NULL; + } + } + + LOG_I(NR_RRC, + "[gNB %d] Frame %d, Logical Channel DL-DCCH, Generate RRCReconfiguration (bytes %d, UE RNTI %x)\n", + ctxt_pP->module_id, ctxt_pP->frame, size, ue_context_pP->ue_context.rnti); + LOG_D(NR_RRC, + "[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n", + ctxt_pP->frame, ctxt_pP->module_id, size, ue_context_pP->ue_context.rnti, rrc_gNB_mui, ctxt_pP->module_id, DCCH); + MSC_LOG_TX_MESSAGE( + MSC_RRC_GNB, + MSC_RRC_UE, + buffer, + size, + MSC_AS_TIME_FMT" dedicated RRCReconfiguration UE %x MUI %d size %u", + MSC_AS_TIME_ARGS(ctxt_pP), + ue_context_pP->ue_context.rnti, + rrc_gNB_mui, + size); + #ifdef ITTI_SIM - MessageDef *message_p; - uint8_t *message_buffer; - message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, size); - memcpy (message_buffer, buffer, size); - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_DCCH_DATA_IND); - GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; - GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; - GNB_RRC_DCCH_DATA_IND (message_p).size = size; - itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); + MessageDef *message_p; + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_GNB_SIM, TASK_RRC_UE_SIM, size); + memcpy (message_buffer, buffer, size); + message_p = itti_alloc_new_message (TASK_RRC_GNB_SIM, 0, GNB_RRC_DCCH_DATA_IND); + GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; + GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; + GNB_RRC_DCCH_DATA_IND (message_p).size = size; + itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); #else - nr_rrc_data_req(ctxt_pP, - DCCH, - rrc_gNB_mui++, - SDU_CONFIRM_NO, - size, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); + nr_rrc_data_req( + ctxt_pP, + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); #endif - // rrc_pdcp_config_asn1_req - // rrc_rlc_config_asn1_req } //----------------------------------------------------------------------------- @@ -789,6 +1167,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( NULL, NULL, dedicatedNAS_MessageList, + NULL, NULL); ue_context_pP->ue_context.pdu_session_release_command_flag = 1; @@ -883,6 +1262,8 @@ rrc_gNB_process_RRCReconfigurationComplete( ue_context_pP->ue_context.rnti); #ifndef ITTI_SIM + LOG_D(NR_RRC,"Configuring PDCP DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); + nr_rrc_pdcp_config_asn1_req(ctxt_pP, SRB_configList, // NULL, DRB_configList, @@ -894,14 +1275,17 @@ rrc_gNB_process_RRCReconfigurationComplete( NULL, NULL, NULL, - NULL); + get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup->rlc_BearerToAddModList : NULL); /* Refresh SRBs/DRBs */ - nr_rrc_rlc_config_asn1_req(ctxt_pP, - SRB_configList, // NULL, - DRB_configList, - DRB_Release_configList2, - NULL, - NULL); + if (!NODE_IS_CU(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + LOG_D(NR_RRC,"Configuring RLC DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); + nr_rrc_rlc_config_asn1_req(ctxt_pP, + SRB_configList, // NULL, + DRB_configList, + DRB_Release_configList2, + NULL, + get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup->rlc_BearerToAddModList : NULL); + } #endif /* Set the SRB active in UE context */ @@ -909,6 +1293,7 @@ rrc_gNB_process_RRCReconfigurationComplete( for (int i = 0; (i < SRB_configList->list.count) && (i < 3); i++) { if (SRB_configList->list.array[i]->srb_Identity == 1) { ue_context_pP->ue_context.Srb1.Active = 1; + ue_context_pP->ue_context.Srb1.Srb_info.Srb_id = 1; } else if (SRB_configList->list.array[i]->srb_Identity == 2) { ue_context_pP->ue_context.Srb2.Active = 1; ue_context_pP->ue_context.Srb2.Srb_info.Srb_id = 2; @@ -1111,7 +1496,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( uint8_t next_xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); int ret = 0; - ue_context_pP->ue_context.status = NR_RRC_CONNECTED; + ue_context_pP->ue_context.StatusRrc = NR_RRC_CONNECTED; ue_context_pP->ue_context.ue_rrc_inactivity_timer = 1; // set rrc inactivity when UE goes into RRC_CONNECTED ue_context_pP->ue_context.reestablishment_xid = next_xid; SRB_configList2 = &ue_context_pP->ue_context.SRB_configList2[xid]; @@ -1290,6 +1675,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( NULL, NULL, // MeasObj_list, NULL, + NULL, NULL); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -1324,15 +1710,15 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( rrc_gNB_mui, size); #ifdef ITTI_SIM - MessageDef *message_p; - uint8_t *message_buffer; - message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, size); - memcpy (message_buffer, buffer, size); - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_DCCH_DATA_IND); - GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; - GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; - GNB_RRC_DCCH_DATA_IND (message_p).size = size; - itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); + MessageDef *message_p; + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, size); + memcpy (message_buffer, buffer, size); + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_DCCH_DATA_IND); + GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; + GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; + GNB_RRC_DCCH_DATA_IND (message_p).size = size; + itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); #else nr_rrc_data_req( ctxt_pP, @@ -1351,6 +1737,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, const uint8_t *buffer, int buffer_length, + OCTET_STRING_t *du_to_cu_rrc_container, const int CC_id) { module_id_t Idx; @@ -1475,9 +1862,9 @@ int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, LOG_E(RRC, "%s:%d:%s: rrc_gNB_get_next_free_ue_context returned NULL\n", __FILE__, __LINE__, __FUNCTION__); } - if (ue_context_p != NULL) { - ue_context_p->ue_context.Initialue_identity_5g_s_TMSI.presence = TRUE; - ue_context_p->ue_context.ng_5G_S_TMSI_Part1 = s_tmsi_part1; + if (ue_context_p != NULL) { + ue_context_p->ue_context.Initialue_identity_5g_s_TMSI.presence = TRUE; + ue_context_p->ue_context.ng_5G_S_TMSI_Part1 = s_tmsi_part1; } } } else { @@ -1500,10 +1887,9 @@ int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, rrc_gNB_generate_RRCSetup(ctxt_pP, rrc_gNB_get_ue_context(gnb_rrc_inst, ctxt_pP->rnti), + du_to_cu_rrc_container, + gnb_rrc_inst->carrier.servingcellconfigcommon, CC_id); - - // FIXME: Check the best place to perform this DRB configuration - nr_DRB_preconfiguration(ctxt_pP->rnti); } break; @@ -1602,7 +1988,7 @@ int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, } #endif //c-plane not end - if((ue_context_p->ue_context.status != NR_RRC_RECONFIGURED) && (ue_context_p->ue_context.reestablishment_cause == NR_ReestablishmentCause_spare1)) { + if((ue_context_p->ue_context.StatusRrc != NR_RRC_RECONFIGURED) && (ue_context_p->ue_context.reestablishment_cause == NR_ReestablishmentCause_spare1)) { LOG_E(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" NR_RRCReestablishmentRequest (UE %x c-plane is not end), RRC establishment failed \n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP),c_rnti); @@ -1614,9 +2000,9 @@ int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, LOG_E(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" RRRCReconfigurationComplete(Previous) don't receive, delete the Previous UE,\nprevious Status %d, new Status NR_RRC_RECONFIGURED\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), - ue_context_p->ue_context.status + ue_context_p->ue_context.StatusRrc ); - ue_context_p->ue_context.status = NR_RRC_RECONFIGURED; + ue_context_p->ue_context.StatusRrc = NR_RRC_RECONFIGURED; protocol_ctxt_t ctxt_old_p; PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt_old_p, ctxt_pP->instance, @@ -1791,6 +2177,10 @@ rrc_gNB_decode_dcch( LOG_D(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" Decoding UL-DCCH Message\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP)); + + //for (int i=0;igNB_DU_id,f1_setup_req->gNB_DU_name); + int cu_cell_ind = 0; + MessageDef *msg_p = NULL,*msg_p2=NULL; + + for (int i = 0; i < f1_setup_req->num_cells_available; i++) { + int found_cell=0; + for (int j=0; jconfiguration.mcc[0] == f1_setup_req->mcc[i] && + rrc->configuration.mnc[0] == f1_setup_req->mnc[i] && + rrc->nr_cellid == f1_setup_req->nr_cellid[i]) { + // check that CU rrc instance corresponds to mcc/mnc/cgi (normally cgi should be enough, but just in case) + rrc->carrier.MIB = malloc(f1_setup_req->mib_length[i]); + rrc->carrier.sizeof_MIB = f1_setup_req->mib_length[i]; + LOG_W(NR_RRC, "instance %d mib length %d\n", i, f1_setup_req->mib_length[i]); + LOG_W(NR_RRC, "instance %d sib1 length %d\n", i, f1_setup_req->sib1_length[i]); + memcpy((void *)rrc->carrier.MIB,f1_setup_req->mib[i],f1_setup_req->mib_length[i]); + asn_dec_rval_t dec_rval = uper_decode_complete(NULL, + &asn_DEF_NR_BCCH_BCH_Message, + (void **)&rrc->carrier.mib_DU, + f1_setup_req->mib[i], + f1_setup_req->mib_length[i]); + AssertFatal(dec_rval.code == RC_OK, + "[gNB_DU %"PRIu8"] Failed to decode NR_BCCH_BCH_MESSAGE (%zu bits)\n", + j, + dec_rval.consumed ); + NR_BCCH_BCH_Message_t *mib = &rrc->carrier.mib; + NR_BCCH_BCH_Message_t *mib_DU = rrc->carrier.mib_DU; + mib->message.present = NR_BCCH_BCH_MessageType_PR_mib; + mib->message.choice.mib = CALLOC(1,sizeof(struct NR_MIB)); + memset(mib->message.choice.mib,0,sizeof(struct NR_MIB)); + memcpy(mib->message.choice.mib, mib_DU->message.choice.mib, sizeof(struct NR_MIB)); + + rrc->carrier.SIB1 = malloc(f1_setup_req->sib1_length[i]); + rrc->carrier.sizeof_SIB1 = f1_setup_req->sib1_length[i]; + memcpy((void *)rrc->carrier.SIB1,f1_setup_req->sib1[i],f1_setup_req->sib1_length[i]); + dec_rval = uper_decode_complete(NULL, + &asn_DEF_NR_BCCH_DL_SCH_Message, + (void **)&rrc->carrier.siblock1_DU, + f1_setup_req->sib1[i], + f1_setup_req->sib1_length[i]); + AssertFatal(dec_rval.code == RC_OK, + "[gNB_DU %"PRIu8"] Failed to decode NR_BCCH_DLSCH_MESSAGE (%zu bits)\n", + j, + dec_rval.consumed ); + + // Parse message and extract SystemInformationBlockType1 field + NR_BCCH_DL_SCH_Message_t *bcch_message = rrc->carrier.siblock1_DU; + AssertFatal(bcch_message->message.present == NR_BCCH_DL_SCH_MessageType_PR_c1, + "bcch_message->message.present != NR_BCCH_DL_SCH_MessageType_PR_c1\n"); + AssertFatal(bcch_message->message.choice.c1->present == NR_BCCH_DL_SCH_MessageType__c1_PR_systemInformationBlockType1, + "bcch_message->message.choice.c1->present != NR_BCCH_DL_SCH_MessageType__c1_PR_systemInformationBlockType1\n"); + rrc->carrier.sib1 = bcch_message->message.choice.c1->choice.systemInformationBlockType1; + rrc->carrier.physCellId = f1_setup_req->nr_pci[i]; + if (cu_cell_ind == 0) { + // prepare F1_SETUP_RESPONSE + GNB_CU_CONFIGURATION_UPDATE + if (msg_p == NULL) { + msg_p = itti_alloc_new_message (TASK_RRC_GNB, 0, F1AP_SETUP_RESP); + } + if (msg_p2 == NULL) { + msg_p2 = itti_alloc_new_message (TASK_RRC_GNB, 0, F1AP_GNB_CU_CONFIGURATION_UPDATE); + } + + F1AP_SETUP_RESP (msg_p).gNB_CU_name = rrc->node_name; + F1AP_SETUP_RESP (msg_p).num_cells_to_activate = 0; + + } + + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).gNB_CU_name = rrc->node_name; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].mcc = rrc->configuration.mcc[0]; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].mnc = rrc->configuration.mnc[0]; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].mnc_digit_length = rrc->configuration.mnc_digit_length[0]; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].nr_cellid = rrc->nr_cellid; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].nrpci = f1_setup_req->nr_pci[i]; + int num_SI= 0; + + if (rrc->carrier.SIB23) { + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].SI_container[2] = rrc->carrier.SIB23; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].SI_container_length[2] = rrc->carrier.sizeof_SIB23; + num_SI++; + } + + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).cells_to_activate[cu_cell_ind].num_SI = num_SI; + cu_cell_ind++; + found_cell=1; + F1AP_GNB_CU_CONFIGURATION_UPDATE (msg_p2).num_cells_to_activate = cu_cell_ind; + // send + break; + } else {// setup_req mcc/mnc match rrc internal list element + LOG_W(NR_RRC,"[Inst %d] No matching MCC/MNC: rrc->mcc/f1_setup_req->mcc %d/%d rrc->mnc/f1_setup_req->mnc %d/%d rrc->nr_cellid/f1_setup_req->nr_cellid %ld/%ld \n", + j, rrc->configuration.mcc[0], f1_setup_req->mcc[i], + rrc->configuration.mnc[0], f1_setup_req->mnc[i], + rrc->nr_cellid, f1_setup_req->nr_cellid[i]); + } + }// for (int j=0;jnum_cells_available;i++) +} + void rrc_gNB_process_release_request(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_release_request_t *m) { gNB_RRC_INST *rrc = RC.nrrrc[gnb_mod_idP]; @@ -2284,7 +2790,18 @@ void *rrc_gnb_task(void *args_p) { int result; //SRB_INFO *srb_info_p; //int CC_id; - protocol_ctxt_t ctxt; + + protocol_ctxt_t ctxt={.module_id=0, + .enb_flag=1, + .instance=0, + .rnti=0, + .frame=-1, + .subframe=-1, + .eNB_index=0, + .configured=true, + .brOption=false + }; + itti_mark_task_ready(TASK_RRC_GNB); LOG_I(NR_RRC,"Entering main loop of NR_RRC message task\n"); @@ -2314,17 +2831,18 @@ void *rrc_gnb_task(void *args_p) { /* Messages from MAC */ case NR_RRC_MAC_CCCH_DATA_IND: - PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, - NR_RRC_MAC_CCCH_DATA_IND(msg_p).gnb_index, - GNB_FLAG_YES, - NR_RRC_MAC_CCCH_DATA_IND(msg_p).rnti, - msg_p->ittiMsgHeader.lte_time.frame, - msg_p->ittiMsgHeader.lte_time.slot); - LOG_I(NR_RRC,"Decoding CCCH : inst %ld, CC_id %d, ctxt %p, sib_info_p->Rx_buffer.payload_size %d\n", - instance, - NR_RRC_MAC_CCCH_DATA_IND(msg_p).CC_id, - &ctxt, - NR_RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size); + PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, + NR_RRC_MAC_CCCH_DATA_IND(msg_p).gnb_index, + GNB_FLAG_YES, + NR_RRC_MAC_CCCH_DATA_IND(msg_p).rnti, + msg_p->ittiMsgHeader.lte_time.frame, + msg_p->ittiMsgHeader.lte_time.slot); + LOG_I(NR_RRC,"Decoding CCCH : ue %d, inst %ld, CC_id %d, ctxt %p, sib_info_p->Rx_buffer.payload_size %d\n", + ctxt.rnti, + instance, + NR_RRC_MAC_CCCH_DATA_IND(msg_p).CC_id, + &ctxt, + NR_RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size); if (NR_RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size >= CCCH_SDU_SIZE) { LOG_I(NR_RRC, "CCCH message has size %d > %d\n", @@ -2333,9 +2851,16 @@ void *rrc_gnb_task(void *args_p) { } nr_rrc_gNB_decode_ccch(&ctxt, - (uint8_t *)NR_RRC_MAC_CCCH_DATA_IND(msg_p).sdu, - NR_RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size, - NR_RRC_MAC_CCCH_DATA_IND(msg_p).CC_id); + (uint8_t *)NR_RRC_MAC_CCCH_DATA_IND(msg_p).sdu, + NR_RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size, + NR_RRC_MAC_CCCH_DATA_IND(msg_p).du_to_cu_rrc_container, + NR_RRC_MAC_CCCH_DATA_IND(msg_p).CC_id); + + if (NR_RRC_MAC_CCCH_DATA_IND(msg_p).du_to_cu_rrc_container) { + free(NR_RRC_MAC_CCCH_DATA_IND(msg_p).du_to_cu_rrc_container->buf); + free(NR_RRC_MAC_CCCH_DATA_IND(msg_p).du_to_cu_rrc_container); + } + break; /* Messages from PDCP */ @@ -2346,6 +2871,11 @@ void *rrc_gnb_task(void *args_p) { NR_RRC_DCCH_DATA_IND(msg_p).rnti, msg_p->ittiMsgHeader.lte_time.frame, msg_p->ittiMsgHeader.lte_time.slot); + LOG_I(NR_RRC,"Decoding DCCH : ue %d, inst %ld, ctxt %p, size %d\n", + ctxt.rnti, + instance, + &ctxt, + NR_RRC_DCCH_DATA_IND(msg_p).sdu_size); LOG_D(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" Received on DCCH %d %s\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(&ctxt), NR_RRC_DCCH_DATA_IND(msg_p).dcch_index, @@ -2354,6 +2884,8 @@ void *rrc_gnb_task(void *args_p) { NR_RRC_DCCH_DATA_IND(msg_p).dcch_index, NR_RRC_DCCH_DATA_IND(msg_p).sdu_p, NR_RRC_DCCH_DATA_IND(msg_p).sdu_size); + result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), NR_RRC_DCCH_DATA_IND(msg_p).sdu_p); + break; case NGAP_DOWNLINK_NAS: @@ -2417,11 +2949,7 @@ void *rrc_gnb_task(void *args_p) { ///Nothing to do. Apparently everything is done in S1AP processing //LOG_I(RRC, "[eNB %d] Received message %s, not processed because procedure not synched\n", //instance, msg_name_p); - if (rrc_eNB_get_ue_context(RC.nrrrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti) - && rrc_eNB_get_ue_context(RC.nrrrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_rrc > 0) { - rrc_eNB_get_ue_context(RC.nrrrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_rrc = - rrc_eNB_get_ue_context(RC.nrrrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_thres_rrc; - } + AssertFatal(false, "Removed double mechanism for same feature: now delete_tunnel() function should be called\n"); break; #endif @@ -2432,6 +2960,14 @@ void *rrc_gnb_task(void *args_p) { openair_rrc_gNB_configuration(GNB_INSTANCE_TO_MODULE_ID(instance), &NRRRC_CONFIGURATION_REQ(msg_p)); break; + /* Messages from F1AP task */ + case F1AP_SETUP_REQ: + AssertFatal(NODE_IS_CU(RC.nrrrc[instance]->node_type), + "should not receive F1AP_SETUP_REQUEST, need call by CU!\n"); + LOG_I(NR_RRC,"[gNB %ld] Received %s : %p\n", instance, msg_name_p, &F1AP_SETUP_REQ(msg_p)); + rrc_gNB_process_f1_setup_req(&F1AP_SETUP_REQ(msg_p)); + break; + /* Messages from X2AP */ case X2AP_ENDC_SGNB_ADDITION_REQ: LOG_I(NR_RRC, "Received ENDC sgNB addition request from X2AP \n"); @@ -2483,12 +3019,13 @@ void *rrc_gnb_task(void *args_p) { void rrc_gNB_generate_SecurityModeCommand( const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *const ue_context_pP + rrc_gNB_ue_context_t *const ue_context_pP ) //----------------------------------------------------------------------------- { uint8_t buffer[100]; uint8_t size; + T(T_ENB_RRC_SECURITY_MODE_COMMAND, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame), T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti)); NR_IntegrityProtAlgorithm_t integrity_algorithm = (NR_IntegrityProtAlgorithm_t)ue_context_pP->ue_context.integrity_algorithm; @@ -2503,24 +3040,45 @@ rrc_gNB_generate_SecurityModeCommand( PROTOCOL_NR_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate SecurityModeCommand (bytes %d)\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), size); - LOG_D(NR_RRC, + + switch (RC.nrrrc[ctxt_pP->module_id]->node_type) { + case ngran_gNB_CU: + // create an ITTI message + memcpy(ue_context_pP->ue_context.Srb1.Srb_info.Tx_buffer.Payload, buffer, size); + ue_context_pP->ue_context.Srb1.Srb_info.Tx_buffer.payload_size = size; + + LOG_I(NR_RRC,"calling rrc_data_req :securityModeCommand\n"); + nr_rrc_data_req(ctxt_pP, + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + break; + + case ngran_gNB_DU: + // nothing to do for DU + AssertFatal(1==0,"nothing to do for DU\n"); + break; + + case ngran_gNB: + LOG_D(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" --- PDCP_DATA_REQ/%d Bytes (securityModeCommand to UE MUI %d) --->[PDCP][RB %02d]\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), size, rrc_gNB_mui, DCCH); - MSC_LOG_TX_MESSAGE( - MSC_RRC_GNB, - MSC_RRC_UE, - buffer, - size, - MSC_AS_TIME_FMT" securityModeCommand UE %x MUI %d size %u", - MSC_AS_TIME_ARGS(ctxt_pP), - ue_context_pP->ue_context.rnti, - rrc_gNB_mui, - size); - - LOG_I(NR_RRC,"calling rrc_data_req :securityModeCommand\n"); + MSC_LOG_TX_MESSAGE( + MSC_RRC_GNB, + MSC_RRC_UE, + buffer, + size, + MSC_AS_TIME_FMT" securityModeCommand UE %x MUI %d size %u", + MSC_AS_TIME_ARGS(ctxt_pP), + ue_context_pP->ue_context.rnti, + rrc_gNB_mui, + size); #ifdef ITTI_SIM MessageDef *message_p; uint8_t *message_buffer; @@ -2532,15 +3090,20 @@ rrc_gNB_generate_SecurityModeCommand( GNB_RRC_DCCH_DATA_IND (message_p).size = size; itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); #else + LOG_D(NR_RRC,"calling rrc_data_req :securityModeCommand\n"); nr_rrc_data_req(ctxt_pP, - DCCH, - rrc_gNB_mui++, - SDU_CONFIRM_NO, - size, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); #endif + break; + default : + LOG_W(NR_RRC, "Unknown node type %d\n", RC.nrrrc[ctxt_pP->module_id]->node_type); + } } void @@ -2552,32 +3115,52 @@ rrc_gNB_generate_UECapabilityEnquiry( { uint8_t buffer[100]; uint8_t size; + T(T_ENB_RRC_UE_CAPABILITY_ENQUIRY, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame), T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti)); size = do_NR_SA_UECapabilityEnquiry( ctxt_pP, buffer, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id)); - LOG_I(RRC, - PROTOCOL_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate NR UECapabilityEnquiry (bytes %d)\n", - PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP), + LOG_I(NR_RRC, + PROTOCOL_NR_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate NR UECapabilityEnquiry (bytes %d)\n", + PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), size); - LOG_D(RRC, - PROTOCOL_RRC_CTXT_UE_FMT" --- PDCP_DATA_REQ/%d Bytes (NR UECapabilityEnquiry MUI %d) --->[PDCP][RB %02d]\n", - PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP), + switch (RC.nrrrc[ctxt_pP->module_id]->node_type) { + case ngran_gNB_CU: + nr_rrc_data_req( + ctxt_pP, + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + break; + + case ngran_gNB_DU: + // nothing to do for DU + AssertFatal(1==0,"nothing to do for DU\n"); + break; + + case ngran_gNB: + // rrc_mac_config_req_gNB + LOG_D(NR_RRC, + PROTOCOL_NR_RRC_CTXT_UE_FMT" --- PDCP_DATA_REQ/%d Bytes (NR UECapabilityEnquiry MUI %d) --->[PDCP][RB %02d]\n", + PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), size, rrc_gNB_mui, DCCH); - MSC_LOG_TX_MESSAGE( - MSC_RRC_GNB, - MSC_RRC_UE, - buffer, - size, - MSC_AS_TIME_FMT" rrcNRUECapabilityEnquiry UE %x MUI %d size %u", - MSC_AS_TIME_ARGS(ctxt_pP), - ue_context_pP->ue_context.rnti, - rrc_gNB_mui, - size); + MSC_LOG_TX_MESSAGE( + MSC_RRC_GNB, + MSC_RRC_UE, + buffer, + size, + MSC_AS_TIME_FMT" rrcNRUECapabilityEnquiry UE %x MUI %d size %u", + MSC_AS_TIME_ARGS(ctxt_pP), + ue_context_pP->ue_context.rnti, + rrc_gNB_mui, + size); #ifdef ITTI_SIM MessageDef *message_p; uint8_t *message_buffer; @@ -2589,15 +3172,19 @@ rrc_gNB_generate_UECapabilityEnquiry( GNB_RRC_DCCH_DATA_IND (message_p).size = size; itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); #else - nr_rrc_data_req( - ctxt_pP, - DCCH, - rrc_gNB_mui++, - SDU_CONFIRM_NO, - size, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); + nr_rrc_data_req(ctxt_pP, + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); #endif + break; + + default : + LOG_W(NR_RRC, "Unknown node type %d\n", RC.nrrrc[ctxt_pP->module_id]->node_type); + } } //----------------------------------------------------------------------------- @@ -2620,6 +3207,8 @@ rrc_gNB_generate_RRCRelease( size = do_NR_RRCRelease(buffer,rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id)); ue_context_pP->ue_context.ue_reestablishment_timer = 0; ue_context_pP->ue_context.ue_release_timer = 0; + ue_context_pP->ue_context.ul_failure_timer = 0; + ue_context_pP->ue_context.ue_release_timer_rrc = 0; LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate RRCRelease (bytes %d)\n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), @@ -2652,7 +3241,7 @@ rrc_gNB_generate_RRCRelease( GNB_RRC_DCCH_DATA_IND (message_p).size = size; itti_send_msg_to_task (TASK_RRC_UE_SIM, ctxt_pP->instance, message_p); #else - if (NODE_IS_CU(RC.rrc[ctxt_pP->module_id]->node_type)) { + if (NODE_IS_CU(RC.nrrrc[ctxt_pP->module_id]->node_type)) { MessageDef *m = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_UE_CONTEXT_RELEASE_CMD); F1AP_UE_CONTEXT_RELEASE_CMD(m).rnti = ctxt_pP->rnti; F1AP_UE_CONTEXT_RELEASE_CMD(m).cause = F1AP_CAUSE_RADIO_NETWORK; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 5bb44205c4..619880da59 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -57,6 +57,7 @@ #include "RRC/NR/MESSAGES/asn1_msg.h" #include "NR_UERadioAccessCapabilityInformation.h" #include "NR_UE-CapabilityRAT-ContainerList.h" +#include "f1ap_messages_types.h" extern RAN_CONTEXT_t RC; @@ -303,25 +304,44 @@ nr_rrc_pdcp_config_security( uint8_t *kRRCenc = NULL; uint8_t *kRRCint = NULL; uint8_t *kUPenc = NULL; - pdcp_t *pdcp_p = NULL; - static int print_keys= 1; - hashtable_rc_t h_rc; - hash_key_t key; + static int print_keys= 1; #ifndef PHYSIM - /* Derive the keys from kgnb */ - if (SRB_configList != NULL) { - nr_derive_key_up_enc(ue_context_pP->ue_context.ciphering_algorithm, - ue_context_pP->ue_context.kgnb, - &kUPenc); - } - nr_derive_key_rrc_enc(ue_context_pP->ue_context.ciphering_algorithm, - ue_context_pP->ue_context.kgnb, - &kRRCenc); - nr_derive_key_rrc_int(ue_context_pP->ue_context.integrity_algorithm, - ue_context_pP->ue_context.kgnb, - &kRRCint); + uint8_t *k_kdf = NULL; + + /* Derive the keys from kgnb */ + if (SRB_configList != NULL) { + k_kdf = NULL; + nr_derive_key_up_enc(ue_context_pP->ue_context.ciphering_algorithm, + ue_context_pP->ue_context.kgnb, + &k_kdf); + /* kUPenc: last 128 bits of key derivation function which returns 256 bits */ + kUPenc = malloc(16); + if (kUPenc == NULL) exit(1); + memcpy(kUPenc, k_kdf+16, 16); + free(k_kdf); + } + + k_kdf = NULL; + nr_derive_key_rrc_enc(ue_context_pP->ue_context.ciphering_algorithm, + ue_context_pP->ue_context.kgnb, + &k_kdf); + /* kRRCenc: last 128 bits of key derivation function which returns 256 bits */ + kRRCenc = malloc(16); + if (kRRCenc == NULL) exit(1); + memcpy(kRRCenc, k_kdf+16, 16); + free(k_kdf); + + k_kdf = NULL; + nr_derive_key_rrc_int(ue_context_pP->ue_context.integrity_algorithm, + ue_context_pP->ue_context.kgnb, + &k_kdf); + /* kRRCint: last 128 bits of key derivation function which returns 256 bits */ + kRRCint = malloc(16); + if (kRRCint == NULL) exit(1); + memcpy(kRRCint, k_kdf+16, 16); + free(k_kdf); #endif if (!IS_SOFTMODEM_IQPLAYER) { SET_LOG_DUMP(DEBUG_SECURITY) ; @@ -337,28 +357,18 @@ nr_rrc_pdcp_config_security( } } - key = PDCP_COLL_KEY_VALUE(ctxt_pP->module_id, ctxt_pP->rnti, ctxt_pP->enb_flag, DCCH, SRB_FLAG_YES); - h_rc = hashtable_get(pdcp_coll_p, key, (void **)&pdcp_p); - - if (h_rc == HASH_TABLE_OK) { - pdcp_config_set_security( - ctxt_pP, - pdcp_p, - DCCH, - DCCH+2, - (send_security_mode_command == TRUE) ? - 0 | (ue_context_pP->ue_context.integrity_algorithm << 4) : - (ue_context_pP->ue_context.ciphering_algorithm ) | - (ue_context_pP->ue_context.integrity_algorithm << 4), - kRRCenc, - kRRCint, - kUPenc); - } else { - LOG_E(NR_RRC, - PROTOCOL_NR_RRC_CTXT_UE_FMT"Could not get PDCP instance for SRB DCCH %u\n", - PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), - DCCH); - } + pdcp_config_set_security( + ctxt_pP, + NULL, /* pdcp_pP not used anymore in NR */ + DCCH, + DCCH+2, + (send_security_mode_command == TRUE) ? + 0 | (ue_context_pP->ue_context.integrity_algorithm << 4) : + (ue_context_pP->ue_context.ciphering_algorithm ) | + (ue_context_pP->ue_context.integrity_algorithm << 4), + kRRCenc, + kRRCint, + kUPenc); } //------------------------------------------------------------------------------ @@ -424,57 +434,57 @@ rrc_gNB_send_NGAP_NAS_FIRST_REQ( NGAP_NAS_FIRST_REQ(message_p).selected_plmn_identity = selected_plmn_identity; if (rrcSetupComplete->registeredAMF != NULL) { - NR_RegisteredAMF_t *r_amf = rrcSetupComplete->registeredAMF; - NGAP_NAS_FIRST_REQ(message_p).ue_identity.presenceMask |= NGAP_UE_IDENTITIES_guami; + NR_RegisteredAMF_t *r_amf = rrcSetupComplete->registeredAMF; + NGAP_NAS_FIRST_REQ(message_p).ue_identity.presenceMask |= NGAP_UE_IDENTITIES_guami; - if (r_amf->plmn_Identity != NULL) { - if ((r_amf->plmn_Identity->mcc != NULL) && (r_amf->plmn_Identity->mcc->list.count > 0)) { - /* Use first indicated PLMN MCC if it is defined */ - NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mcc = *r_amf->plmn_Identity->mcc->list.array[selected_plmn_identity]; - LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MCC %u ue %x\n", - ctxt_pP->module_id, - NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.mcc, - ue_context_pP->ue_context.rnti); + if (r_amf->plmn_Identity != NULL) { + if ((r_amf->plmn_Identity->mcc != NULL) && (r_amf->plmn_Identity->mcc->list.count > 0)) { + /* Use first indicated PLMN MCC if it is defined */ + NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mcc = *r_amf->plmn_Identity->mcc->list.array[selected_plmn_identity]; + LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MCC %u ue %x\n", + ctxt_pP->module_id, + NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.mcc, + ue_context_pP->ue_context.rnti); + } + + if (r_amf->plmn_Identity->mnc.list.count > 0) { + /* Use first indicated PLMN MNC if it is defined */ + NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mnc = *r_amf->plmn_Identity->mnc.list.array[selected_plmn_identity]; + LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MNC %u ue %x\n", + ctxt_pP->module_id, + NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.mnc, + ue_context_pP->ue_context.rnti); + } + } else { + /* TODO */ } - if (r_amf->plmn_Identity->mnc.list.count > 0) { - /* Use first indicated PLMN MNC if it is defined */ - NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mnc = *r_amf->plmn_Identity->mnc.list.array[selected_plmn_identity]; - LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MNC %u ue %x\n", + /* amf_Identifier */ + uint32_t amf_Id = BIT_STRING_to_uint32(&r_amf->amf_Identifier); + NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_region_id = amf_Id >> 16; + NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_set_id = ue_context_pP->ue_context.Initialue_identity_5g_s_TMSI.amf_set_id; + NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_pointer = ue_context_pP->ue_context.Initialue_identity_5g_s_TMSI.amf_pointer; + + ue_context_pP->ue_context.ue_guami.mcc = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mcc; + ue_context_pP->ue_context.ue_guami.mnc = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mnc; + ue_context_pP->ue_context.ue_guami.mnc_len = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mnc_len; + ue_context_pP->ue_context.ue_guami.amf_region_id = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_region_id; + ue_context_pP->ue_context.ue_guami.amf_set_id = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_set_id; + ue_context_pP->ue_context.ue_guami.amf_pointer = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_pointer; + + MSC_LOG_TX_MESSAGE(MSC_NGAP_GNB, + MSC_NGAP_AMF, + (const char *)&message_p->ittiMsg.ngap_nas_first_req, + sizeof(ngap_nas_first_req_t), + MSC_AS_TIME_FMT" NGAP_NAS_FIRST_REQ gNB %u UE %x", + MSC_AS_TIME_ARGS(ctxt_pP), + ctxt_pP->module_id, + ctxt_pP->rnti); + LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUAMI amf_set_id %u amf_region_id %u ue %x\n", ctxt_pP->module_id, - NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.mnc, + NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.amf_set_id, + NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.amf_region_id, ue_context_pP->ue_context.rnti); - } - } else { - /* TODO */ - } - - /* amf_Identifier */ - uint32_t amf_Id = BIT_STRING_to_uint32(&r_amf->amf_Identifier); - NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_region_id = amf_Id >> 16; - NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_set_id = ue_context_pP->ue_context.Initialue_identity_5g_s_TMSI.amf_set_id; - NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_pointer = ue_context_pP->ue_context.Initialue_identity_5g_s_TMSI.amf_pointer; - - ue_context_pP->ue_context.ue_guami.mcc = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mcc; - ue_context_pP->ue_context.ue_guami.mnc = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mnc; - ue_context_pP->ue_context.ue_guami.mnc_len = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.mnc_len; - ue_context_pP->ue_context.ue_guami.amf_region_id = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_region_id; - ue_context_pP->ue_context.ue_guami.amf_set_id = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_set_id; - ue_context_pP->ue_context.ue_guami.amf_pointer = NGAP_NAS_FIRST_REQ(message_p).ue_identity.guami.amf_pointer; - - MSC_LOG_TX_MESSAGE(MSC_NGAP_GNB, - MSC_NGAP_AMF, - (const char *)&message_p->ittiMsg.ngap_nas_first_req, - sizeof(ngap_nas_first_req_t), - MSC_AS_TIME_FMT" NGAP_NAS_FIRST_REQ gNB %u UE %x", - MSC_AS_TIME_ARGS(ctxt_pP), - ctxt_pP->module_id, - ctxt_pP->rnti); - LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUAMI amf_set_id %u amf_region_id %u ue %x\n", - ctxt_pP->module_id, - NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.amf_set_id, - NGAP_NAS_FIRST_REQ (message_p).ue_identity.guami.amf_region_id, - ue_context_pP->ue_context.rnti); } itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, message_p); @@ -569,7 +579,7 @@ rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ( } // in case, send the S1SP initial context response if it is not sent with the attach complete message - if (ue_context_p->ue_context.status == NR_RRC_RECONFIGURED) { + if (ue_context_p->ue_context.StatusRrc == NR_RRC_RECONFIGURED) { LOG_I(NR_RRC, "Sending rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP, cause %ld\n", ue_context_p->ue_context.reestablishment_cause); rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(&ctxt,ue_context_p); } @@ -626,6 +636,10 @@ rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP( } static NR_CipheringAlgorithm_t rrc_gNB_select_ciphering(uint16_t algorithms) { + + return NR_CipheringAlgorithm_nea0; + + if (algorithms & NGAP_ENCRYPTION_NEA3_MASK) { return NR_CipheringAlgorithm_nea3; } @@ -638,10 +652,13 @@ static NR_CipheringAlgorithm_t rrc_gNB_select_ciphering(uint16_t algorithms) { return NR_CipheringAlgorithm_nea1; } - return NR_CipheringAlgorithm_nea0; } static e_NR_IntegrityProtAlgorithm rrc_gNB_select_integrity(uint16_t algorithms) { + + //only NIA2 supported for now + return NR_IntegrityProtAlgorithm_nia2; + if (algorithms & NGAP_INTEGRITY_NIA3_MASK) { return NR_IntegrityProtAlgorithm_nia3; } @@ -784,28 +801,54 @@ rrc_gNB_process_NGAP_DOWNLINK_NAS( /* * switch UL or DL NAS message without RRC piggybacked to SRB2 if active. */ + switch (RC.nrrrc[ctxt.module_id]->node_type) { + case ngran_gNB_CU: + /* Transfer data to PDCP */ + nr_rrc_data_req ( + &ctxt, + ue_context_p->ue_context.Srb2.Active == 1 ? ue_context_p->ue_context.Srb2.Srb_info.Srb_id : ue_context_p->ue_context.Srb1.Srb_info.Srb_id, + (*rrc_gNB_mui)++, + SDU_CONFIRM_NO, + length, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + break; + + case ngran_gNB_DU: + // nothing to do for DU + AssertFatal(1==0,"nothing to do for DU\n"); + break; + + case ngran_gNB: + { + // rrc_mac_config_req_gNB #ifdef ITTI_SIM - MessageDef *message_p; uint8_t *message_buffer; message_buffer = itti_malloc (TASK_RRC_GNB, TASK_RRC_UE_SIM, length); memcpy (message_buffer, buffer, length); - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_DCCH_DATA_IND); + MessageDef *message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, GNB_RRC_DCCH_DATA_IND); GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; GNB_RRC_DCCH_DATA_IND (message_p).size = length; itti_send_msg_to_task (TASK_RRC_UE_SIM, instance, message_p); LOG_I(NR_RRC, "Send DL NAS message \n"); #else - /* Transfer data to PDCP */ - nr_rrc_data_req ( - &ctxt, - ue_context_p->ue_context.Srb2.Srb_info.Srb_id, - (*rrc_gNB_mui)++, - SDU_CONFIRM_NO, - length, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); + /* Transfer data to PDCP */ + nr_rrc_data_req ( + &ctxt, + ue_context_p->ue_context.Srb2.Active == 1 ? ue_context_p->ue_context.Srb2.Srb_info.Srb_id : ue_context_p->ue_context.Srb1.Srb_info.Srb_id, + (*rrc_gNB_mui)++, + SDU_CONFIRM_NO, + length, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); #endif + } + break; + + default : + LOG_W(NR_RRC, "Unknown node type %d\n", RC.nrrrc[ctxt.module_id]->node_type); + } return (0); } } @@ -1015,8 +1058,9 @@ rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ( ue_context_p->ue_context.setup_pdu_sessions += nb_pdusessions_tosetup; // TEST - ue_context_p->ue_context.pdusession[0].status = PDU_SESSION_STATUS_DONE; - rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(&ctxt, ue_context_p, 0); + // ue_context_p->ue_context.pdusession[0].status = PDU_SESSION_STATUS_DONE; + // rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(&ctxt, ue_context_p, 0); + rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p); return(0); } } diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 7861f2166c..f8cc951bc3 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -346,16 +346,25 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ rrc->Nb_ue++; // configure MAC and RLC - rrc_mac_config_req_gNB(rrc->module_id, - rrc->carrier.ssb_SubcarrierOffset, - rrc->carrier.pdsch_AntennaPorts, - rrc->carrier.pusch_AntennaPorts, - rrc->carrier.pusch_TargetSNRx10, - rrc->carrier.pucch_TargetSNRx10, - NULL, - 1, // add_ue flag - ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup); + if (NODE_IS_DU(rrc->node_type)) { + rrc_mac_config_req_gNB(rrc->module_id, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, + rrc->carrier.servingcellconfigcommon, + 1, // add_ue flag + ue_context_p->ue_id_rnti, + ue_context_p->ue_context.secondaryCellGroup); + } else { + rrc_mac_config_req_gNB(rrc->module_id, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, + NULL, + 1, // add_ue flag + ue_context_p->ue_id_rnti, + ue_context_p->ue_context.secondaryCellGroup); + } if(m == NULL){ LOG_W(RRC, "Calling RRC PDCP/RLC ASN1 request functions for protocol context %p with module_id %d, rnti %x, frame %d, subframe %d eNB_index %d \n", &ctxt, @@ -366,26 +375,25 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ ctxt.eNB_index); } - nr_rrc_pdcp_config_asn1_req( - &ctxt, - (NR_SRB_ToAddModList_t *) NULL, - ue_context_p->ue_context.rb_config->drb_ToAddModList , - ue_context_p->ue_context.rb_config->drb_ToReleaseList, - (ue_context_p->ue_context.integrity_algorithm << 4) | ue_context_p->ue_context.ciphering_algorithm, - NULL, /* kRRCenc - unused */ - NULL, /* kRRCint - unused */ - kUPenc, /* kUPenc */ - NULL, /* kUPint - unused */ - NULL, - NULL, - ue_context_p->ue_context.secondaryCellGroup->rlc_BearerToAddModList); + nr_rrc_pdcp_config_asn1_req(&ctxt, + get_softmodem_params()->sa ? ue_context_p->ue_context.rb_config->srb_ToAddModList : (NR_SRB_ToAddModList_t *) NULL, + ue_context_p->ue_context.rb_config->drb_ToAddModList , + ue_context_p->ue_context.rb_config->drb_ToReleaseList, + (ue_context_p->ue_context.integrity_algorithm << 4) | ue_context_p->ue_context.ciphering_algorithm, + NULL, /* kRRCenc - unused */ + NULL, /* kRRCint - unused */ + kUPenc, /* kUPenc */ + NULL, /* kUPint - unused */ + NULL, + NULL, + ue_context_p->ue_context.secondaryCellGroup->rlc_BearerToAddModList); nr_rrc_rlc_config_asn1_req (&ctxt, - (NR_SRB_ToAddModList_t *) NULL, - ue_context_p->ue_context.rb_config->drb_ToAddModList, - ue_context_p->ue_context.rb_config->drb_ToReleaseList, - (LTE_PMCH_InfoList_r9_t *) NULL, - ue_context_p->ue_context.secondaryCellGroup->rlc_BearerToAddModList); + get_softmodem_params()->sa ? ue_context_p->ue_context.rb_config->srb_ToAddModList : (NR_SRB_ToAddModList_t *) NULL, + ue_context_p->ue_context.rb_config->drb_ToAddModList, + ue_context_p->ue_context.rb_config->drb_ToReleaseList, + (LTE_PMCH_InfoList_r9_t *) NULL, + ue_context_p->ue_context.secondaryCellGroup->rlc_BearerToAddModList); LOG_D(RRC, "%s:%d: done RRC PDCP/RLC ASN1 request for UE rnti %x\n", __FUNCTION__, __LINE__, ctxt.rnti); @@ -394,7 +402,6 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ void rrc_remove_nsa_user(gNB_RRC_INST *rrc, int rnti) { protocol_ctxt_t ctxt; rrc_gNB_ue_context_t *ue_context; - MessageDef *msg_delete_tunnels_p; int e_rab; LOG_D(RRC, "calling rrc_remove_nsa_user rnti %d\n", rnti); @@ -411,25 +418,18 @@ void rrc_remove_nsa_user(gNB_RRC_INST *rrc, int rnti) { rrc_rlc_remove_ue(&ctxt); mac_remove_nr_ue(rrc->module_id, rnti); - - /* delete gtp tunnel */ - msg_delete_tunnels_p = itti_alloc_new_message(TASK_RRC_GNB, 0, GTPV1U_ENB_DELETE_TUNNEL_REQ); - memset(>PV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p), 0, sizeof(GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p))); - GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p).rnti = rnti; - GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p).from_gnb = 1; - - LOG_D(RRC, "ue_context->ue_context.nb_of_e_rabs %d\n", ue_context->ue_context.nb_of_e_rabs); + gtpv1u_enb_delete_tunnel_req_t tmp={0}; + tmp.rnti=rnti; + tmp.from_gnb=1; + LOG_D(RRC, "ue_context->ue_context.nb_of_e_rabs %d will be deleted for rnti %d\n", ue_context->ue_context.nb_of_e_rabs, rnti); for (e_rab = 0; e_rab < ue_context->ue_context.nb_of_e_rabs; e_rab++) { - GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p).eps_bearer_id[GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p).num_erab++] = - ue_context->ue_context.gnb_gtp_ebi[e_rab]; + tmp.eps_bearer_id[tmp.num_erab++]= ue_context->ue_context.gnb_gtp_ebi[e_rab]; // erase data ue_context->ue_context.gnb_gtp_teid[e_rab] = 0; memset(&ue_context->ue_context.gnb_gtp_addrs[e_rab], 0, sizeof(ue_context->ue_context.gnb_gtp_addrs[e_rab])); ue_context->ue_context.gnb_gtp_ebi[e_rab] = 0; } - - itti_send_msg_to_task(TASK_VARIABLE, rrc->module_id, msg_delete_tunnels_p); - + gtpv1u_delete_s1u_tunnel(rrc->module_id, &tmp); /* remove context */ rrc_gNB_remove_ue_context(&ctxt, rrc, ue_context); } diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 72b0fd1d34..3736ed5244 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -50,29 +50,9 @@ #define true 1 void fill_default_initialDownlinkBWP(NR_BWP_Downlink_t *bwp, NR_ServingCellConfigCommon_t *servingcellconfigcommon) { - bwp->bwp_Id = 0; - bwp->bwp_Common=calloc(1,sizeof(*bwp->bwp_Common)); - memcpy((void*)&bwp->bwp_Common, - &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP, - sizeof(bwp->bwp_Common)); - - bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); - bwp->bwp_Dedicated->pdsch_Config = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); - bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID0=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID1=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0; + *bwp->bwp_Common = *servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP; } void fill_default_coresetZero(NR_ControlResourceSet_t *coreset0, NR_ServingCellConfigCommon_t *servingcellconfigcommon) { @@ -174,7 +154,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco memset(secondaryCellGroup,0,sizeof(NR_CellGroupConfig_t)); secondaryCellGroup->cellGroupId = scg_id; NR_RLC_BearerConfig_t *RLC_BearerConfig = calloc(1,sizeof(*RLC_BearerConfig)); - nr_rlc_bearer_init(RLC_BearerConfig); + nr_rlc_bearer_init(RLC_BearerConfig, NR_RLC_BearerConfig__servedRadioBearer_PR_drb_Identity); if (get_softmodem_params()->do_ra || get_softmodem_params()->sa) nr_drb_config(RLC_BearerConfig->rlc_Config, NR_RLC_Config_PR_um_Bi_Directional); else @@ -183,9 +163,9 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->rlc_BearerToAddModList = calloc(1,sizeof(*secondaryCellGroup->rlc_BearerToAddModList)); ASN_SEQUENCE_ADD(&secondaryCellGroup->rlc_BearerToAddModList->list, RLC_BearerConfig); + secondaryCellGroup->mac_CellGroupConfig=calloc(1,sizeof(*secondaryCellGroup->mac_CellGroupConfig)); secondaryCellGroup->mac_CellGroupConfig->drx_Config = NULL; - secondaryCellGroup->mac_CellGroupConfig->schedulingRequestConfig = calloc(1,sizeof(*secondaryCellGroup->mac_CellGroupConfig->schedulingRequestConfig)); secondaryCellGroup->mac_CellGroupConfig->schedulingRequestConfig->schedulingRequestToAddModList = calloc(1,sizeof(*secondaryCellGroup->mac_CellGroupConfig->schedulingRequestConfig->schedulingRequestToAddModList)); NR_SchedulingRequestToAddMod_t *SchedulingRequestConf = calloc(1,sizeof(*SchedulingRequestConf)); @@ -1045,7 +1025,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco long *delay[8]; for (int i=0;i<8;i++) { delay[i] = calloc(1,sizeof(*delay[i])); - *delay[i] = i+2; + *delay[i] = (i<6) ? (i+2) : 0; ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); } pucch_Config->spatialRelationInfoToAddModList = calloc(1,sizeof(*pucch_Config->spatialRelationInfoToAddModList)); diff --git a/openair2/RRC/NR_UE/L2_interface_ue.c b/openair2/RRC/NR_UE/L2_interface_ue.c index c78a1caf37..5c9293dcf9 100644 --- a/openair2/RRC/NR_UE/L2_interface_ue.c +++ b/openair2/RRC/NR_UE/L2_interface_ue.c @@ -162,12 +162,10 @@ rrc_data_req_ue( MessageDef *message_p; // Uses a new buffer to avoid issue with PDCP buffer content that could be changed by PDCP (asynchronous message handling). uint8_t *message_buffer; - message_buffer = itti_malloc ( - ctxt_pP->enb_flag ? TASK_RRC_ENB : TASK_RRC_UE, - ctxt_pP->enb_flag ? TASK_PDCP_ENB : TASK_PDCP_UE, - sdu_sizeP); + message_buffer = itti_malloc (TASK_RRC_UE, TASK_PDCP_UE, sdu_sizeP); + LOG_D(RRC,"Sending RRC message for SRB %ld, sdu_size %d\n",rb_idP, sdu_sizeP); memcpy (message_buffer, buffer_pP, sdu_sizeP); - message_p = itti_alloc_new_message (ctxt_pP->enb_flag ? TASK_RRC_ENB : TASK_RRC_UE, 0, RRC_DCCH_DATA_REQ); + message_p = itti_alloc_new_message ( TASK_RRC_UE, 0, RRC_DCCH_DATA_REQ); RRC_DCCH_DATA_REQ (message_p).frame = ctxt_pP->frame; RRC_DCCH_DATA_REQ (message_p).enb_flag = ctxt_pP->enb_flag; RRC_DCCH_DATA_REQ (message_p).rb_id = rb_idP; diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 86948230a5..cb8eca9504 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -67,8 +67,9 @@ #include "RRC/NAS/nas_config.h" #include "RRC/NAS/rb_config.h" #include "SIMULATION/TOOLS/sim.h" // for taus +#include -#if ITTI_SIM +#if defined(ITTI_SIM) || defined(RFSIM_NAS) #include "nr_nas_msg_sim.h" #endif @@ -128,7 +129,6 @@ nr_rrc_ue_generate_rrcReestablishmentComplete( ); mui_t nr_rrc_mui=0; -uint8_t first_rrcreconfigurationcomplete = 0; static Rrc_State_NR_t nr_rrc_get_state (module_id_t ue_mod_idP) { return NR_UE_rrc_inst[ue_mod_idP].nrRrcState; @@ -228,8 +228,8 @@ int8_t nr_rrc_ue_decode_secondary_cellgroup_config( return -1; } - if(NR_UE_rrc_inst[module_id].cell_group_config == NULL){ - NR_UE_rrc_inst[module_id].cell_group_config = cell_group_config; + if(NR_UE_rrc_inst[module_id].scell_group_config == NULL){ + NR_UE_rrc_inst[module_id].scell_group_config = cell_group_config; nr_rrc_ue_process_scg_config(module_id,cell_group_config); }else{ nr_rrc_ue_process_scg_config(module_id,cell_group_config); @@ -482,7 +482,7 @@ NR_UE_RRC_INST_t* openair_rrc_top_init_ue_nr(char* rrc_config_path){ RRC_LIST_INIT(NR_UE_rrc_inst[nr_ue].CSI_ReportConfig_list, NR_maxNrofCSI_ReportConfigurations); } - if (get_softmodem_params()->phy_test==1 || get_softmodem_params()->do_ra==1 || get_softmodem_params()->sa == 1) { + if (get_softmodem_params()->phy_test==1 || get_softmodem_params()->do_ra==1) { // read in files for RRCReconfiguration and RBconfig FILE *fd; char filename[1024]; @@ -553,45 +553,36 @@ int8_t nr_rrc_ue_decode_NR_BCCH_BCH_Message( const uint8_t gNB_index, uint8_t *const bufferP, const uint8_t buffer_len ){ - int i; NR_BCCH_BCH_Message_t *bcch_message = NULL; - NR_MIB_t *mib = NR_UE_rrc_inst[module_id].mib; - - if(mib != NULL){ - SEQUENCE_free( &asn_DEF_NR_BCCH_BCH_Message, (void *)mib, 1 ); - } - //for(i=0; imessage.choice.mib; + SEQUENCE_free( &asn_DEF_NR_MIB, (void *)NR_UE_rrc_inst[module_id].mib, 1 ); + NR_UE_rrc_inst[module_id].mib = bcch_message->message.choice.mib; //memcpy( (void *)mib, // (void *)&bcch_message->message.choice.mib, // sizeof(NR_MIB_t) ); - - nr_rrc_mac_config_req_ue( 0, 0, 0, mib, NULL); - } - + + nr_rrc_mac_config_req_ue( 0, 0, 0, NR_UE_rrc_inst[module_id].mib, NULL, NULL, NULL); + } + return 0; } @@ -1148,7 +1139,7 @@ int8_t nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message( NR_SIB1_t *sib1 = NR_UE_rrc_inst[module_id].sib1[gNB_index]; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_BCCH, VCD_FUNCTION_IN ); - if (((NR_UE_rrc_inst[module_id].Info[gNB_index].SIStatus&1) == 1) && // SIB1 received + if (((NR_UE_rrc_inst[module_id].Info[gNB_index].SIStatus&1) == 1) && sib1->si_SchedulingInfo &&// SIB1 received (NR_UE_rrc_inst[module_id].Info[gNB_index].SIcnt == sib1->si_SchedulingInfo->schedulingInfoList.list.count)) { // to prevent memory bloating VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_BCCH, VCD_FUNCTION_OUT ); @@ -1186,7 +1177,7 @@ int8_t nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message( if(sib1 != NULL){ SEQUENCE_free(&asn_DEF_NR_SIB1, (void *)sib1, 1 ); } - + NR_UE_rrc_inst[module_id].Info[gNB_index].SIStatus|=1; sib1 = bcch_message->message.choice.c1->choice.systemInformationBlockType1; if (*(int64_t*)sib1 != 1) { NR_UE_rrc_inst[module_id].sib1[gNB_index] = sib1; @@ -1195,14 +1186,20 @@ int8_t nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message( } LOG_I(NR_RRC, "SIB1 decoded\n"); + /// dump_SIB1(); // FIXME: improve condition for the RA trigger // Check for on-demand not broadcasted SI check_requested_SI_List(module_id, NR_UE_rrc_inst[module_id].requested_SI_List, *sib1); - if( nr_rrc_get_state(module_id) == RRC_STATE_IDLE_NR ) { + if( nr_rrc_get_state(module_id) <= RRC_STATE_IDLE_NR ) { NR_UE_rrc_inst[module_id].ra_trigger = INITIAL_ACCESS_FROM_RRC_IDLE; // TODO: remove flag after full RA procedures implemented - get_softmodem_params()->do_ra = 1; + // get_softmodem_params()->do_ra = 1; + LOG_D(PHY,"Setting state to NR_RRC_SI_RECEIVED\n"); + nr_rrc_set_state (module_id, NR_RRC_SI_RECEIVED); } + // take ServingCellConfigCommon and configure L1/L2 + NR_UE_rrc_inst[module_id].servingCellConfigCommonSIB = sib1->servingCellConfigCommon; + nr_rrc_mac_config_req_ue(module_id,0,0,NULL,sib1->servingCellConfigCommon,NULL,NULL); nr_rrc_ue_generate_ra_msg(module_id,gNB_index); } else { LOG_E(NR_RRC, "SIB1 not decoded\n"); @@ -1256,10 +1253,18 @@ nr_rrc_ue_process_masterCellGroup( ) //----------------------------------------------------------------------------- { - NR_CellGroupConfig_t *cellGroupConfig = (NR_CellGroupConfig_t *)masterCellGroup; + NR_CellGroupConfig_t *cellGroupConfig=NULL; + uper_decode(NULL, + &asn_DEF_NR_CellGroupConfig, //might be added prefix later + (void **)&cellGroupConfig, + (uint8_t *)masterCellGroup->buf, + masterCellGroup->size, 0, 0); + xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)cellGroupConfig); + if( cellGroupConfig->spCellConfig != NULL && cellGroupConfig->spCellConfig->reconfigurationWithSync != NULL){ //TODO (perform Reconfiguration with sync according to 5.3.5.5.2) //TODO (resume all suspended radio bearers and resume SCG transmission for all radio bearers, if suspended) + // NSA procedures } if( cellGroupConfig->rlc_BearerToReleaseList != NULL){ @@ -1279,12 +1284,18 @@ nr_rrc_ue_process_masterCellGroup( } if( cellGroupConfig->spCellConfig != NULL){ - if (NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config->spCellConfig) { + if (NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config && + NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config->spCellConfig) { memcpy(NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config->spCellConfig,cellGroupConfig->spCellConfig, sizeof(struct NR_SpCellConfig)); } else { - NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config->spCellConfig = cellGroupConfig->spCellConfig; + if (NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config) + NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config->spCellConfig = cellGroupConfig->spCellConfig; + else + NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config = cellGroupConfig; } + LOG_D(RRC,"Sending CellGroupConfig to MAC\n"); + nr_rrc_mac_config_req_ue(ctxt_pP->module_id,0,0,NULL,NULL,cellGroupConfig,NULL); //TODO (configure the SpCell as specified in 5.3.5.5.7) } @@ -1312,8 +1323,16 @@ static void rrc_ue_generate_RRCSetupComplete( uint8_t size; const char *nas_msg; int nas_msg_length; + NR_UE_MAC_INST_t *mac = get_mac_inst(0); + + if (mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->csi_MeasConfig) + AssertFatal(1==0,"2 > csi_MeasConfig is not null\n"); + if (AMF_MODE_ENABLED) { -#if ITTI_SIM +#if defined(ITTI_SIM) || defined(RFSIM_NAS) as_nas_info_t initialNasMsg; generateRegistrationRequest(&initialNasMsg); nas_msg = (char*)initialNasMsg.data; @@ -1329,18 +1348,22 @@ static void rrc_ue_generate_RRCSetupComplete( size = do_RRCSetupComplete(ctxt_pP->module_id,buffer,Transaction_id,sel_plmn_id,nas_msg_length,nas_msg); LOG_I(NR_RRC,"[UE %d][RAPROC] Frame %d : Logical Channel UL-DCCH (SRB1), Generating RRCSetupComplete (bytes%d, gNB %d)\n", ctxt_pP->module_id,ctxt_pP->frame, size, gNB_index); - LOG_D(RLC, - "[FRAME %05d][RRC_UE][MOD %02d][][--- PDCP_DATA_REQ/%d Bytes (RRCConnectionSetupComplete to gNB %d MUI %d) --->][PDCP][MOD %02d][RB %02d]\n", + LOG_D(NR_RRC, + "[FRAME %05d][RRC_UE][MOD %02d][][--- PDCP_DATA_REQ/%d Bytes (RRCSetupComplete to gNB %d MUI %d) --->][PDCP][MOD %02d][RB %02d]\n", ctxt_pP->frame, ctxt_pP->module_id+NB_RN_INST, size, gNB_index, nr_rrc_mui, ctxt_pP->module_id+NB_eNB_INST, DCCH); + + //for (int i=0;irnti; - rrc_data_req_ue( - ctxt_pP, - DCCH, - nr_rrc_mui++, - SDU_CONFIRM_NO, - size, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); + rrc_data_req_ue(ctxt_pP, + DCCH, + nr_rrc_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + #ifdef ITTI_SIM MessageDef *message_p; uint8_t *message_buffer; @@ -1355,382 +1378,392 @@ static void rrc_ue_generate_RRCSetupComplete( } int8_t nr_rrc_ue_decode_ccch( const protocol_ctxt_t *const ctxt_pP, const NR_SRB_INFO *const Srb_info, const uint8_t gNB_index ){ - NR_DL_CCCH_Message_t *dl_ccch_msg=NULL; - asn_dec_rval_t dec_rval; - int rval=0; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_IN); - // LOG_D(RRC,"[UE %d] Decoding DL-CCCH message (%d bytes), State %d\n",ue_mod_idP,Srb_info->Rx_buffer.payload_size, - // NR_UE_rrc_inst[ue_mod_idP].Info[gNB_index].State); - dec_rval = uper_decode(NULL, - &asn_DEF_NR_DL_CCCH_Message, - (void **)&dl_ccch_msg, - (uint8_t *)Srb_info->Rx_buffer.Payload, - 100,0,0); - if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { - xer_fprint(stdout,&asn_DEF_NR_DL_CCCH_Message,(void *)dl_ccch_msg); - } - - if ((dec_rval.code != RC_OK) && (dec_rval.consumed==0)) { - LOG_E(RRC,"[UE %d] Frame %d : Failed to decode DL-CCCH-Message (%zu bytes)\n",ctxt_pP->module_id,ctxt_pP->frame,dec_rval.consumed); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_OUT); - return -1; - } - - if (dl_ccch_msg->message.present == NR_DL_CCCH_MessageType_PR_c1) { - if (NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].State == NR_RRC_SI_RECEIVED) { - switch (dl_ccch_msg->message.choice.c1->present) { - case NR_DL_CCCH_MessageType__c1_PR_NOTHING: - LOG_I(NR_RRC, "[UE%d] Frame %d : Received PR_NOTHING on DL-CCCH-Message\n", - ctxt_pP->module_id, - ctxt_pP->frame); - rval = 0; - break; - - case NR_DL_CCCH_MessageType__c1_PR_rrcReject: - LOG_I(NR_RRC, - "[UE%d] Frame %d : Logical Channel DL-CCCH (SRB0), Received RRCConnectionReject \n", - ctxt_pP->module_id, - ctxt_pP->frame); - rval = 0; - break; - - case NR_DL_CCCH_MessageType__c1_PR_rrcSetup: - LOG_I(NR_RRC, - "[UE%d][RAPROC] Frame %d : Logical Channel DL-CCCH (SRB0), Received NR_RRCSetup RNTI %x\n", - ctxt_pP->module_id, - ctxt_pP->frame, - ctxt_pP->rnti); + NR_DL_CCCH_Message_t *dl_ccch_msg=NULL; + asn_dec_rval_t dec_rval; + int rval=0; + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_IN); + LOG_D(RRC,"[NR UE%d] Decoding DL-CCCH message (%d bytes), State %d\n",ctxt_pP->module_id,Srb_info->Rx_buffer.payload_size, + NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].State); + dec_rval = uper_decode(NULL, + &asn_DEF_NR_DL_CCCH_Message, + (void **)&dl_ccch_msg, + (uint8_t *)Srb_info->Rx_buffer.Payload, + 100,0,0); - // Get configuration - // Release T300 timer - NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].T300_active = 0; - - nr_rrc_ue_process_masterCellGroup( - ctxt_pP, - gNB_index, - &dl_ccch_msg->message.choice.c1->choice.rrcSetup->criticalExtensions.choice.rrcSetup->masterCellGroup); - nr_sa_rrc_ue_process_radioBearerConfig( - ctxt_pP, - gNB_index, - &dl_ccch_msg->message.choice.c1->choice.rrcSetup->criticalExtensions.choice.rrcSetup->radioBearerConfig); - nr_rrc_set_state (ctxt_pP->module_id, RRC_STATE_CONNECTED); - nr_rrc_set_sub_state (ctxt_pP->module_id, RRC_SUB_STATE_CONNECTED); - NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].rnti = ctxt_pP->rnti; - rrc_ue_generate_RRCSetupComplete( - ctxt_pP, - gNB_index, - dl_ccch_msg->message.choice.c1->choice.rrcSetup->rrc_TransactionIdentifier, - NR_UE_rrc_inst[ctxt_pP->module_id].selected_plmn_identity); - rval = 0; - break; - - default: - LOG_E(NR_RRC, "[UE%d] Frame %d : Unknown message\n", - ctxt_pP->module_id, - ctxt_pP->frame); - rval = -1; - break; - } - } - } - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_OUT); - return rval; -} + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout,&asn_DEF_NR_DL_CCCH_Message,(void *)dl_ccch_msg); + } -// from NR SRB3 -int8_t nr_rrc_ue_decode_NR_DL_DCCH_Message( - const module_id_t module_id, - const uint8_t gNB_index, - const uint8_t *bufferP, - const uint32_t buffer_len ){ - // uper_decode by nr R15 rrc_connection_reconfiguration - - int32_t i; - NR_DL_DCCH_Message_t *nr_dl_dcch_msg = NULL; - MessageDef *msg_p; - - asn_dec_rval_t dec_rval = uper_decode( NULL, - &asn_DEF_NR_DL_DCCH_Message, - (void**)&nr_dl_dcch_msg, - (uint8_t *)bufferP, - buffer_len, 0, 0); - - if ((dec_rval.code != RC_OK) || (dec_rval.consumed == 0)) { - for (i=0; imessage.present){ - case NR_DL_DCCH_MessageType_PR_c1: - switch(nr_dl_dcch_msg->message.choice.c1->present){ - case NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration: - nr_rrc_ue_process_rrcReconfiguration(module_id,nr_dl_dcch_msg->message.choice.c1->choice.rrcReconfiguration); - break; - - case NR_DL_DCCH_MessageType__c1_PR_NOTHING: - case NR_DL_DCCH_MessageType__c1_PR_rrcResume: - case NR_DL_DCCH_MessageType__c1_PR_rrcRelease: - msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_CONN_RELEASE_IND); - if((nr_dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.present == NR_RRCRelease__criticalExtensions_PR_rrcRelease) && - (nr_dl_dcch_msg->message.choice.c1->present == NR_DL_DCCH_MessageType__c1_PR_rrcRelease)){ - nr_dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationTimer = - NR_RRCRelease_IEs__deprioritisationReq__deprioritisationTimer_min5; - nr_dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationType = - NR_RRCRelease_IEs__deprioritisationReq__deprioritisationType_frequency; - } - itti_send_msg_to_task(TASK_RRC_NRUE,module_id,msg_p); - break; - - case NR_DL_DCCH_MessageType__c1_PR_rrcReestablishment: - case NR_DL_DCCH_MessageType__c1_PR_securityModeCommand: - case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransfer: - case NR_DL_DCCH_MessageType__c1_PR_ueCapabilityEnquiry: - case NR_DL_DCCH_MessageType__c1_PR_counterCheck: - case NR_DL_DCCH_MessageType__c1_PR_mobilityFromNRCommand: - case NR_DL_DCCH_MessageType__c1_PR_dlDedicatedMessageSegment_r16: - case NR_DL_DCCH_MessageType__c1_PR_ueInformationRequest_r16: - case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransferMRDC_r16: - case NR_DL_DCCH_MessageType__c1_PR_loggedMeasurementConfiguration_r16: - case NR_DL_DCCH_MessageType__c1_PR_spare3: - case NR_DL_DCCH_MessageType__c1_PR_spare2: - case NR_DL_DCCH_MessageType__c1_PR_spare1: - default: - // not supported or unused - break; - } - break; - case NR_DL_DCCH_MessageType_PR_NOTHING: - case NR_DL_DCCH_MessageType_PR_messageClassExtension: - default: - // not supported or unused - break; - } - - // release memory allocation - SEQUENCE_free( &asn_DEF_NR_DL_DCCH_Message, (void *)nr_dl_dcch_msg, 1 ); - }else{ - // log.. - } - - return 0; -} - - -//----------------------------------------------------------------------------- -void -nr_rrc_ue_process_securityModeCommand( - const protocol_ctxt_t *const ctxt_pP, - NR_SecurityModeCommand_t *const securityModeCommand, - const uint8_t gNB_index -) -//----------------------------------------------------------------------------- -{ - asn_enc_rval_t enc_rval; - NR_UL_DCCH_Message_t ul_dcch_msg; - uint8_t buffer[200]; - int i, securityMode; - LOG_I(NR_RRC,"[UE %d] SFN/SF %d/%d: Receiving from SRB1 (DL-DCCH), Processing securityModeCommand (eNB %d)\n", - ctxt_pP->module_id,ctxt_pP->frame, ctxt_pP->subframe, gNB_index); - - switch (securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.cipheringAlgorithm) { - case NR_CipheringAlgorithm_nea0: - LOG_I(NR_RRC,"[UE %d] Security algorithm is set to nea0\n", - ctxt_pP->module_id); - securityMode= NR_CipheringAlgorithm_nea0; - break; - - case NR_CipheringAlgorithm_nea1: - LOG_I(NR_RRC,"[UE %d] Security algorithm is set to nea1\n",ctxt_pP->module_id); - securityMode= NR_CipheringAlgorithm_nea1; - break; - - case NR_CipheringAlgorithm_nea2: - LOG_I(NR_RRC,"[UE %d] Security algorithm is set to nea2\n", - ctxt_pP->module_id); - securityMode = NR_CipheringAlgorithm_nea2; - break; - - default: - LOG_I(NR_RRC,"[UE %d] Security algorithm is set to none\n",ctxt_pP->module_id); - securityMode = NR_CipheringAlgorithm_spare1; - break; - } - - switch (*securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.integrityProtAlgorithm) { - case NR_IntegrityProtAlgorithm_nia1: - LOG_I(NR_RRC,"[UE %d] Integrity protection algorithm is set to nia1\n",ctxt_pP->module_id); - securityMode |= 1 << 5; - break; - - case NR_IntegrityProtAlgorithm_nia2: - LOG_I(NR_RRC,"[UE %d] Integrity protection algorithm is set to nia2\n",ctxt_pP->module_id); - securityMode |= 1 << 6; - break; - - default: - LOG_I(NR_RRC,"[UE %d] Integrity protection algorithm is set to none\n",ctxt_pP->module_id); - securityMode |= 0x70 ; - break; - } - - LOG_D(NR_RRC,"[UE %d] security mode is %x \n",ctxt_pP->module_id, securityMode); - NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm = - securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.cipheringAlgorithm; - NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm = - *securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.integrityProtAlgorithm; - memset((void *)&ul_dcch_msg,0,sizeof(NR_UL_DCCH_Message_t)); - //memset((void *)&SecurityModeCommand,0,sizeof(SecurityModeCommand_t)); - ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1; - ul_dcch_msg.message.choice.c1 = calloc(1, sizeof(*ul_dcch_msg.message.choice.c1)); - - if (securityMode >= NO_SECURITY_MODE) { - LOG_I(NR_RRC, "rrc_ue_process_securityModeCommand, security mode complete case \n"); - ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeComplete; - } else { - LOG_I(NR_RRC, "rrc_ue_process_securityModeCommand, security mode failure case \n"); - ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeFailure; - } - - uint8_t *kRRCenc = NULL; - uint8_t *kUPenc = NULL; - uint8_t *kRRCint = NULL; - pdcp_t *pdcp_p = NULL; - hash_key_t key = HASHTABLE_NOT_A_KEY_VALUE; - hashtable_rc_t h_rc; - key = PDCP_COLL_KEY_VALUE(ctxt_pP->module_id, ctxt_pP->rnti, - ctxt_pP->enb_flag, DCCH, SRB_FLAG_YES); - h_rc = hashtable_get(pdcp_coll_p, key, (void **) &pdcp_p); - - if (h_rc == HASH_TABLE_OK) { - LOG_D(NR_RRC, "PDCP_COLL_KEY_VALUE() returns valid key = %ld\n", key); - LOG_D(NR_RRC, "driving kRRCenc, kRRCint and kUPenc from KgNB=" - "%02x%02x%02x%02x" - "%02x%02x%02x%02x" - "%02x%02x%02x%02x" - "%02x%02x%02x%02x" - "%02x%02x%02x%02x" - "%02x%02x%02x%02x" - "%02x%02x%02x%02x" - "%02x%02x%02x%02x\n", - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[0], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[1], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[2], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[3], - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[4], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[5], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[6], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[7], - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[8], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[9], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[10], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[11], - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[12], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[13], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[14], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[15], - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[16], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[17], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[18], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[19], - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[20], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[21], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[22], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[23], - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[24], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[25], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[26], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[27], - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[28], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[29], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[30], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[31]); - derive_key_rrc_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm,NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCenc); - derive_key_rrc_int(NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm,NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCint); - derive_key_up_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm,NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kUPenc); - - if (securityMode != 0xff) { - pdcp_config_set_security(ctxt_pP, pdcp_p, 0, 0, - NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm - | (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4), - kRRCenc, kRRCint, kUPenc); - } else { - LOG_I(NR_RRC, "skipped pdcp_config_set_security() as securityMode == 0x%02x", - securityMode); - } - } else { - LOG_I(NR_RRC, "Could not get PDCP instance where key=0x%ld\n", key); - } - - if (securityModeCommand->criticalExtensions.present == NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand) { - ul_dcch_msg.message.choice.c1->choice.securityModeComplete = CALLOC(1, sizeof(NR_SecurityModeComplete_t)); - ul_dcch_msg.message.choice.c1->choice.securityModeComplete->rrc_TransactionIdentifier = securityModeCommand->rrc_TransactionIdentifier; - ul_dcch_msg.message.choice.c1->choice.securityModeComplete->criticalExtensions.present = NR_SecurityModeComplete__criticalExtensions_PR_securityModeComplete; - ul_dcch_msg.message.choice.c1->choice.securityModeComplete->criticalExtensions.choice.securityModeComplete = CALLOC(1, sizeof(NR_SecurityModeComplete_IEs_t)); - ul_dcch_msg.message.choice.c1->choice.securityModeComplete->criticalExtensions.choice.securityModeComplete->nonCriticalExtension =NULL; - LOG_I(NR_RRC,"[UE %d] SFN/SF %d/%d: Receiving from SRB1 (DL-DCCH), encoding securityModeComplete (gNB %d), rrc_TransactionIdentifier: %ld\n", - ctxt_pP->module_id,ctxt_pP->frame, ctxt_pP->subframe, gNB_index, securityModeCommand->rrc_TransactionIdentifier); - enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, - NULL, - (void *)&ul_dcch_msg, - buffer, - 100); - AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - - if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { - xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg); + if ((dec_rval.code != RC_OK) && (dec_rval.consumed==0)) { + LOG_E(RRC,"[UE %d] Frame %d : Failed to decode DL-CCCH-Message (%zu bytes)\n",ctxt_pP->module_id,ctxt_pP->frame,dec_rval.consumed); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_OUT); + return -1; } - LOG_D(NR_RRC, "securityModeComplete Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded+7)/8); + if (dl_ccch_msg->message.present == NR_DL_CCCH_MessageType_PR_c1) { + if (NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].SIStatus > 0) { + switch (dl_ccch_msg->message.choice.c1->present) { + case NR_DL_CCCH_MessageType__c1_PR_NOTHING: + LOG_I(NR_RRC, "[UE%d] Frame %d : Received PR_NOTHING on DL-CCCH-Message\n", + ctxt_pP->module_id, + ctxt_pP->frame); + rval = 0; + break; - for (i = 0; i < (enc_rval.encoded + 7) / 8; i++) { - LOG_T(NR_RRC, "%02x.", buffer[i]); + case NR_DL_CCCH_MessageType__c1_PR_rrcReject: + LOG_I(NR_RRC, + "[UE%d] Frame %d : Logical Channel DL-CCCH (SRB0), Received RRCReject \n", + ctxt_pP->module_id, + ctxt_pP->frame); + rval = 0; + break; + + case NR_DL_CCCH_MessageType__c1_PR_rrcSetup: + LOG_I(NR_RRC, + "[UE%d][RAPROC] Frame %d : Logical Channel DL-CCCH (SRB0), Received NR_RRCSetup RNTI %x\n", + ctxt_pP->module_id, + ctxt_pP->frame, + ctxt_pP->rnti); + + // Get configuration + // Release T300 timer + NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].T300_active = 0; + + nr_rrc_ue_process_masterCellGroup( + ctxt_pP, + gNB_index, + &dl_ccch_msg->message.choice.c1->choice.rrcSetup->criticalExtensions.choice.rrcSetup->masterCellGroup); + nr_sa_rrc_ue_process_radioBearerConfig( + ctxt_pP, + gNB_index, + &dl_ccch_msg->message.choice.c1->choice.rrcSetup->criticalExtensions.choice.rrcSetup->radioBearerConfig); + nr_rrc_set_state (ctxt_pP->module_id, RRC_STATE_CONNECTED); + nr_rrc_set_sub_state (ctxt_pP->module_id, RRC_SUB_STATE_CONNECTED); + NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].rnti = ctxt_pP->rnti; + rrc_ue_generate_RRCSetupComplete( + ctxt_pP, + gNB_index, + dl_ccch_msg->message.choice.c1->choice.rrcSetup->rrc_TransactionIdentifier, + NR_UE_rrc_inst[ctxt_pP->module_id].selected_plmn_identity); + rval = 0; + break; + + default: + LOG_E(NR_RRC, "[UE%d] Frame %d : Unknown message\n", + ctxt_pP->module_id, + ctxt_pP->frame); + rval = -1; + break; + } + } + } + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_OUT); + return rval; + } + + // from NR SRB3 + int8_t nr_rrc_ue_decode_NR_DL_DCCH_Message( + const module_id_t module_id, + const uint8_t gNB_index, + const uint8_t *bufferP, + const uint32_t buffer_len ){ + // uper_decode by nr R15 rrc_connection_reconfiguration + + int32_t i; + NR_DL_DCCH_Message_t *nr_dl_dcch_msg = NULL; + MessageDef *msg_p; + + asn_dec_rval_t dec_rval = uper_decode( NULL, + &asn_DEF_NR_DL_DCCH_Message, + (void**)&nr_dl_dcch_msg, + (uint8_t *)bufferP, + buffer_len, 0, 0); + + if ((dec_rval.code != RC_OK) || (dec_rval.consumed == 0)) { + for (i=0; imessage.present){ + case NR_DL_DCCH_MessageType_PR_c1: + switch(nr_dl_dcch_msg->message.choice.c1->present){ + case NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration: + nr_rrc_ue_process_rrcReconfiguration(module_id,nr_dl_dcch_msg->message.choice.c1->choice.rrcReconfiguration); + break; + + case NR_DL_DCCH_MessageType__c1_PR_NOTHING: + case NR_DL_DCCH_MessageType__c1_PR_rrcResume: + case NR_DL_DCCH_MessageType__c1_PR_rrcRelease: + msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_CONN_RELEASE_IND); + if((nr_dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.present == NR_RRCRelease__criticalExtensions_PR_rrcRelease) && + (nr_dl_dcch_msg->message.choice.c1->present == NR_DL_DCCH_MessageType__c1_PR_rrcRelease)){ + nr_dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationTimer = + NR_RRCRelease_IEs__deprioritisationReq__deprioritisationTimer_min5; + nr_dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationType = + NR_RRCRelease_IEs__deprioritisationReq__deprioritisationType_frequency; + } + itti_send_msg_to_task(TASK_RRC_NRUE,module_id,msg_p); + break; + + case NR_DL_DCCH_MessageType__c1_PR_rrcReestablishment: + case NR_DL_DCCH_MessageType__c1_PR_securityModeCommand: + case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransfer: + case NR_DL_DCCH_MessageType__c1_PR_ueCapabilityEnquiry: + case NR_DL_DCCH_MessageType__c1_PR_counterCheck: + case NR_DL_DCCH_MessageType__c1_PR_mobilityFromNRCommand: + case NR_DL_DCCH_MessageType__c1_PR_dlDedicatedMessageSegment_r16: + case NR_DL_DCCH_MessageType__c1_PR_ueInformationRequest_r16: + case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransferMRDC_r16: + case NR_DL_DCCH_MessageType__c1_PR_loggedMeasurementConfiguration_r16: + case NR_DL_DCCH_MessageType__c1_PR_spare3: + case NR_DL_DCCH_MessageType__c1_PR_spare2: + case NR_DL_DCCH_MessageType__c1_PR_spare1: + default: + // not supported or unused + break; + } + break; + case NR_DL_DCCH_MessageType_PR_NOTHING: + case NR_DL_DCCH_MessageType_PR_messageClassExtension: + default: + // not supported or unused + break; + } + + // release memory allocation + SEQUENCE_free( &asn_DEF_NR_DL_DCCH_Message, (void *)nr_dl_dcch_msg, 1 ); + }else{ + // log.. + } + + return 0; + } + + + //----------------------------------------------------------------------------- + void + nr_rrc_ue_process_securityModeCommand( + const protocol_ctxt_t *const ctxt_pP, + NR_SecurityModeCommand_t *const securityModeCommand, + const uint8_t gNB_index + ) + //----------------------------------------------------------------------------- + { + asn_enc_rval_t enc_rval; + NR_UL_DCCH_Message_t ul_dcch_msg; + uint8_t buffer[200]; + int i, securityMode; + LOG_I(NR_RRC,"[UE %d] SFN/SF %d/%d: Receiving from SRB1 (DL-DCCH), Processing securityModeCommand (eNB %d)\n", + ctxt_pP->module_id,ctxt_pP->frame, ctxt_pP->subframe, gNB_index); + + switch (securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.cipheringAlgorithm) { + case NR_CipheringAlgorithm_nea0: + LOG_I(NR_RRC,"[UE %d] Security algorithm is set to nea0\n", + ctxt_pP->module_id); + securityMode= NR_CipheringAlgorithm_nea0; + break; + + case NR_CipheringAlgorithm_nea1: + LOG_I(NR_RRC,"[UE %d] Security algorithm is set to nea1\n",ctxt_pP->module_id); + securityMode= NR_CipheringAlgorithm_nea1; + break; + + case NR_CipheringAlgorithm_nea2: + LOG_I(NR_RRC,"[UE %d] Security algorithm is set to nea2\n", + ctxt_pP->module_id); + securityMode = NR_CipheringAlgorithm_nea2; + break; + + default: + LOG_I(NR_RRC,"[UE %d] Security algorithm is set to none\n",ctxt_pP->module_id); + securityMode = NR_CipheringAlgorithm_spare1; + break; + } + NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm = + securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.cipheringAlgorithm; + + if (securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.integrityProtAlgorithm != NULL) + { + switch (*securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.integrityProtAlgorithm) { + case NR_IntegrityProtAlgorithm_nia1: + LOG_I(NR_RRC,"[UE %d] Integrity protection algorithm is set to nia1\n",ctxt_pP->module_id); + securityMode |= 1 << 5; + break; + + case NR_IntegrityProtAlgorithm_nia2: + LOG_I(NR_RRC,"[UE %d] Integrity protection algorithm is set to nia2\n",ctxt_pP->module_id); + securityMode |= 1 << 6; + break; + + default: + LOG_I(NR_RRC,"[UE %d] Integrity protection algorithm is set to none\n",ctxt_pP->module_id); + securityMode |= 0x70 ; + break; + } + + NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm = + *securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC.securityAlgorithmConfig.integrityProtAlgorithm; + + } + + LOG_D(NR_RRC,"[UE %d] security mode is %x \n",ctxt_pP->module_id, securityMode); + memset((void *)&ul_dcch_msg,0,sizeof(NR_UL_DCCH_Message_t)); + //memset((void *)&SecurityModeCommand,0,sizeof(SecurityModeCommand_t)); + ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1; + ul_dcch_msg.message.choice.c1 = calloc(1, sizeof(*ul_dcch_msg.message.choice.c1)); + + if (securityMode >= NO_SECURITY_MODE) { + LOG_I(NR_RRC, "rrc_ue_process_securityModeCommand, security mode complete case \n"); + ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeComplete; + } else { + LOG_I(NR_RRC, "rrc_ue_process_securityModeCommand, security mode failure case \n"); + ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeFailure; + ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeComplete; + } + + uint8_t *kRRCenc = NULL; + uint8_t *kUPenc = NULL; + uint8_t *kRRCint = NULL; + pdcp_t *pdcp_p = NULL; + hash_key_t key = HASHTABLE_NOT_A_KEY_VALUE; + hashtable_rc_t h_rc; + key = PDCP_COLL_KEY_VALUE(ctxt_pP->module_id, ctxt_pP->rnti, ctxt_pP->enb_flag, DCCH, SRB_FLAG_YES); + h_rc = hashtable_get(pdcp_coll_p, key, (void **) &pdcp_p); + + if (h_rc == HASH_TABLE_OK) { + LOG_D(NR_RRC, "PDCP_COLL_KEY_VALUE() returns valid key = %ld\n", key); + LOG_D(NR_RRC, "driving kRRCenc, kRRCint and kUPenc from KgNB=" + "%02x%02x%02x%02x" + "%02x%02x%02x%02x" + "%02x%02x%02x%02x" + "%02x%02x%02x%02x" + "%02x%02x%02x%02x" + "%02x%02x%02x%02x" + "%02x%02x%02x%02x" + "%02x%02x%02x%02x\n", + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[0], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[1], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[2], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[3], + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[4], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[5], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[6], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[7], + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[8], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[9], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[10], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[11], + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[12], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[13], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[14], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[15], + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[16], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[17], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[18], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[19], + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[20], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[21], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[22], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[23], + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[24], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[25], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[26], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[27], + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[28], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[29], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[30], NR_UE_rrc_inst[ctxt_pP->module_id].kgnb[31]); + derive_key_rrc_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm,NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCenc); + derive_key_rrc_int(NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm,NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCint); + derive_key_up_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm,NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kUPenc); + + if (securityMode != 0xff) { + pdcp_config_set_security(ctxt_pP, pdcp_p, 0, 0, + NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm + | (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4), + kRRCenc, kRRCint, kUPenc); + } else { + LOG_I(NR_RRC, "skipped pdcp_config_set_security() as securityMode == 0x%02x", securityMode); + } + } else { + LOG_I(NR_RRC, "Could not get PDCP instance where key=0x%ld\n", key); + } + + if (securityModeCommand->criticalExtensions.present == NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand) { + ul_dcch_msg.message.choice.c1->choice.securityModeComplete = CALLOC(1, sizeof(NR_SecurityModeComplete_t)); + ul_dcch_msg.message.choice.c1->choice.securityModeComplete->rrc_TransactionIdentifier = securityModeCommand->rrc_TransactionIdentifier; + ul_dcch_msg.message.choice.c1->choice.securityModeComplete->criticalExtensions.present = NR_SecurityModeComplete__criticalExtensions_PR_securityModeComplete; + ul_dcch_msg.message.choice.c1->choice.securityModeComplete->criticalExtensions.choice.securityModeComplete = CALLOC(1, sizeof(NR_SecurityModeComplete_IEs_t)); + ul_dcch_msg.message.choice.c1->choice.securityModeComplete->criticalExtensions.choice.securityModeComplete->nonCriticalExtension =NULL; + LOG_I(NR_RRC,"[UE %d] SFN/SF %d/%d: Receiving from SRB1 (DL-DCCH), encoding securityModeComplete (gNB %d), rrc_TransactionIdentifier: %ld\n", + ctxt_pP->module_id,ctxt_pP->frame, ctxt_pP->subframe, gNB_index, securityModeCommand->rrc_TransactionIdentifier); + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, + NULL, + (void *)&ul_dcch_msg, + buffer, + 100); + AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg); } + log_dump(MAC, buffer, 16, LOG_DUMP_CHAR, "securityModeComplete payload: "); + LOG_D(NR_RRC, "securityModeComplete Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded+7)/8); - LOG_T(NR_RRC, "\n"); -#ifdef ITTI_SIM - MessageDef *message_p; - uint8_t *message_buffer; - message_buffer = itti_malloc (TASK_RRC_NRUE,TASK_RRC_GNB_SIM, - (enc_rval.encoded + 7) / 8); - memcpy (message_buffer, buffer, (enc_rval.encoded + 7) / 8); + for (i = 0; i < (enc_rval.encoded + 7) / 8; i++) { + LOG_T(NR_RRC, "%02x.", buffer[i]); + } - message_p = itti_alloc_new_message (TASK_RRC_NRUE, 0, UE_RRC_DCCH_DATA_IND); - GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; - GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; - GNB_RRC_DCCH_DATA_IND (message_p).size = (enc_rval.encoded + 7) / 8; - itti_send_msg_to_task (TASK_RRC_GNB_SIM, ctxt_pP->instance, message_p); -#else - rrc_data_req ( - ctxt_pP, - DCCH, - nr_rrc_mui++, - SDU_CONFIRM_NO, - (enc_rval.encoded + 7) / 8, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); -#endif - } else - LOG_W(NR_RRC,"securityModeCommand->criticalExtensions.present (%d) != NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand\n", - securityModeCommand->criticalExtensions.present); -} + LOG_T(NR_RRC, "\n"); + #ifdef ITTI_SIM + MessageDef *message_p; + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_NRUE,TASK_RRC_GNB_SIM, + (enc_rval.encoded + 7) / 8); + memcpy (message_buffer, buffer, (enc_rval.encoded + 7) / 8); -//----------------------------------------------------------------------------- -void nr_rrc_ue_generate_RRCSetupRequest(module_id_t module_id, const uint8_t gNB_index) { - uint8_t i=0,rv[6]; + message_p = itti_alloc_new_message (TASK_RRC_NRUE, 0, UE_RRC_DCCH_DATA_IND); + GNB_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; + GNB_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; + GNB_RRC_DCCH_DATA_IND (message_p).size = (enc_rval.encoded + 7) / 8; + itti_send_msg_to_task (TASK_RRC_GNB_SIM, ctxt_pP->instance, message_p); + #else + rrc_data_req_ue (ctxt_pP, + DCCH, + nr_rrc_mui++, + SDU_CONFIRM_NO, + (enc_rval.encoded + 7) / 8, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + #endif + } else + LOG_W(NR_RRC,"securityModeCommand->criticalExtensions.present (%d) != NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand\n", + securityModeCommand->criticalExtensions.present); + } - if(NR_UE_rrc_inst[module_id].Srb0[gNB_index].Tx_buffer.payload_size ==0) { - // Get RRCConnectionRequest, fill random for now - // Generate random byte stream for contention resolution - for (i=0; i<6; i++) { -#ifdef SMBV - // if SMBV is configured the contention resolution needs to be fix for the connection procedure to succeed - rv[i]=i; -#else - rv[i]=taus()&0xff; -#endif - LOG_T(NR_RRC,"%x.",rv[i]); - } + //----------------------------------------------------------------------------- + void nr_rrc_ue_generate_RRCSetupRequest(module_id_t module_id, const uint8_t gNB_index) { + uint8_t i=0,rv[6]; - LOG_T(NR_RRC,"\n"); - NR_UE_rrc_inst[module_id].Srb0[gNB_index].Tx_buffer.payload_size = - do_RRCSetupRequest( - module_id, - (uint8_t *)NR_UE_rrc_inst[module_id].Srb0[gNB_index].Tx_buffer.Payload, - rv); - LOG_I(NR_RRC,"[UE %d] : Logical Channel UL-CCCH (SRB0), Generating RRCSetupRequest (bytes %d, gNB %d)\n", - module_id, NR_UE_rrc_inst[module_id].Srb0[gNB_index].Tx_buffer.payload_size, gNB_index); + if(get_softmodem_params()->sa) { + AMF_MODE_ENABLED = 1; + } + if(NR_UE_rrc_inst[module_id].Srb0[gNB_index].Tx_buffer.payload_size ==0) { + // Get RRCConnectionRequest, fill random for now + // Generate random byte stream for contention resolution + for (i=0; i<6; i++) { + #ifdef SMBV + // if SMBV is configured the contention resolution needs to be fix for the connection procedure to succeed + rv[i]=i; + #else + rv[i]=taus()&0xff; + #endif + LOG_T(NR_RRC,"%x.",rv[i]); + } - for (i=0; idrb_Identity); + //----------------------------------------------------------------------------- + int32_t + nr_rrc_ue_establish_drb( + module_id_t ue_mod_idP, + frame_t frameP, + uint8_t gNB_index, + NR_DRB_ToAddMod_t *DRB_config + ) + //----------------------------------------------------------------------------- + { + // add descriptor from RRC PDU + int oip_ifup = 0, ip_addr_offset3 = 0, ip_addr_offset4 = 0; + /* avoid gcc warnings */ + (void)oip_ifup; + (void)ip_addr_offset3; + (void)ip_addr_offset4; + LOG_I(NR_RRC,"[UE %d] Frame %d: processing RRCReconfiguration: reconfiguring DRB %ld\n", + ue_mod_idP, frameP, DRB_config->drb_Identity); - if(!AMF_MODE_ENABLED) { - ip_addr_offset3 = 0; - ip_addr_offset4 = 1; - LOG_I(OIP, "[UE %d] trying to bring up the OAI interface %d, IP X.Y.%d.%d\n", ue_mod_idP, ip_addr_offset3+ue_mod_idP, - ip_addr_offset3+ue_mod_idP+1, ip_addr_offset4+ue_mod_idP+1); - oip_ifup = nas_config(ip_addr_offset3+ue_mod_idP+1, // interface_id - UE_NAS_USE_TUN?1:(ip_addr_offset3+ue_mod_idP+1), // third_octet - ip_addr_offset4+ue_mod_idP+1, // fourth_octet - "oip"); // interface suffix (when using kernel module) + if(!AMF_MODE_ENABLED) { + ip_addr_offset3 = 0; + ip_addr_offset4 = 1; + LOG_I(OIP, "[UE %d] trying to bring up the OAI interface %d, IP X.Y.%d.%d\n", ue_mod_idP, ip_addr_offset3+ue_mod_idP, + ip_addr_offset3+ue_mod_idP+1, ip_addr_offset4+ue_mod_idP+1); + oip_ifup = nas_config(ip_addr_offset3+ue_mod_idP+1, // interface_id + UE_NAS_USE_TUN?1:(ip_addr_offset3+ue_mod_idP+1), // third_octet + ip_addr_offset4+ue_mod_idP+1, // fourth_octet + "oip"); // interface suffix (when using kernel module) - if (oip_ifup == 0 && (!UE_NAS_USE_TUN)) { // interface is up --> send a config the DRB - LOG_I(OIP, "[UE %d] Config the ue net interface %d to send/receive pkt on DRB %ld to/from the protocol stack\n", - ue_mod_idP, - ip_addr_offset3+ue_mod_idP, - (long int)((gNB_index * NR_maxDRB) + DRB_config->drb_Identity)); - rb_conf_ipv4(0,//add - ue_mod_idP,//cx align with the UE index - ip_addr_offset3+ue_mod_idP,//inst num_enb+ue_index - (gNB_index * NR_maxDRB) + DRB_config->drb_Identity,//rb - 0,//dscp - ipv4_address(ip_addr_offset3+ue_mod_idP+1, ip_addr_offset4+ue_mod_idP+1),//saddr - ipv4_address(ip_addr_offset3+ue_mod_idP+1, gNB_index+1));//daddr - LOG_D(NR_RRC,"[UE %d] State = Attached (gNB %d)\n",ue_mod_idP,gNB_index); + if (oip_ifup == 0 && (!UE_NAS_USE_TUN)) { // interface is up --> send a config the DRB + LOG_I(OIP, "[UE %d] Config the ue net interface %d to send/receive pkt on DRB %ld to/from the protocol stack\n", + ue_mod_idP, + ip_addr_offset3+ue_mod_idP, + (long int)((gNB_index * NR_maxDRB) + DRB_config->drb_Identity)); + rb_conf_ipv4(0,//add + ue_mod_idP,//cx align with the UE index + ip_addr_offset3+ue_mod_idP,//inst num_enb+ue_index + (gNB_index * NR_maxDRB) + DRB_config->drb_Identity,//rb + 0,//dscp + ipv4_address(ip_addr_offset3+ue_mod_idP+1, ip_addr_offset4+ue_mod_idP+1),//saddr + ipv4_address(ip_addr_offset3+ue_mod_idP+1, gNB_index+1));//daddr + LOG_D(NR_RRC,"[UE %d] State = Attached (gNB %d)\n",ue_mod_idP,gNB_index); + } + } + + return(0); + } + + //----------------------------------------------------------------------------- + void + nr_rrc_ue_process_measConfig( + const protocol_ctxt_t *const ctxt_pP, + const uint8_t gNB_index, + NR_MeasConfig_t *const measConfig + ) + //----------------------------------------------------------------------------- + { + int i; + long ind; + NR_MeasObjectToAddMod_t *measObj = NULL; + NR_ReportConfigToAddMod_t *reportConfig = NULL; + + if (measConfig->measObjectToRemoveList != NULL) { + for (i = 0; i < measConfig->measObjectToRemoveList->list.count; i++) { + ind = *measConfig->measObjectToRemoveList->list.array[i]; + free(NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1]); + } + } + + if (measConfig->measObjectToAddModList != NULL) { + LOG_I(NR_RRC, "Measurement Object List is present\n"); + for (i = 0; i < measConfig->measObjectToAddModList->list.count; i++) { + measObj = measConfig->measObjectToAddModList->list.array[i]; + ind = measConfig->measObjectToAddModList->list.array[i]->measObjectId; + + if (NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1]) { + LOG_D(NR_RRC, "Modifying measurement object %ld\n",ind); + memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1], + (char *)measObj, + sizeof(NR_MeasObjectToAddMod_t)); + } else { + LOG_I(NR_RRC, "Adding measurement object %ld\n", ind); + + if (measObj->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) { + NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1]=measObj; + } + } + } + + LOG_I(NR_RRC, "call rrc_mac_config_req \n"); + // rrc_mac_config_req_ue + } + + if (measConfig->reportConfigToRemoveList != NULL) { + for (i = 0; i < measConfig->reportConfigToRemoveList->list.count; i++) { + ind = *measConfig->reportConfigToRemoveList->list.array[i]; + free(NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1]); + } + } + + if (measConfig->reportConfigToAddModList != NULL) { + LOG_I(NR_RRC,"Report Configuration List is present\n"); + for (i = 0; i < measConfig->reportConfigToAddModList->list.count; i++) { + ind = measConfig->reportConfigToAddModList->list.array[i]->reportConfigId; + reportConfig = measConfig->reportConfigToAddModList->list.array[i]; + + if (NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1]) { + LOG_I(NR_RRC, "Modifying Report Configuration %ld\n", ind-1); + memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1], + (char *)measConfig->reportConfigToAddModList->list.array[i], + sizeof(NR_ReportConfigToAddMod_t)); + } else { + LOG_D(NR_RRC,"Adding Report Configuration %ld %p \n", ind-1, measConfig->reportConfigToAddModList->list.array[i]); + if (reportConfig->reportConfig.present == NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR) { + NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1] = measConfig->reportConfigToAddModList->list.array[i]; + } + } + } + } + + if (measConfig->measIdToRemoveList != NULL) { + for (i = 0; i < measConfig->measIdToRemoveList->list.count; i++) { + ind = *measConfig->measIdToRemoveList->list.array[i]; + free(NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1]); + } + } + + if (measConfig->measIdToAddModList != NULL) { + for (i = 0; i < measConfig->measIdToAddModList->list.count; i++) { + ind = measConfig->measIdToAddModList->list.array[i]->measId; + + if (NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1]) { + LOG_D(NR_RRC, "Modifying Measurement ID %ld\n",ind-1); + memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1], + (char *)measConfig->measIdToAddModList->list.array[i], + sizeof(NR_MeasIdToAddMod_t)); + } else { + LOG_D(NR_RRC, "Adding Measurement ID %ld %p\n", ind-1, measConfig->measIdToAddModList->list.array[i]); + NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1] = measConfig->measIdToAddModList->list.array[i]; + } + } + } + + if (measConfig->quantityConfig != NULL) { + if (NR_UE_rrc_inst[ctxt_pP->module_id].QuantityConfig[gNB_index]) { + LOG_D(NR_RRC,"Modifying Quantity Configuration \n"); + memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].QuantityConfig[gNB_index], + (char *)measConfig->quantityConfig, + sizeof(NR_QuantityConfig_t)); + } else { + LOG_D(NR_RRC, "Adding Quantity configuration\n"); + NR_UE_rrc_inst[ctxt_pP->module_id].QuantityConfig[gNB_index] = measConfig->quantityConfig; + } + } + + if (measConfig->measGapConfig != NULL) { + if (NR_UE_rrc_inst[ctxt_pP->module_id].measGapConfig[gNB_index]) { + memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].measGapConfig[gNB_index], + (char *)measConfig->measGapConfig, + sizeof(NR_MeasGapConfig_t)); + } else { + NR_UE_rrc_inst[ctxt_pP->module_id].measGapConfig[gNB_index] = measConfig->measGapConfig; + } + } + + if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_ssb_RSRP) { + NR_UE_rrc_inst[ctxt_pP->module_id].s_measure = measConfig->s_MeasureConfig->choice.ssb_RSRP; + } else if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_csi_RSRP) { + NR_UE_rrc_inst[ctxt_pP->module_id].s_measure = measConfig->s_MeasureConfig->choice.csi_RSRP; } } - return(0); -} + //----------------------------------------------------------------------------- + void + nr_sa_rrc_ue_process_radioBearerConfig( + const protocol_ctxt_t *const ctxt_pP, + const uint8_t gNB_index, + NR_RadioBearerConfig_t *const radioBearerConfig + ) + //----------------------------------------------------------------------------- + { + long SRB_id, DRB_id; + int i, cnt; -//----------------------------------------------------------------------------- -void -nr_rrc_ue_process_measConfig( - const protocol_ctxt_t *const ctxt_pP, - const uint8_t gNB_index, - NR_MeasConfig_t *const measConfig -) -//----------------------------------------------------------------------------- -{ - int i; - long ind; - NR_MeasObjectToAddMod_t *measObj = NULL; - NR_ReportConfigToAddMod_t *reportConfig = NULL; + if( radioBearerConfig->srb3_ToRelease != NULL){ + if( *radioBearerConfig->srb3_ToRelease == TRUE){ + //TODO (release the PDCP entity and the srb-Identity of the SRB3.) + } + } - if (measConfig->measObjectToRemoveList != NULL) { - for (i = 0; i < measConfig->measObjectToRemoveList->list.count; i++) { - ind = *measConfig->measObjectToRemoveList->list.array[i]; - free(NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1]); - } - } + if (radioBearerConfig->srb_ToAddModList != NULL) { + if (radioBearerConfig->securityConfig != NULL) { + if (*radioBearerConfig->securityConfig->keyToUse == NR_SecurityConfig__keyToUse_master) { + NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm = radioBearerConfig->securityConfig->securityAlgorithmConfig->cipheringAlgorithm; + NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm = *radioBearerConfig->securityConfig->securityAlgorithmConfig->integrityProtAlgorithm; + } + } - if (measConfig->measObjectToAddModList != NULL) { - LOG_I(NR_RRC, "Measurement Object List is present\n"); - for (i = 0; i < measConfig->measObjectToAddModList->list.count; i++) { - measObj = measConfig->measObjectToAddModList->list.array[i]; - ind = measConfig->measObjectToAddModList->list.array[i]->measObjectId; + uint8_t *kRRCenc = NULL; + uint8_t *kRRCint = NULL; + derive_key_rrc_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm, + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCenc); + derive_key_rrc_int(NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm, + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCint); + // Refresh SRBs + nr_rrc_pdcp_config_asn1_req(ctxt_pP, + radioBearerConfig->srb_ToAddModList, + NULL, + NULL, + NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm | + (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4), + kRRCenc, + kRRCint, + NULL, + NULL, + NULL, + NULL, + NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config->rlc_BearerToAddModList); + // Refresh SRBs + nr_rrc_rlc_config_asn1_req(ctxt_pP, + radioBearerConfig->srb_ToAddModList, + NULL, + NULL, + NULL, + NR_UE_rrc_inst[ctxt_pP->module_id].cell_group_config->rlc_BearerToAddModList + ); - if (NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1]) { - LOG_D(NR_RRC, "Modifying measurement object %ld\n",ind); - memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1], - (char *)measObj, - sizeof(NR_MeasObjectToAddMod_t)); - } else { - LOG_I(NR_RRC, "Adding measurement object %ld\n", ind); + for (cnt = 0; cnt < radioBearerConfig->srb_ToAddModList->list.count; cnt++) { + SRB_id = radioBearerConfig->srb_ToAddModList->list.array[cnt]->srb_Identity; + LOG_D(NR_RRC,"[UE %d]: Frame %d SRB config cnt %d (SRB%ld)\n", ctxt_pP->module_id, ctxt_pP->frame, cnt, SRB_id); + if (SRB_id == 1) { + if (NR_UE_rrc_inst[ctxt_pP->module_id].SRB1_config[gNB_index]) { + memcpy(NR_UE_rrc_inst[ctxt_pP->module_id].SRB1_config[gNB_index], + radioBearerConfig->srb_ToAddModList->list.array[cnt], + sizeof(NR_SRB_ToAddMod_t)); + } else { + NR_UE_rrc_inst[ctxt_pP->module_id].SRB1_config[gNB_index] = radioBearerConfig->srb_ToAddModList->list.array[cnt]; + nr_rrc_ue_establish_srb1(ctxt_pP->module_id, + ctxt_pP->frame, + gNB_index, + radioBearerConfig->srb_ToAddModList->list.array[cnt]); - if (measObj->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) { - NR_UE_rrc_inst[ctxt_pP->module_id].MeasObj[gNB_index][ind-1]=measObj; - } - } - } + LOG_I(NR_RRC, "[FRAME %05d][RRC_UE][MOD %02d][][--- MAC_CONFIG_REQ (SRB1 gNB %d) --->][MAC_UE][MOD %02d][]\n", + ctxt_pP->frame, ctxt_pP->module_id, gNB_index, ctxt_pP->module_id); + // rrc_mac_config_req_ue + } + } else { + if (NR_UE_rrc_inst[ctxt_pP->module_id].SRB2_config[gNB_index]) { + memcpy(NR_UE_rrc_inst[ctxt_pP->module_id].SRB2_config[gNB_index], + radioBearerConfig->srb_ToAddModList->list.array[cnt], sizeof(NR_SRB_ToAddMod_t)); + } else { + NR_UE_rrc_inst[ctxt_pP->module_id].SRB2_config[gNB_index] = radioBearerConfig->srb_ToAddModList->list.array[cnt]; + nr_rrc_ue_establish_srb2(ctxt_pP->module_id, + ctxt_pP->frame, + gNB_index, + radioBearerConfig->srb_ToAddModList->list.array[cnt]); - LOG_I(NR_RRC, "call rrc_mac_config_req \n"); - // rrc_mac_config_req_ue - } + LOG_I(NR_RRC, "[FRAME %05d][RRC_UE][MOD %02d][][--- MAC_CONFIG_REQ (SRB2 gNB %d) --->][MAC_UE][MOD %02d][]\n", + ctxt_pP->frame, ctxt_pP->module_id, gNB_index, ctxt_pP->module_id); + // rrc_mac_config_req_ue + } + } // srb2 + } + } // srb_ToAddModList - if (measConfig->reportConfigToRemoveList != NULL) { - for (i = 0; i < measConfig->reportConfigToRemoveList->list.count; i++) { - ind = *measConfig->reportConfigToRemoveList->list.array[i]; - free(NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1]); - } - } + // Establish DRBs if present + if (radioBearerConfig->drb_ToAddModList != NULL) { + if ((NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB == NULL) && + (radioBearerConfig->drb_ToAddModList->list.count >= 1)) { + NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB = malloc(sizeof(rb_id_t)); + *NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB = radioBearerConfig->drb_ToAddModList->list.array[0]->drb_Identity; + } - if (measConfig->reportConfigToAddModList != NULL) { - LOG_I(NR_RRC,"Report Configuration List is present\n"); - for (i = 0; i < measConfig->reportConfigToAddModList->list.count; i++) { - ind = measConfig->reportConfigToAddModList->list.array[i]->reportConfigId; - reportConfig = measConfig->reportConfigToAddModList->list.array[i]; + for (cnt = 0; cnt < radioBearerConfig->drb_ToAddModList->list.count; cnt++) { + DRB_id = radioBearerConfig->drb_ToAddModList->list.array[cnt]->drb_Identity; + if (NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1]) { + memcpy(NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1], + radioBearerConfig->drb_ToAddModList->list.array[cnt], sizeof(NR_DRB_ToAddMod_t)); + } else { + LOG_D(NR_RRC, "Adding DRB %ld %p\n", DRB_id-1, radioBearerConfig->drb_ToAddModList->list.array[cnt]); + NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1] = radioBearerConfig->drb_ToAddModList->list.array[cnt]; + } + } - if (NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1]) { - LOG_I(NR_RRC, "Modifying Report Configuration %ld\n", ind-1); - memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1], - (char *)measConfig->reportConfigToAddModList->list.array[i], - sizeof(NR_ReportConfigToAddMod_t)); - } else { - LOG_D(NR_RRC,"Adding Report Configuration %ld %p \n", ind-1, measConfig->reportConfigToAddModList->list.array[i]); - if (reportConfig->reportConfig.present == NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR) { - NR_UE_rrc_inst[ctxt_pP->module_id].ReportConfig[gNB_index][ind-1] = measConfig->reportConfigToAddModList->list.array[i]; - } - } - } - } + uint8_t *kUPenc = NULL; + derive_key_up_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm, + NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kUPenc); + MSC_LOG_TX_MESSAGE( + MSC_RRC_UE, + MSC_PDCP_UE, + NULL, + 0, + MSC_AS_TIME_FMT" CONFIG_REQ UE %x DRB (security %X)", + MSC_AS_TIME_ARGS(ctxt_pP), + ctxt_pP->rnti, + NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm | + (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4)); - if (measConfig->measIdToRemoveList != NULL) { - for (i = 0; i < measConfig->measIdToRemoveList->list.count; i++) { - ind = *measConfig->measIdToRemoveList->list.array[i]; - free(NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1]); - } - } + // Refresh DRBs + // nr_rrc_pdcp_config_asn1_req(ctxt_pP, + // NULL, + // radioBearerConfig->drb_ToAddModList, + // NULL, + // NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm | + // (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4), + // NULL, + // NULL, + // kUPenc, + // NULL, + // NULL, + // NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB, + // NULL); + // Refresh DRBs + // nr_rrc_rlc_config_asn1_req(ctxt_pP, + // NULL, + // radioBearerConfig->drb_ToAddModList, + // NULL, + // NULL, + // NULL + // ); + } // drb_ToAddModList - if (measConfig->measIdToAddModList != NULL) { - for (i = 0; i < measConfig->measIdToAddModList->list.count; i++) { - ind = measConfig->measIdToAddModList->list.array[i]->measId; + if (radioBearerConfig->drb_ToReleaseList != NULL) { + for (i = 0; i < radioBearerConfig->drb_ToReleaseList->list.count; i++) { + DRB_id = *radioBearerConfig->drb_ToReleaseList->list.array[i]; + free(NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1]); + } + } - if (NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1]) { - LOG_D(NR_RRC, "Modifying Measurement ID %ld\n",ind-1); - memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1], - (char *)measConfig->measIdToAddModList->list.array[i], - sizeof(NR_MeasIdToAddMod_t)); - } else { - LOG_D(NR_RRC, "Adding Measurement ID %ld %p\n", ind-1, measConfig->measIdToAddModList->list.array[i]); - NR_UE_rrc_inst[ctxt_pP->module_id].MeasId[gNB_index][ind-1] = measConfig->measIdToAddModList->list.array[i]; - } - } - } + NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].State = NR_RRC_CONNECTED; + LOG_I(NR_RRC,"[UE %d] State = NR_RRC_CONNECTED (gNB %d)\n", ctxt_pP->module_id, gNB_index); + } - if (measConfig->quantityConfig != NULL) { - if (NR_UE_rrc_inst[ctxt_pP->module_id].QuantityConfig[gNB_index]) { - LOG_D(NR_RRC,"Modifying Quantity Configuration \n"); - memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].QuantityConfig[gNB_index], - (char *)measConfig->quantityConfig, - sizeof(NR_QuantityConfig_t)); - } else { - LOG_D(NR_RRC, "Adding Quantity configuration\n"); - NR_UE_rrc_inst[ctxt_pP->module_id].QuantityConfig[gNB_index] = measConfig->quantityConfig; - } - } + //----------------------------------------------------------------------------- + void + rrc_ue_process_rrcReconfiguration( + const protocol_ctxt_t *const ctxt_pP, + NR_RRCReconfiguration_t *rrcReconfiguration, + uint8_t gNB_index + ) + //----------------------------------------------------------------------------- + { + LOG_I(NR_RRC, "[UE %d] Frame %d: Receiving from SRB1 (DL-DCCH), Processing RRCReconfiguration (gNB %d)\n", + ctxt_pP->module_id, ctxt_pP->frame, gNB_index); - if (measConfig->measGapConfig != NULL) { - if (NR_UE_rrc_inst[ctxt_pP->module_id].measGapConfig[gNB_index]) { - memcpy((char *)NR_UE_rrc_inst[ctxt_pP->module_id].measGapConfig[gNB_index], - (char *)measConfig->measGapConfig, - sizeof(NR_MeasGapConfig_t)); - } else { - NR_UE_rrc_inst[ctxt_pP->module_id].measGapConfig[gNB_index] = measConfig->measGapConfig; - } - } + NR_RRCReconfiguration_IEs_t *ie = NULL; - if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_ssb_RSRP) { - NR_UE_rrc_inst[ctxt_pP->module_id].s_measure = measConfig->s_MeasureConfig->choice.ssb_RSRP; - } else if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_csi_RSRP) { - NR_UE_rrc_inst[ctxt_pP->module_id].s_measure = measConfig->s_MeasureConfig->choice.csi_RSRP; - } -} + if (rrcReconfiguration->criticalExtensions.present + == NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration) { + ie = rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration; + if (ie->measConfig != NULL) { + LOG_I(NR_RRC, "Measurement Configuration is present\n"); + // nr_rrc_ue_process_measConfig(ctxt_pP, gNB_index, ie->measConfig); + } -//----------------------------------------------------------------------------- -void -nr_sa_rrc_ue_process_radioBearerConfig( - const protocol_ctxt_t *const ctxt_pP, - const uint8_t gNB_index, - NR_RadioBearerConfig_t *const radioBearerConfig -) -//----------------------------------------------------------------------------- -{ - long SRB_id, DRB_id; - int i, cnt; + if (ie->radioBearerConfig != NULL) { + LOG_I(NR_RRC, "radio Bearer Configuration is present\n"); + // nr_sa_rrc_ue_process_radioBearerConfig(ctxt_pP, gNB_index, ie->radioBearerConfig); + } - if( radioBearerConfig->srb3_ToRelease != NULL){ - if( *radioBearerConfig->srb3_ToRelease == TRUE){ - //TODO (release the PDCP entity and the srb-Identity of the SRB3.) - } - } + /* Check if there is dedicated NAS information to forward to NAS */ + if (ie->nonCriticalExtension->dedicatedNAS_MessageList != NULL) { + int list_count; + uint32_t pdu_length; + uint8_t *pdu_buffer; + MessageDef *msg_p; - if (radioBearerConfig->srb_ToAddModList != NULL) { - if (radioBearerConfig->securityConfig != NULL) { - if (*radioBearerConfig->securityConfig->keyToUse == NR_SecurityConfig__keyToUse_master) { - NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm = - radioBearerConfig->securityConfig->securityAlgorithmConfig->cipheringAlgorithm; - NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm = - *radioBearerConfig->securityConfig->securityAlgorithmConfig->integrityProtAlgorithm; - } - } + for (list_count = 0; list_count < ie->nonCriticalExtension->dedicatedNAS_MessageList->list.count; list_count++) { + pdu_length = ie->nonCriticalExtension->dedicatedNAS_MessageList->list.array[list_count]->size; + pdu_buffer = ie->nonCriticalExtension->dedicatedNAS_MessageList->list.array[list_count]->buf; + msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_CONN_ESTABLI_CNF); + NAS_CONN_ESTABLI_CNF(msg_p).errCode = AS_SUCCESS; + NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length = pdu_length; + NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.data = pdu_buffer; + itti_send_msg_to_task(TASK_NAS_NRUE, ctxt_pP->instance, msg_p); + } - uint8_t *kRRCenc = NULL; - uint8_t *kRRCint = NULL; - derive_key_rrc_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm, - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCenc); - derive_key_rrc_int(NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm, - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kRRCint); - // Refresh SRBs - // nr_rrc_pdcp_config_asn1_req(ctxt_pP, - // radioBearerConfig->srb_ToAddModList, - // NULL, - // NULL, - // NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm | - // (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4), - // kRRCenc, - // kRRCint, - // NULL, - // NULL, - // NULL, - // NULL, - // NULL); - // Refresh SRBs - // nr_rrc_rlc_config_asn1_req(ctxt_pP, - // radioBearerConfig->srb_ToAddModList, - // NULL, - // NULL, - // NULL, - // NULL - // ); + free (ie->nonCriticalExtension->dedicatedNAS_MessageList); + } + } + } - for (cnt = 0; cnt < radioBearerConfig->srb_ToAddModList->list.count; cnt++) { - SRB_id = radioBearerConfig->srb_ToAddModList->list.array[cnt]->srb_Identity; - LOG_D(NR_RRC,"[UE %d]: Frame %d SRB config cnt %d (SRB%ld)\n", ctxt_pP->module_id, ctxt_pP->frame, cnt, SRB_id); - if (SRB_id == 1) { - if (NR_UE_rrc_inst[ctxt_pP->module_id].SRB1_config[gNB_index]) { - memcpy(NR_UE_rrc_inst[ctxt_pP->module_id].SRB1_config[gNB_index], - radioBearerConfig->srb_ToAddModList->list.array[cnt], sizeof(NR_SRB_ToAddMod_t)); - } else { - NR_UE_rrc_inst[ctxt_pP->module_id].SRB1_config[gNB_index] = radioBearerConfig->srb_ToAddModList->list.array[cnt]; - nr_rrc_ue_establish_srb1(ctxt_pP->module_id, - ctxt_pP->frame, - gNB_index, - radioBearerConfig->srb_ToAddModList->list.array[cnt]); + //----------------------------------------------------------------------------- + void nr_rrc_ue_generate_RRCReconfigurationComplete( const protocol_ctxt_t *const ctxt_pP, const uint8_t gNB_index, const uint8_t Transaction_id ) { + uint8_t buffer[32], size; + size = do_NR_RRCReconfigurationComplete(ctxt_pP, buffer, Transaction_id); + LOG_I(NR_RRC,PROTOCOL_RRC_CTXT_UE_FMT" Logical Channel UL-DCCH (SRB1), Generating RRCReconfigurationComplete (bytes %d, gNB_index %d)\n", + PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP), size, gNB_index); + LOG_D(RLC, + "[FRAME %05d][RRC_UE][INST %02d][][--- PDCP_DATA_REQ/%d Bytes (RRCReconfigurationComplete to gNB %d MUI %d) --->][PDCP][INST %02d][RB %02d]\n", + ctxt_pP->frame, + UE_MODULE_ID_TO_INSTANCE(ctxt_pP->module_id), + size, + gNB_index, + nr_rrc_mui, + UE_MODULE_ID_TO_INSTANCE(ctxt_pP->module_id), + DCCH); + #ifdef ITTI_SIM + MessageDef *message_p; + uint8_t *message_buffer; + message_buffer = itti_malloc (TASK_RRC_NRUE,TASK_RRC_GNB_SIM,size); + memcpy (message_buffer, buffer, size); - LOG_I(NR_RRC, "[FRAME %05d][RRC_UE][MOD %02d][][--- MAC_CONFIG_REQ (SRB1 gNB %d) --->][MAC_UE][MOD %02d][]\n", - ctxt_pP->frame, ctxt_pP->module_id, gNB_index, ctxt_pP->module_id); - // rrc_mac_config_req_ue - } - } else { - if (NR_UE_rrc_inst[ctxt_pP->module_id].SRB2_config[gNB_index]) { - memcpy(NR_UE_rrc_inst[ctxt_pP->module_id].SRB2_config[gNB_index], - radioBearerConfig->srb_ToAddModList->list.array[cnt], sizeof(NR_SRB_ToAddMod_t)); - } else { - NR_UE_rrc_inst[ctxt_pP->module_id].SRB2_config[gNB_index] = radioBearerConfig->srb_ToAddModList->list.array[cnt]; - nr_rrc_ue_establish_srb2(ctxt_pP->module_id, - ctxt_pP->frame, - gNB_index, - radioBearerConfig->srb_ToAddModList->list.array[cnt]); + message_p = itti_alloc_new_message (TASK_RRC_NRUE, 0, UE_RRC_DCCH_DATA_IND); + UE_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; + UE_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; + UE_RRC_DCCH_DATA_IND (message_p).size = size; + itti_send_msg_to_task (TASK_RRC_GNB_SIM, ctxt_pP->instance, message_p); - LOG_I(NR_RRC, "[FRAME %05d][RRC_UE][MOD %02d][][--- MAC_CONFIG_REQ (SRB2 gNB %d) --->][MAC_UE][MOD %02d][]\n", - ctxt_pP->frame, ctxt_pP->module_id, gNB_index, ctxt_pP->module_id); - // rrc_mac_config_req_ue - } - } // srb2 - } - } // srb_ToAddModList + #else + rrc_data_req_ue ( + ctxt_pP, + DCCH, + nr_rrc_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + #endif - // Establish DRBs if present - if (radioBearerConfig->drb_ToAddModList != NULL) { - if ((NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB == NULL) && - (radioBearerConfig->drb_ToAddModList->list.count >= 1)) { - NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB = malloc(sizeof(rb_id_t)); - *NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB = radioBearerConfig->drb_ToAddModList->list.array[0]->drb_Identity; - } + } - for (cnt = 0; cnt < radioBearerConfig->drb_ToAddModList->list.count; cnt++) { - DRB_id = radioBearerConfig->drb_ToAddModList->list.array[cnt]->drb_Identity; - if (NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1]) { - memcpy(NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1], - radioBearerConfig->drb_ToAddModList->list.array[cnt], sizeof(NR_DRB_ToAddMod_t)); - } else { - LOG_D(NR_RRC, "Adding DRB %ld %p\n", DRB_id-1, radioBearerConfig->drb_ToAddModList->list.array[cnt]); - NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1] = radioBearerConfig->drb_ToAddModList->list.array[cnt]; - } - } + // from NR SRB1 + //----------------------------------------------------------------------------- + int + nr_rrc_ue_decode_dcch( + const protocol_ctxt_t *const ctxt_pP, + const srb_id_t Srb_id, + const uint8_t *const Buffer, + const uint8_t gNB_indexP + ) + //----------------------------------------------------------------------------- + { + asn_dec_rval_t dec_rval; + NR_DL_DCCH_Message_t *dl_dcch_msg = NULL; + MessageDef *msg_p; - uint8_t *kUPenc = NULL; - derive_key_up_enc(NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm, - NR_UE_rrc_inst[ctxt_pP->module_id].kgnb, &kUPenc); - MSC_LOG_TX_MESSAGE( - MSC_RRC_UE, - MSC_PDCP_UE, - NULL, - 0, - MSC_AS_TIME_FMT" CONFIG_REQ UE %x DRB (security %X)", - MSC_AS_TIME_ARGS(ctxt_pP), - ctxt_pP->rnti, - NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm | - (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4)); + if (Srb_id != 1) { + LOG_E(NR_RRC,"[UE %d] Frame %d: Received message on DL-DCCH (SRB%ld), should not have ...\n", + ctxt_pP->module_id, ctxt_pP->frame, Srb_id); + return -1; + } else { + LOG_D(NR_RRC, "Received message on SRB%ld\n", Srb_id); + } - // Refresh DRBs - // nr_rrc_pdcp_config_asn1_req(ctxt_pP, - // NULL, - // radioBearerConfig->drb_ToAddModList, - // NULL, - // NR_UE_rrc_inst[ctxt_pP->module_id].cipheringAlgorithm | - // (NR_UE_rrc_inst[ctxt_pP->module_id].integrityProtAlgorithm << 4), - // NULL, - // NULL, - // kUPenc, - // NULL, - // NULL, - // NR_UE_rrc_inst[ctxt_pP->module_id].defaultDRB, - // NULL); - // Refresh DRBs - // nr_rrc_rlc_config_asn1_req(ctxt_pP, - // NULL, - // radioBearerConfig->drb_ToAddModList, - // NULL, - // NULL, - // NULL - // ); - } // drb_ToAddModList + LOG_D(NR_RRC, "Decoding DL-DCCH Message\n"); + dec_rval = uper_decode( NULL, + &asn_DEF_NR_DL_DCCH_Message, + (void **)&dl_dcch_msg, + Buffer, + RRC_BUF_SIZE, + 0, + 0); - if (radioBearerConfig->drb_ToReleaseList != NULL) { - for (i = 0; i < radioBearerConfig->drb_ToReleaseList->list.count; i++) { - DRB_id = *radioBearerConfig->drb_ToReleaseList->list.array[i]; - free(NR_UE_rrc_inst[ctxt_pP->module_id].DRB_config[gNB_index][DRB_id-1]); - } - } + if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { + LOG_E(NR_RRC, "Failed to decode DL-DCCH (%zu bytes)\n", dec_rval.consumed); + return -1; + } - NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].State = NR_RRC_CONNECTED; - LOG_I(NR_RRC,"[UE %d] State = NR_RRC_CONNECTED (gNB %d)\n", ctxt_pP->module_id, gNB_index); -} + // if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message,(void *)dl_dcch_msg); + // } -//----------------------------------------------------------------------------- -void -rrc_ue_process_rrcReconfiguration( - const protocol_ctxt_t *const ctxt_pP, - NR_RRCReconfiguration_t *rrcReconfiguration, - uint8_t gNB_index -) -//----------------------------------------------------------------------------- -{ - LOG_I(NR_RRC, "[UE %d] Frame %d: Receiving from SRB1 (DL-DCCH), Processing RRCReconfiguration (gNB %d)\n", - ctxt_pP->module_id, ctxt_pP->frame, gNB_index); + if (dl_dcch_msg->message.present == NR_DL_DCCH_MessageType_PR_c1) { + switch (dl_dcch_msg->message.choice.c1->present) { + case NR_DL_DCCH_MessageType__c1_PR_NOTHING: + LOG_I(NR_RRC, "Received PR_NOTHING on DL-DCCH-Message\n"); + break; - NR_RRCReconfiguration_IEs_t *ie = NULL; + case NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration: + { + rrc_ue_process_rrcReconfiguration(ctxt_pP, + dl_dcch_msg->message.choice.c1->choice.rrcReconfiguration, + gNB_indexP); + nr_rrc_ue_generate_RRCReconfigurationComplete(ctxt_pP, + gNB_indexP, + dl_dcch_msg->message.choice.c1->choice.rrcReconfiguration->rrc_TransactionIdentifier); + break; + } - if (rrcReconfiguration->criticalExtensions.present - == NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration) { - ie = rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration; - if (ie->measConfig != NULL) { - LOG_I(NR_RRC, "Measurement Configuration is present\n"); - nr_rrc_ue_process_measConfig(ctxt_pP, gNB_index, ie->measConfig); - } + case NR_DL_DCCH_MessageType__c1_PR_rrcResume: + case NR_DL_DCCH_MessageType__c1_PR_rrcRelease: + LOG_I(NR_RRC, "[UE %d] Received RRC Release (gNB %d)\n", + ctxt_pP->module_id, gNB_indexP); - if (ie->radioBearerConfig != NULL) { - LOG_I(NR_RRC, "radio Bearer Configuration is present\n"); - nr_sa_rrc_ue_process_radioBearerConfig(ctxt_pP, gNB_index, ie->radioBearerConfig); - } + msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_CONN_RELEASE_IND); - /* Check if there is dedicated NAS information to forward to NAS */ - if (ie->nonCriticalExtension->dedicatedNAS_MessageList != NULL) { - int list_count; - uint32_t pdu_length; - uint8_t *pdu_buffer; - MessageDef *msg_p; + if((dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.present == NR_RRCRelease__criticalExtensions_PR_rrcRelease) && + (dl_dcch_msg->message.choice.c1->present == NR_DL_DCCH_MessageType__c1_PR_rrcRelease)){ + dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationTimer = + NR_RRCRelease_IEs__deprioritisationReq__deprioritisationTimer_min5; + dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationType = + NR_RRCRelease_IEs__deprioritisationReq__deprioritisationType_frequency; + } - for (list_count = 0; list_count < ie->nonCriticalExtension->dedicatedNAS_MessageList->list.count; list_count++) { - pdu_length = ie->nonCriticalExtension->dedicatedNAS_MessageList->list.array[list_count]->size; - pdu_buffer = ie->nonCriticalExtension->dedicatedNAS_MessageList->list.array[list_count]->buf; -#ifdef ITTI_SIM - uint8_t msg_type = 0; - if((pdu_buffer + 1) != NULL){ - if (*(pdu_buffer + 1) > 0 ) { - if((pdu_buffer + 9) != NULL){ - msg_type = *(pdu_buffer + 9); - } else { - LOG_W(NR_RRC, "[UE] Received invalid downlink message\n"); - return; - } - } else { - if((pdu_buffer + 2) != NULL){ - msg_type = *(pdu_buffer + 2); - } else { - LOG_W(NR_RRC, "[UE] Received invalid downlink message\n"); - return; - } - } - } - if(msg_type == REGISTRATION_ACCEPT){ - LOG_I(NR_RRC, "[UE] Received REGISTRATION ACCEPT message\n"); - } -#endif - msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_CONN_ESTABLI_CNF); - NAS_CONN_ESTABLI_CNF(msg_p).errCode = AS_SUCCESS; - NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length = pdu_length; - NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.data = pdu_buffer; - itti_send_msg_to_task(TASK_NAS_UE, ctxt_pP->instance, msg_p); - } + itti_send_msg_to_task(TASK_NAS_UE, ctxt_pP->instance, msg_p); + break; + case NR_DL_DCCH_MessageType__c1_PR_ueCapabilityEnquiry: + LOG_I(NR_RRC, "[UE %d] Received Capability Enquiry (gNB %d)\n", ctxt_pP->module_id,gNB_indexP); + nr_rrc_ue_process_ueCapabilityEnquiry( + ctxt_pP, + dl_dcch_msg->message.choice.c1->choice.ueCapabilityEnquiry, + gNB_indexP); + break; + case NR_DL_DCCH_MessageType__c1_PR_rrcReestablishment: + LOG_I(NR_RRC, + "[UE%d] Frame %d : Logical Channel DL-DCCH (SRB1), Received RRCReestablishment\n", + ctxt_pP->module_id, + ctxt_pP->frame); + nr_rrc_ue_generate_rrcReestablishmentComplete( + ctxt_pP, + dl_dcch_msg->message.choice.c1->choice.rrcReestablishment, + gNB_indexP); + break; + case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransfer: + { + NR_DLInformationTransfer_t *dlInformationTransfer = dl_dcch_msg->message.choice.c1->choice.dlInformationTransfer; - free (ie->nonCriticalExtension->dedicatedNAS_MessageList); - } - } -} + if (dlInformationTransfer->criticalExtensions.present + == NR_DLInformationTransfer__criticalExtensions_PR_dlInformationTransfer) { + /* This message hold a dedicated info NAS payload, forward it to NAS */ + NR_DedicatedNAS_Message_t *dedicatedNAS_Message = + dlInformationTransfer->criticalExtensions.choice.dlInformationTransfer->dedicatedNAS_Message; -//----------------------------------------------------------------------------- -void nr_rrc_ue_generate_RRCReconfigurationComplete( const protocol_ctxt_t *const ctxt_pP, const uint8_t gNB_index, const uint8_t Transaction_id ) { - uint8_t buffer[32], size; - size = do_NR_RRCReconfigurationComplete(ctxt_pP, buffer, Transaction_id); - LOG_I(NR_RRC,PROTOCOL_RRC_CTXT_UE_FMT" Logical Channel UL-DCCH (SRB1), Generating RRCReconfigurationComplete (bytes %d, gNB_index %d)\n", - PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP), size, gNB_index); - LOG_D(RLC, - "[FRAME %05d][RRC_UE][INST %02d][][--- PDCP_DATA_REQ/%d Bytes (RRCReconfigurationComplete to gNB %d MUI %d) --->][PDCP][INST %02d][RB %02d]\n", - ctxt_pP->frame, - UE_MODULE_ID_TO_INSTANCE(ctxt_pP->module_id), - size, - gNB_index, - nr_rrc_mui, - UE_MODULE_ID_TO_INSTANCE(ctxt_pP->module_id), - DCCH); -#ifdef ITTI_SIM - MessageDef *message_p; - uint8_t *message_buffer; - message_buffer = itti_malloc (TASK_RRC_NRUE,TASK_RRC_GNB_SIM,size); - memcpy (message_buffer, buffer, size); + MessageDef *msg_p; + msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_DOWNLINK_DATA_IND); + NAS_DOWNLINK_DATA_IND(msg_p).UEid = ctxt_pP->module_id; // TODO set the UEid to something else ? + NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length = dedicatedNAS_Message->size; + NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data = dedicatedNAS_Message->buf; + itti_send_msg_to_task(TASK_NAS_NRUE, ctxt_pP->instance, msg_p); + } + } - message_p = itti_alloc_new_message (TASK_RRC_NRUE, 0, UE_RRC_DCCH_DATA_IND); - UE_RRC_DCCH_DATA_IND (message_p).rbid = DCCH; - UE_RRC_DCCH_DATA_IND (message_p).sdu = message_buffer; - UE_RRC_DCCH_DATA_IND (message_p).size = size; - itti_send_msg_to_task (TASK_RRC_GNB_SIM, ctxt_pP->instance, message_p); + break; + case NR_DL_DCCH_MessageType__c1_PR_mobilityFromNRCommand: + case NR_DL_DCCH_MessageType__c1_PR_dlDedicatedMessageSegment_r16: + case NR_DL_DCCH_MessageType__c1_PR_ueInformationRequest_r16: + case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransferMRDC_r16: + case NR_DL_DCCH_MessageType__c1_PR_loggedMeasurementConfiguration_r16: + case NR_DL_DCCH_MessageType__c1_PR_spare3: + case NR_DL_DCCH_MessageType__c1_PR_spare2: + case NR_DL_DCCH_MessageType__c1_PR_spare1: + case NR_DL_DCCH_MessageType__c1_PR_counterCheck: + break; + case NR_DL_DCCH_MessageType__c1_PR_securityModeCommand: + LOG_I(NR_RRC, "[UE %d] Received securityModeCommand (gNB %d)\n", + ctxt_pP->module_id, gNB_indexP); + nr_rrc_ue_process_securityModeCommand( + ctxt_pP, + dl_dcch_msg->message.choice.c1->choice.securityModeCommand, + gNB_indexP); -#else - rrc_data_req_ue ( - ctxt_pP, - DCCH, - nr_rrc_mui++, - SDU_CONFIRM_NO, - size, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); -#endif + break; + } + } + return 0; + } -} + //----------------------------------------------------------------------------- + void *rrc_nrue_task( void *args_p ) { + MessageDef *msg_p; + instance_t instance; + unsigned int ue_mod_id; + int result; + NR_SRB_INFO *srb_info_p; + protocol_ctxt_t ctxt; + itti_mark_task_ready (TASK_RRC_NRUE); -// from NR SRB1 -//----------------------------------------------------------------------------- -int -nr_rrc_ue_decode_dcch( - const protocol_ctxt_t *const ctxt_pP, - const srb_id_t Srb_id, - const uint8_t *const Buffer, - const uint8_t gNB_indexP -) -//----------------------------------------------------------------------------- -{ - asn_dec_rval_t dec_rval; - NR_DL_DCCH_Message_t *dl_dcch_msg = NULL; - MessageDef *msg_p; + while(1) { + // Wait for a message + itti_receive_msg (TASK_RRC_NRUE, &msg_p); + instance = ITTI_MSG_DESTINATION_INSTANCE (msg_p); + ue_mod_id = UE_INSTANCE_TO_MODULE_ID(instance); - if (Srb_id != 1) { - LOG_E(NR_RRC,"[UE %d] Frame %d: Received message on DL-DCCH (SRB%ld), should not have ...\n", - ctxt_pP->module_id, ctxt_pP->frame, Srb_id); - return -1; - } else { - LOG_D(NR_RRC, "Received message on SRB%ld\n", Srb_id); - } + switch (ITTI_MSG_ID(msg_p)) { + case TERMINATE_MESSAGE: + LOG_W(NR_RRC, " *** Exiting RRC thread\n"); + itti_exit_task (); + break; - LOG_D(NR_RRC, "Decoding DL-DCCH Message\n"); - dec_rval = uper_decode( NULL, - &asn_DEF_NR_DL_DCCH_Message, - (void **)&dl_dcch_msg, - Buffer, - RRC_BUF_SIZE, - 0, - 0); + case MESSAGE_TEST: + LOG_D(NR_RRC, "[UE %d] Received %s\n", ue_mod_id, ITTI_MSG_NAME (msg_p)); + break; - if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { - LOG_E(NR_RRC, "Failed to decode DL-DCCH (%zu bytes)\n", dec_rval.consumed); - return -1; - } + case NR_RRC_MAC_BCCH_DATA_IND: + LOG_D(NR_RRC, "[UE %d] Received %s: frameP %d, gNB %d\n", ue_mod_id, ITTI_MSG_NAME (msg_p), + NR_RRC_MAC_BCCH_DATA_IND (msg_p).frame, NR_RRC_MAC_BCCH_DATA_IND (msg_p).gnb_index); + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, ue_mod_id, GNB_FLAG_NO, NOT_A_RNTI, NR_RRC_MAC_BCCH_DATA_IND (msg_p).frame, 0,NR_RRC_MAC_BCCH_DATA_IND (msg_p).gnb_index); + nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message (ctxt.module_id, + NR_RRC_MAC_BCCH_DATA_IND (msg_p).gnb_index, + NR_RRC_MAC_BCCH_DATA_IND (msg_p).sdu, + NR_RRC_MAC_BCCH_DATA_IND (msg_p).sdu_size, + NR_RRC_MAC_BCCH_DATA_IND (msg_p).rsrq, + NR_RRC_MAC_BCCH_DATA_IND (msg_p).rsrp); + break; - // if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { - xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message,(void *)dl_dcch_msg); - // } - - if (dl_dcch_msg->message.present == NR_DL_DCCH_MessageType_PR_c1) { - switch (dl_dcch_msg->message.choice.c1->present) { - case NR_DL_DCCH_MessageType__c1_PR_NOTHING: - LOG_I(NR_RRC, "Received PR_NOTHING on DL-DCCH-Message\n"); - break; - - case NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration: - { - rrc_ue_process_rrcReconfiguration(ctxt_pP, - dl_dcch_msg->message.choice.c1->choice.rrcReconfiguration, - gNB_indexP); - nr_rrc_ue_generate_RRCReconfigurationComplete(ctxt_pP, - gNB_indexP, - dl_dcch_msg->message.choice.c1->choice.rrcReconfiguration->rrc_TransactionIdentifier); - - if (first_rrcreconfigurationcomplete == 0) { - first_rrcreconfigurationcomplete = 1; -#ifdef ITTI_SIM - if (AMF_MODE_ENABLED) { - as_nas_info_t initialNasMsg; - memset(&initialNasMsg, 0, sizeof(as_nas_info_t)); - generateRegistrationComplete(&initialNasMsg, NULL); - if(initialNasMsg.length > 0){ - MessageDef *message_p; - message_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_UPLINK_DATA_REQ); - NAS_UPLINK_DATA_REQ(message_p).UEid = ctxt_pP->module_id; - NAS_UPLINK_DATA_REQ(message_p).nasMsg.data = (uint8_t *)initialNasMsg.data; - NAS_UPLINK_DATA_REQ(message_p).nasMsg.length = initialNasMsg.length; - itti_send_msg_to_task(TASK_RRC_NRUE, ctxt_pP->instance, message_p); - LOG_I(NR_RRC, " Send NAS_UPLINK_DATA_REQ message(RegistrationComplete)\n"); - } - as_nas_info_t pduEstablishMsg; - memset(&pduEstablishMsg, 0, sizeof(as_nas_info_t)); - generatePduSessionEstablishRequest(&pduEstablishMsg); - if(initialNasMsg.length > 0){ - MessageDef *message_p; - message_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_UPLINK_DATA_REQ); - NAS_UPLINK_DATA_REQ(message_p).UEid = ctxt_pP->module_id; - NAS_UPLINK_DATA_REQ(message_p).nasMsg.data = (uint8_t *)pduEstablishMsg.data; - NAS_UPLINK_DATA_REQ(message_p).nasMsg.length = pduEstablishMsg.length; - itti_send_msg_to_task(TASK_RRC_NRUE, ctxt_pP->instance, message_p); - LOG_I(NR_RRC, " Send NAS_UPLINK_DATA_REQ message(PduSessionEstablishRequest)\n"); - } - } -#endif - } - } - break; - - case NR_DL_DCCH_MessageType__c1_PR_rrcResume: - case NR_DL_DCCH_MessageType__c1_PR_rrcRelease: - LOG_I(NR_RRC, "[UE %d] Received RRC Release (gNB %d)\n", - ctxt_pP->module_id, gNB_indexP); - - msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_CONN_RELEASE_IND); - - if((dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.present == NR_RRCRelease__criticalExtensions_PR_rrcRelease) && - (dl_dcch_msg->message.choice.c1->present == NR_DL_DCCH_MessageType__c1_PR_rrcRelease)){ - dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationTimer = - NR_RRCRelease_IEs__deprioritisationReq__deprioritisationTimer_min5; - dl_dcch_msg->message.choice.c1->choice.rrcRelease->criticalExtensions.choice.rrcRelease->deprioritisationReq->deprioritisationType = - NR_RRCRelease_IEs__deprioritisationReq__deprioritisationType_frequency; - } - - itti_send_msg_to_task(TASK_NAS_UE, ctxt_pP->instance, msg_p); - break; - case NR_DL_DCCH_MessageType__c1_PR_ueCapabilityEnquiry: - LOG_I(NR_RRC, "[UE %d] Received Capability Enquiry (gNB %d)\n", - ctxt_pP->module_id,gNB_indexP); - nr_rrc_ue_process_ueCapabilityEnquiry( - ctxt_pP, - dl_dcch_msg->message.choice.c1->choice.ueCapabilityEnquiry, - gNB_indexP); - break; - case NR_DL_DCCH_MessageType__c1_PR_rrcReestablishment: - LOG_I(NR_RRC, - "[UE%d] Frame %d : Logical Channel DL-DCCH (SRB1), Received RRCReestablishment\n", - ctxt_pP->module_id, - ctxt_pP->frame); - nr_rrc_ue_generate_rrcReestablishmentComplete( - ctxt_pP, - dl_dcch_msg->message.choice.c1->choice.rrcReestablishment, - gNB_indexP); - break; - case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransfer: - { - NR_DLInformationTransfer_t *dlInformationTransfer = dl_dcch_msg->message.choice.c1->choice.dlInformationTransfer; - - if (dlInformationTransfer->criticalExtensions.present - == NR_DLInformationTransfer__criticalExtensions_PR_dlInformationTransfer) { - /* This message hold a dedicated info NAS payload, forward it to NAS */ - NR_DedicatedNAS_Message_t *dedicatedNAS_Message = - dlInformationTransfer->criticalExtensions.choice.dlInformationTransfer->dedicatedNAS_Message; - uint32_t pdu_length; - uint8_t *pdu_buffer; - pdu_length = dedicatedNAS_Message->size; - pdu_buffer = dedicatedNAS_Message->buf; -#ifdef ITTI_SIM - LOG_I(NR_RRC, "[UE %d] Received %s: UEid %u, length %u , buffer %p\n", ctxt_pP->module_id, messages_info[NAS_DOWNLINK_DATA_IND].name, - ctxt_pP->module_id, pdu_length, pdu_buffer); - as_nas_info_t initialNasMsg; - uint8_t msg_type = 0; - memset(&initialNasMsg, 0, sizeof(as_nas_info_t)); - if((pdu_buffer + 1) != NULL){ - if (*(pdu_buffer + 1) > 0 ) { - msg_type = *(pdu_buffer + 9); - } else { - msg_type = *(pdu_buffer + 2); - } - } - if((pdu_buffer + 2) == NULL){ - LOG_W(NR_RRC, "[UE] Received invalid downlink message\n"); - return 0; - } - - switch(msg_type){ - case FGS_IDENTITY_REQUEST: - generateIdentityResponse(&initialNasMsg,*(pdu_buffer+3)); - break; - case FGS_AUTHENTICATION_REQUEST: - generateAuthenticationResp(&initialNasMsg, pdu_buffer); - break; - case FGS_SECURITY_MODE_COMMAND: - generateSecurityModeComplete(&initialNasMsg); - break; - default: - LOG_W(NR_RRC,"unknow message type %d\n",msg_type); - break; - } - if(initialNasMsg.length > 0){ - MessageDef *message_p; - message_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_UPLINK_DATA_REQ); - NAS_UPLINK_DATA_REQ(message_p).UEid = ctxt_pP->module_id; - NAS_UPLINK_DATA_REQ(message_p).nasMsg.data = (uint8_t *)initialNasMsg.data; - NAS_UPLINK_DATA_REQ(message_p).nasMsg.length = initialNasMsg.length; - itti_send_msg_to_task(TASK_RRC_NRUE, ctxt_pP->instance, message_p); - LOG_I(NR_RRC, " Send NAS_UPLINK_DATA_REQ message\n"); - } -#else - MessageDef *msg_p; - msg_p = itti_alloc_new_message(TASK_RRC_NRUE, 0, NAS_DOWNLINK_DATA_IND); - NAS_DOWNLINK_DATA_IND(msg_p).UEid = ctxt_pP->module_id; // TODO set the UEid to something else ? - NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length = pdu_length; - NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data = pdu_buffer; - itti_send_msg_to_task(TASK_NAS_UE, ctxt_pP->instance, msg_p); -#endif - } - } - - break; - case NR_DL_DCCH_MessageType__c1_PR_mobilityFromNRCommand: - case NR_DL_DCCH_MessageType__c1_PR_dlDedicatedMessageSegment_r16: - case NR_DL_DCCH_MessageType__c1_PR_ueInformationRequest_r16: - case NR_DL_DCCH_MessageType__c1_PR_dlInformationTransferMRDC_r16: - case NR_DL_DCCH_MessageType__c1_PR_loggedMeasurementConfiguration_r16: - case NR_DL_DCCH_MessageType__c1_PR_spare3: - case NR_DL_DCCH_MessageType__c1_PR_spare2: - case NR_DL_DCCH_MessageType__c1_PR_spare1: - case NR_DL_DCCH_MessageType__c1_PR_counterCheck: - break; - case NR_DL_DCCH_MessageType__c1_PR_securityModeCommand: - LOG_I(NR_RRC, "[UE %d] Received securityModeCommand (gNB %d)\n", - ctxt_pP->module_id, gNB_indexP); - nr_rrc_ue_process_securityModeCommand( - ctxt_pP, - dl_dcch_msg->message.choice.c1->choice.securityModeCommand, - gNB_indexP); - - break; - } - } - return 0; -} - -//----------------------------------------------------------------------------- -void *rrc_nrue_task( void *args_p ) { - MessageDef *msg_p; - instance_t instance; - unsigned int ue_mod_id; - int result; - NR_SRB_INFO *srb_info_p; - protocol_ctxt_t ctxt; - itti_mark_task_ready (TASK_RRC_NRUE); - - while(1) { - // Wait for a message - itti_receive_msg (TASK_RRC_NRUE, &msg_p); - instance = ITTI_MSG_DESTINATION_INSTANCE (msg_p); - ue_mod_id = UE_INSTANCE_TO_MODULE_ID(instance); - - switch (ITTI_MSG_ID(msg_p)) { - case TERMINATE_MESSAGE: - LOG_W(NR_RRC, " *** Exiting RRC thread\n"); - itti_exit_task (); - break; - - case MESSAGE_TEST: - LOG_D(NR_RRC, "[UE %d] Received %s\n", ue_mod_id, ITTI_MSG_NAME (msg_p)); - break; - - case NR_RRC_MAC_BCCH_DATA_IND: - LOG_D(NR_RRC, "[UE %d] Received %s: frameP %d, gNB %d\n", ue_mod_id, ITTI_MSG_NAME (msg_p), - NR_RRC_MAC_BCCH_DATA_IND (msg_p).frame, NR_RRC_MAC_BCCH_DATA_IND (msg_p).gnb_index); - PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, ue_mod_id, GNB_FLAG_NO, NOT_A_RNTI, NR_RRC_MAC_BCCH_DATA_IND (msg_p).frame, 0,NR_RRC_MAC_BCCH_DATA_IND (msg_p).gnb_index); - nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message (ctxt.module_id, - NR_RRC_MAC_BCCH_DATA_IND (msg_p).gnb_index, - NR_RRC_MAC_BCCH_DATA_IND (msg_p).sdu, - NR_RRC_MAC_BCCH_DATA_IND (msg_p).sdu_size, - NR_RRC_MAC_BCCH_DATA_IND (msg_p).rsrq, - NR_RRC_MAC_BCCH_DATA_IND (msg_p).rsrp); - break; - - case NR_RRC_MAC_CCCH_DATA_IND: - LOG_D(NR_RRC, "[UE %d] RNTI %x Received %s: frameP %d, gNB %d\n", - ue_mod_id, - NR_RRC_MAC_CCCH_DATA_IND (msg_p).rnti, - ITTI_MSG_NAME (msg_p), - NR_RRC_MAC_CCCH_DATA_IND (msg_p).frame, - NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index); - srb_info_p = &NR_UE_rrc_inst[ue_mod_id].Srb0[NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index]; - memcpy (srb_info_p->Rx_buffer.Payload, NR_RRC_MAC_CCCH_DATA_IND (msg_p).sdu, - NR_RRC_MAC_CCCH_DATA_IND (msg_p).sdu_size); - srb_info_p->Rx_buffer.payload_size = NR_RRC_MAC_CCCH_DATA_IND (msg_p).sdu_size; - PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_NO, NR_RRC_MAC_CCCH_DATA_IND (msg_p).rnti, NR_RRC_MAC_CCCH_DATA_IND (msg_p).frame, 0); - // PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, ue_mod_id, GNB_FLAG_NO, NR_RRC_MAC_CCCH_DATA_IND (msg_p).rnti, NR_RRC_MAC_CCCH_DATA_IND (msg_p).frame, 0, NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index); - nr_rrc_ue_decode_ccch (&ctxt, - srb_info_p, - NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index); - break; + case NR_RRC_MAC_CCCH_DATA_IND: + LOG_D(NR_RRC, "[UE %d] RNTI %x Received %s: frameP %d, gNB %d\n", + ue_mod_id, + NR_RRC_MAC_CCCH_DATA_IND (msg_p).rnti, + ITTI_MSG_NAME (msg_p), + NR_RRC_MAC_CCCH_DATA_IND (msg_p).frame, + NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index); + srb_info_p = &NR_UE_rrc_inst[ue_mod_id].Srb0[NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index]; + memcpy (srb_info_p->Rx_buffer.Payload, NR_RRC_MAC_CCCH_DATA_IND (msg_p).sdu, + NR_RRC_MAC_CCCH_DATA_IND (msg_p).sdu_size); + srb_info_p->Rx_buffer.payload_size = NR_RRC_MAC_CCCH_DATA_IND (msg_p).sdu_size; + PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_NO, NR_RRC_MAC_CCCH_DATA_IND (msg_p).rnti, NR_RRC_MAC_CCCH_DATA_IND (msg_p).frame, 0); + // PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, ue_mod_id, GNB_FLAG_NO, NR_RRC_MAC_CCCH_DATA_IND (msg_p).rnti, NR_RRC_MAC_CCCH_DATA_IND (msg_p).frame, 0, NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index); + nr_rrc_ue_decode_ccch (&ctxt, + srb_info_p, + NR_RRC_MAC_CCCH_DATA_IND (msg_p).gnb_index); + break; /* PDCP messages */ case NR_RRC_DCCH_DATA_IND: diff --git a/openair2/RRC/NR_UE/rrc_defs.h b/openair2/RRC/NR_UE/rrc_defs.h index f4ce474657..e417b9f0b4 100644 --- a/openair2/RRC/NR_UE/rrc_defs.h +++ b/openair2/RRC/NR_UE/rrc_defs.h @@ -105,6 +105,9 @@ typedef struct NR_UE_RRC_INST_s { NR_MeasConfig_t *meas_config; NR_CellGroupConfig_t *cell_group_config; + NR_CellGroupConfig_t *scell_group_config; + NR_ServingCellConfigCommonSIB_t *servingCellConfigCommonSIB; + NR_RadioBearerConfig_t *radio_bearer_config; NR_MeasObjectToAddMod_t *MeasObj[NB_CNX_UE][MAX_MEAS_OBJ]; @@ -123,7 +126,7 @@ typedef struct NR_UE_RRC_INST_s { NR_SRB_INFO_TABLE_ENTRY Srb2[NB_CNX_UE]; uint8_t MBMS_flag; - OAI_NR_UECapability_t *UECap; + OAI_NR_UECapability_t *UECap; uint8_t *UECapability; uint8_t UECapability_size; @@ -209,7 +212,7 @@ typedef struct NR_UE_RRC_INST_s { long selected_plmn_identity; Rrc_State_NR_t nrRrcState; Rrc_Sub_State_NR_t nrRrcSubState; - as_nas_info_t initialNasMsg; + as_nas_info_t initialNasMsg; } NR_UE_RRC_INST_t; #endif diff --git a/openair2/SIMULATION/NR_RRC/itti_sim.c b/openair2/SIMULATION/NR_RRC/itti_sim.c index 82906b45f8..0cd022217d 100644 --- a/openair2/SIMULATION/NR_RRC/itti_sim.c +++ b/openair2/SIMULATION/NR_RRC/itti_sim.c @@ -83,6 +83,9 @@ unsigned short config_frames[4] = {2,9,11,13}; #include "openair3/NAS/UE/nas_ue_task.h" #include #include +#if ITTI_SIM +#include "nr_nas_msg_sim.h" +#endif pthread_cond_t nfapi_sync_cond; pthread_mutex_t nfapi_sync_mutex; @@ -412,6 +415,12 @@ int create_tasks_nrue(uint32_t ue_nb) { LOG_E(NR_RRC, "Create task for RRC UE failed\n"); return -1; } + + LOG_D(NR_RRC,"create TASK_NAS_NRUE\n"); + if (itti_create_task (TASK_NAS_NRUE, nas_nrue_task, NULL) < 0) { + LOG_E(NR_RRC, "Create task for NAS UE failed\n"); + return -1; + } } @@ -579,8 +588,14 @@ int main( int argc, char **argv ) init_pdcp(); if (RC.nb_nr_inst > 0) { + nr_read_config_and_init(); // don't create if node doesn't connect to RRC/S1/GTP AssertFatal(create_gNB_tasks(1) == 0,"cannot create ITTI tasks\n"); + for (int gnb_id = 0; gnb_id < RC.nb_nr_inst; gnb_id++) { + MessageDef *msg_p = itti_alloc_new_message (TASK_GNB_APP, 0, NRRRC_CONFIGURATION_REQ); + NRRRC_CONFIGURATION_REQ(msg_p) = RC.nrrrc[gnb_id]->configuration; + itti_send_msg_to_task (TASK_RRC_GNB, GNB_MODULE_ID_TO_INSTANCE(gnb_id), msg_p); + } } else { printf("No ITTI, Initializing L1\n"); return 0; diff --git a/openair3/GTPV1-U/gtpv1u_gNB.c b/openair3/GTPV1-U/gtpv1u_gNB.c index 19d2440a95..d3f26aeff1 100644 --- a/openair3/GTPV1-U/gtpv1u_gNB.c +++ b/openair3/GTPV1-U/gtpv1u_gNB.c @@ -52,6 +52,9 @@ #undef GTP_DUMP_SOCKET +#undef GTPV1U_BEARER_OFFSET +#define GTPV1U_BEARER_OFFSET 1 + extern unsigned char NB_eNB_INST; extern RAN_CONTEXT_t RC; @@ -309,6 +312,106 @@ void *gtpv1u_gNB_task(void *args) { return NULL; } +/* Callback called when a gtpv1u message arrived on UDP interface */ +NwGtpv1uRcT nr_gtpv1u_gNB_process_stack_req( + NwGtpv1uUlpHandleT hUlp, + NwGtpv1uUlpApiT *pUlpApi) { + boolean_t result = FALSE; + teid_t teid = 0; + hashtable_rc_t hash_rc = HASH_TABLE_KEY_NOT_EXISTS; + nr_gtpv1u_teid_data_t *gtpv1u_teid_data_p = NULL; + protocol_ctxt_t ctxt; + NwGtpv1uRcT rc; + + switch(pUlpApi->apiType) { + /* Here there are two type of messages handled: + * - T-PDU + * - END-MARKER + */ + case NW_GTPV1U_ULP_API_RECV_TPDU: { + uint8_t buffer[4096]; + uint32_t buffer_len; + //uint16_t msgType = NW_GTP_GPDU; + //NwGtpv1uMsgT *pMsg = NULL; + /* Nw-gptv1u stack has processed a PDU. we can schedule it to PDCP + * for transmission. + */ + teid = pUlpApi->apiInfo.recvMsgInfo.teid; + //pMsg = (NwGtpv1uMsgT *) pUlpApi->apiInfo.recvMsgInfo.hMsg; + //msgType = pMsg->msgType; + + if (NW_GTPV1U_OK != nwGtpv1uMsgGetTpdu(pUlpApi->apiInfo.recvMsgInfo.hMsg, + buffer, &buffer_len)) { + LOG_E(GTPU, "Error while retrieving T-PDU"); + } + + itti_free(TASK_UDP, ((NwGtpv1uMsgT *)pUlpApi->apiInfo.recvMsgInfo.hMsg)->msgBuf); +#if defined(GTP_DUMP_SOCKET) && GTP_DUMP_SOCKET > 0 + gtpv1u_eNB_write_dump_socket(buffer,buffer_len); +#endif + rc = nwGtpv1uMsgDelete(RC.nr_gtpv1u_data_g->gtpv1u_stack, + pUlpApi->apiInfo.recvMsgInfo.hMsg); + + if (rc != NW_GTPV1U_OK) { + LOG_E(GTPU, "nwGtpv1uMsgDelete failed: 0x%x\n", rc); + } + + hash_rc = hashtable_get(RC.nr_gtpv1u_data_g->teid_mapping, teid, (void **)>pv1u_teid_data_p); + + if (hash_rc == HASH_TABLE_OK) { +// #if defined(LOG_GTPU) && LOG_GTPU > 0 + LOG_D(GTPU, "Received T-PDU from gtpv1u stack teid %u size %d -> gnb module id %u ue module id %u pdu session id %u\n", + teid, + buffer_len, + gtpv1u_teid_data_p->gnb_id, + gtpv1u_teid_data_p->ue_id, + gtpv1u_teid_data_p->pdu_session_id); +// #endif + //warning "LG eps bearer mapping to DRB id to do (offset -4)" + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, gtpv1u_teid_data_p->gnb_id, GNB_FLAG_YES, gtpv1u_teid_data_p->ue_id, 0, 0,gtpv1u_teid_data_p->gnb_id); + // MSC_LOG_TX_MESSAGE( + // MSC_GTPU_ENB, + // MSC_PDCP_ENB, + // NULL,0, + // MSC_AS_TIME_FMT" DATA-REQ rb %u size %u", + // 0,0, + // (gtpv1u_teid_data_p->eps_bearer_id) ? gtpv1u_teid_data_p->eps_bearer_id - 4: 5-4, + // buffer_len); + + result = pdcp_data_req( + &ctxt, + SRB_FLAG_NO, + 1, + 0, // mui + SDU_CONFIRM_NO, // confirm + buffer_len, + buffer, + PDCP_TRANSMISSION_MODE_DATA,NULL, NULL + ); + + if ( result == FALSE ) { + if (ctxt.configured == FALSE ) + LOG_W(GTPU, "gNB node PDCP data request failed, cause: [UE:%x]RB is not configured!\n", ctxt.rnti) ; + else + LOG_W(GTPU, "PDCP data request failed\n"); + + return NW_GTPV1U_FAILURE; + } + } else { + LOG_W(GTPU, "Received T-PDU from gtpv1u stack teid %u unknown size %u", teid, buffer_len); + } + } + break; + + default: { + LOG_E(GTPU, "Received undefined UlpApi (%02x) from gtpv1u stack!\n", + pUlpApi->apiType); + } + } // end of switch + + return NW_GTPV1U_OK; +} + int nr_gtpv1u_gNB_init(void) { NwGtpv1uRcT rc = NW_GTPV1U_FAILURE; NwGtpv1uUlpEntityT ulp; @@ -344,7 +447,7 @@ int nr_gtpv1u_gNB_init(void) { /* Set the ULP API callback. Called once message have been processed by the * nw-gtpv1u stack. */ - ulp.ulpReqCallback = gtpv1u_gNB_process_stack_req; + ulp.ulpReqCallback = nr_gtpv1u_gNB_process_stack_req; memset((void *)&(ulp.hUlp), 0, sizeof(NwGtpv1uUlpHandleT)); if ((rc = nwGtpv1uSetUlpEntity(RC.nr_gtpv1u_data_g->gtpv1u_stack, &ulp)) != NW_GTPV1U_OK) { @@ -751,6 +854,81 @@ static int gtpv1u_ng_req( return 0; } +static int gtpv1u_gnb_tunnel_data_req(gtpv1u_gnb_tunnel_data_req_t *gnb_tunnel_data_req) { + gtpv1u_gnb_tunnel_data_req_t *data_req_p = NULL; + NwGtpv1uUlpApiT stack_req; + NwGtpv1uRcT rc = NW_GTPV1U_FAILURE; + hashtable_rc_t hash_rc = HASH_TABLE_KEY_NOT_EXISTS; + nr_gtpv1u_ue_data_t *gtpv1u_ue_data_p = NULL; + teid_t gnb_ngu_teid = 0; + teid_t upf_ngu_teid = 0; + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GTPV1U_PROCESS_TUNNEL_DATA_REQ, VCD_FUNCTION_IN); + data_req_p = gnb_tunnel_data_req; + + memset(&stack_req, 0, sizeof(NwGtpv1uUlpApiT)); + hash_rc = hashtable_get(RC.nr_gtpv1u_data_g->ue_mapping, (uint64_t)data_req_p->rnti, (void **)>pv1u_ue_data_p); + + if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS) { + LOG_E(GTPU, "nwGtpv1uProcessUlpReq failed: while getting ue rnti %x in hashtable ue_mapping\n", data_req_p->rnti); + } else { + if ((data_req_p->pdusession_id >= GTPV1U_BEARER_OFFSET) && (data_req_p->pdusession_id < max_val_NR_DRB_Identity)) { + gnb_ngu_teid = gtpv1u_ue_data_p->bearers[data_req_p->pdusession_id - GTPV1U_BEARER_OFFSET].teid_gNB; + upf_ngu_teid = gtpv1u_ue_data_p->bearers[data_req_p->pdusession_id - GTPV1U_BEARER_OFFSET].teid_upf; + stack_req.apiType = NW_GTPV1U_ULP_API_SEND_TPDU; + stack_req.apiInfo.sendtoInfo.teid = upf_ngu_teid; + stack_req.apiInfo.sendtoInfo.ipAddr = gtpv1u_ue_data_p->bearers[data_req_p->pdusession_id - GTPV1U_BEARER_OFFSET].upf_ip_addr; + rc = nwGtpv1uGpduMsgNew( + RC.nr_gtpv1u_data_g->gtpv1u_stack, + upf_ngu_teid, + NW_FALSE, + RC.nr_gtpv1u_data_g->seq_num++, + data_req_p->buffer, + data_req_p->length, + data_req_p->offset, + &(stack_req.apiInfo.sendtoInfo.hMsg)); + + if (rc != NW_GTPV1U_OK) { + LOG_E(GTPU, "nwGtpv1uGpduMsgNew failed: 0x%x\n", rc); + MSC_LOG_EVENT(MSC_GTPU_GNB,"0 Failed send G-PDU ltid %u rtid %u size %u", + gnb_ngu_teid,upf_ngu_teid,data_req_p->length); + (void)gnb_ngu_teid; /* avoid gcc warning "set but not used" */ + } else { + rc = nwGtpv1uProcessUlpReq(RC.nr_gtpv1u_data_g->gtpv1u_stack, &stack_req); + + if (rc != NW_GTPV1U_OK) { + LOG_E(GTPU, "nwGtpv1uProcessUlpReq failed: 0x%x\n", rc); + MSC_LOG_EVENT(MSC_GTPU_GNB,"0 Failed send G-PDU ltid %u rtid %u size %u", + gnb_ngu_teid,upf_ngu_teid,data_req_p->length); + } else { + MSC_LOG_TX_MESSAGE( + MSC_GTPU_GNB, + MSC_GTPU_SGW, + NULL, + 0, + MSC_AS_TIME_FMT" G-PDU ltid %u rtid %u size %u", + 0,0, + gnb_ngu_teid, + upf_ngu_teid, + data_req_p->length); + } + + rc = nwGtpv1uMsgDelete(RC.nr_gtpv1u_data_g->gtpv1u_stack, + stack_req.apiInfo.sendtoInfo.hMsg); + + if (rc != NW_GTPV1U_OK) { + LOG_E(GTPU, "nwGtpv1uMsgDelete failed: 0x%x\n", rc); + } + } + } + } + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GTPV1U_PROCESS_TUNNEL_DATA_REQ, VCD_FUNCTION_OUT); + /* Buffer still needed, do not free it */ + //itti_free(ITTI_MSG_ORIGIN_ID(received_message_p), data_req_p->buffer); + + return 0; +} + //----------------------------------------------------------------------------- void *gtpv1u_gNB_process_itti_msg(void *notUsed) { /* Trying to fetch a message from the message queue. @@ -774,6 +952,24 @@ void *gtpv1u_gNB_process_itti_msg(void *notUsed) { gtpv1u_delete_ngu_tunnel(instance, &received_message_p->ittiMsg.NRGtpv1uDeleteTunnelReq); break; + // DATA COMING FROM UDP + case UDP_DATA_IND: { + udp_data_ind_t *udp_data_ind_p; + udp_data_ind_p = &received_message_p->ittiMsg.udp_data_ind; + nwGtpv1uProcessUdpReq(RC.nr_gtpv1u_data_g->gtpv1u_stack, + udp_data_ind_p->buffer, + udp_data_ind_p->buffer_length, + udp_data_ind_p->peer_port, + udp_data_ind_p->peer_address); + } + break; + + // DATA TO BE SENT TO UDP + case GTPV1U_GNB_TUNNEL_DATA_REQ: + LOG_I(GTPU, "Received message %s\n", ITTI_MSG_NAME(received_message_p)); + gtpv1u_gnb_tunnel_data_req(>PV1U_GNB_TUNNEL_DATA_REQ(received_message_p)); + break; + case TERMINATE_MESSAGE: { if (RC.nr_gtpv1u_data_g->ue_mapping != NULL) { hashtable_destroy (&(RC.nr_gtpv1u_data_g->ue_mapping)); diff --git a/openair3/GTPV1-U/nw-gtpv1u/src/NwGtpv1u.c b/openair3/GTPV1-U/nw-gtpv1u/src/NwGtpv1u.c index 0c7663921b..0d962cad7d 100644 --- a/openair3/GTPV1-U/nw-gtpv1u/src/NwGtpv1u.c +++ b/openair3/GTPV1-U/nw-gtpv1u/src/NwGtpv1u.c @@ -59,6 +59,7 @@ extern "C" { #endif +//#define LOG_GTPU 1 /*--------------------------------------------------------------------------* * P R I V A T E F U N C T I O N S * *--------------------------------------------------------------------------*/ @@ -900,7 +901,7 @@ nwGtpv1uProcessUdpReq( NW_IN NwGtpv1uStackHandleT hGtpuStackHandle, case NW_GTP_END_MARKER: #if defined(LOG_GTPU) && LOG_GTPU > 0 for(int i =1; i<= udpDataLen; i++){ - printf("%02x ", udpData[i-1]); + printf("%02x ", (uint8_t)udpData[i-1]); if(i % 20 == 0)printf("\n"); } #endif diff --git a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.c b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.c index bc4d34d650..1c9198b720 100644 --- a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.c +++ b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.c @@ -54,6 +54,8 @@ int encode_pdu_session_establishment_request(pdu_session_establishment_request_m encoded++; IES_ENCODE_U16(buffer, encoded, pdusessionestablishrequest->maxdatarate); + *(buffer + encoded) = pdusessionestablishrequest->pdusessiontype; + encoded++; return encoded; } diff --git a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.h b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.h index ac5b250f66..e2742ebc38 100644 --- a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.h +++ b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishRequest.h @@ -53,6 +53,7 @@ typedef struct pdu_session_establishment_request_msg_tag { uint8_t pti; MessageType pdusessionestblishmsgtype; uint16_t maxdatarate; + uint8_t pdusessiontype; /* Optional fields */ } pdu_session_establishment_request_msg; diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 2c07de8dd5..55e21390bf 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -38,12 +38,21 @@ #include "aka_functions.h" #include "secu_defs.h" #include "PduSessionEstablishRequest.h" -char netName[] = "5G:mnc093.mcc208.3gppnetwork.org"; +# include "intertask_interface.h" + +/*char netName[] = "5G:mnc093.mcc208.3gppnetwork.org"; char imsi[] = "2089300007487"; // USIM_API_K: 5122250214c33e723a5dd523fc145fc0 uint8_t k[16] = {0x51, 0x22, 0x25, 0x02, 0x14,0xc3, 0x3e, 0x72, 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0}; // OPC: 981d464c7c52eb6e5036234984ad0bcf -const uint8_t opc[16] = {0x98, 0x1d, 0x46, 0x4c,0x7c,0x52,0xeb, 0x6e, 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf}; +const uint8_t opc[16] = {0x98, 0x1d, 0x46, 0x4c,0x7c,0x52,0xeb, 0x6e, 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf};*/ + +char netName[] = "5G:mnc099.mcc208.3gppnetwork.org"; +char imsi[] = "2089900007487"; //"208990100001100"; +// USIM_API_K: fe c8 6b a6 eb 70 7e d0 89 05 75 7b 1b b4 4b 8f +uint8_t k[16] = {0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f}; +// OPC: c4 24 49 36 3b ba d0 2b 66 d1 6b c9 75 d7 7c c1 +const uint8_t opc[16] = {0xc4, 0x24, 0x49, 0x36, 0x3b, 0xba, 0xd0, 0x2b, 0x66, 0xd1, 0x6b, 0xc9, 0x75, 0xd7, 0x7c, 0xc1}; uint8_t *registration_request_buf; uint32_t registration_request_len; @@ -273,14 +282,14 @@ void generateRegistrationRequest(as_nas_info_t *initialNasMsg) { mm_msg->registration_request.fgsregistrationtype = INITIAL_REGISTRATION; mm_msg->registration_request.naskeysetidentifier.naskeysetidentifier = 1; size += 1; - if(1){ + if(0){ mm_msg->registration_request.fgsmobileidentity.guti.typeofidentity = FGS_MOBILE_IDENTITY_5G_GUTI; mm_msg->registration_request.fgsmobileidentity.guti.amfregionid = 0xca; mm_msg->registration_request.fgsmobileidentity.guti.amfpointer = 0; mm_msg->registration_request.fgsmobileidentity.guti.amfsetid = 1016; mm_msg->registration_request.fgsmobileidentity.guti.tmsi = 10; mm_msg->registration_request.fgsmobileidentity.guti.mncdigit1 = 9; - mm_msg->registration_request.fgsmobileidentity.guti.mncdigit2 = 3; + mm_msg->registration_request.fgsmobileidentity.guti.mncdigit2 = 9; mm_msg->registration_request.fgsmobileidentity.guti.mncdigit3 = 0xf; mm_msg->registration_request.fgsmobileidentity.guti.mccdigit1 = 2; mm_msg->registration_request.fgsmobileidentity.guti.mccdigit2 = 0; @@ -291,7 +300,7 @@ void generateRegistrationRequest(as_nas_info_t *initialNasMsg) { } else { mm_msg->registration_request.fgsmobileidentity.suci.typeofidentity = FGS_MOBILE_IDENTITY_SUCI; mm_msg->registration_request.fgsmobileidentity.suci.mncdigit1 = 9; - mm_msg->registration_request.fgsmobileidentity.suci.mncdigit2 = 3; + mm_msg->registration_request.fgsmobileidentity.suci.mncdigit2 = 9; mm_msg->registration_request.fgsmobileidentity.suci.mncdigit3 = 0xf; mm_msg->registration_request.fgsmobileidentity.suci.mccdigit1 = 2; mm_msg->registration_request.fgsmobileidentity.suci.mccdigit2 = 0; @@ -348,7 +357,7 @@ void generateIdentityResponse(as_nas_info_t *initialNasMsg, uint8_t identitytype if(identitytype == FGS_MOBILE_IDENTITY_SUCI){ mm_msg->fgs_identity_response.fgsmobileidentity.suci.typeofidentity = FGS_MOBILE_IDENTITY_SUCI; mm_msg->fgs_identity_response.fgsmobileidentity.suci.mncdigit1 = 9; - mm_msg->fgs_identity_response.fgsmobileidentity.suci.mncdigit2 = 3; + mm_msg->fgs_identity_response.fgsmobileidentity.suci.mncdigit2 = 9; mm_msg->fgs_identity_response.fgsmobileidentity.suci.mncdigit3 = 0xf; mm_msg->fgs_identity_response.fgsmobileidentity.suci.mccdigit1 = 2; mm_msg->fgs_identity_response.fgsmobileidentity.suci.mccdigit2 = 0; @@ -612,14 +621,15 @@ void generatePduSessionEstablishRequest(as_nas_info_t *initialNasMsg){ memset(&nas_msg, 0, sizeof(fgs_nas_message_t)); // setup pdu session establishment request - uint16_t req_length = 6; + uint16_t req_length = 7; uint8_t *req_buffer = malloc(req_length); pdu_session_establishment_request_msg pdu_session_establish; pdu_session_establish.protocoldiscriminator = FGS_SESSION_MANAGEMENT_MESSAGE; pdu_session_establish.pdusessionid = 10; - pdu_session_establish.pti = 0; + pdu_session_establish.pti = 1; pdu_session_establish.pdusessionestblishmsgtype = FGS_PDU_SESSION_ESTABLISHMENT_REQ; pdu_session_establish.maxdatarate = 0xffff; + pdu_session_establish.pdusessiontype = 0x91; encode_pdu_session_establishment_request(&pdu_session_establish, req_buffer); @@ -627,9 +637,8 @@ void generatePduSessionEstablishRequest(as_nas_info_t *initialNasMsg){ MM_msg *mm_msg; nas_stream_cipher_t stream_cipher; uint8_t mac[4]; - uint8_t nssai[]={1,1,2,3}; - uint8_t dnn[9]={0x8,0x69,0x6e,0x74,0x65,0x72,0x6e,0x65,0x74}; - // set security protected header + uint8_t nssai[]={1,0,0,1}; //Corresponding to SST:1, SD:1 + uint8_t dnn[4]={0x4,0x6f,0x61,0x69}; //Corresponding to dnn:"oai" nas_msg.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; nas_msg.header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED_WITH_NEW_SECU_CTX; size += 7; @@ -661,9 +670,9 @@ void generatePduSessionEstablishRequest(as_nas_info_t *initialNasMsg){ mm_msg->uplink_nas_transport.snssai.length = 4; mm_msg->uplink_nas_transport.snssai.value = nssai; size += (1+1+4); - mm_msg->uplink_nas_transport.dnn.length = 9; + mm_msg->uplink_nas_transport.dnn.length = 4; mm_msg->uplink_nas_transport.dnn.value = dnn; - size += (1+1+9); + size += (1+1+4); // encode the message initialNasMsg->data = (Byte_t *)malloc(size * sizeof(Byte_t)); @@ -690,3 +699,199 @@ void generatePduSessionEstablishRequest(as_nas_info_t *initialNasMsg){ initialNasMsg->data[2+i] = mac[i]; } } + +void *nas_nrue_task(void *args_p) +{ + MessageDef *msg_p; + instance_t instance; + unsigned int Mod_id; + int result; + uint8_t msg_type = 0; + uint8_t *pdu_buffer = NULL; + + itti_mark_task_ready (TASK_NAS_NRUE); + MSC_START_USE(); + + while(1) { + // Wait for a message or an event + itti_receive_msg (TASK_NAS_NRUE, &msg_p); + + if (msg_p != NULL) { + instance = msg_p->ittiMsgHeader.originInstance; + Mod_id = instance ; + if (instance == INSTANCE_DEFAULT) { + printf("%s:%d: FATAL: instance is INSTANCE_DEFAULT, should not happen.\n", + __FILE__, __LINE__); + exit_fun("exit... \n"); + } + + switch (ITTI_MSG_ID(msg_p)) { + case INITIALIZE_MESSAGE: + LOG_I(NAS, "[UE %d] Received %s\n", Mod_id, ITTI_MSG_NAME (msg_p)); + + break; + + case TERMINATE_MESSAGE: + itti_exit_task (); + break; + + case MESSAGE_TEST: + LOG_I(NAS, "[UE %d] Received %s\n", Mod_id, ITTI_MSG_NAME (msg_p)); + break; + + case NAS_CELL_SELECTION_CNF: + LOG_I(NAS, "[UE %d] Received %s: errCode %u, cellID %u, tac %u\n", Mod_id, ITTI_MSG_NAME (msg_p), + NAS_CELL_SELECTION_CNF (msg_p).errCode, NAS_CELL_SELECTION_CNF (msg_p).cellID, NAS_CELL_SELECTION_CNF (msg_p).tac); + // as_stmsi_t s_tmsi={0, 0}; + // as_nas_info_t nas_info; + // plmn_t plmnID={0, 0, 0, 0}; + // generateRegistrationRequest(&nas_info); + // nr_nas_itti_nas_establish_req(0, AS_TYPE_ORIGINATING_SIGNAL, s_tmsi, plmnID, nas_info.data, nas_info.length, 0); + break; + + case NAS_CELL_SELECTION_IND: + LOG_I(NAS, "[UE %d] Received %s: cellID %u, tac %u\n", Mod_id, ITTI_MSG_NAME (msg_p), + NAS_CELL_SELECTION_IND (msg_p).cellID, NAS_CELL_SELECTION_IND (msg_p).tac); + + /* TODO not processed by NAS currently */ + break; + + case NAS_PAGING_IND: + LOG_I(NAS, "[UE %d] Received %s: cause %u\n", Mod_id, ITTI_MSG_NAME (msg_p), + NAS_PAGING_IND (msg_p).cause); + + /* TODO not processed by NAS currently */ + break; + + case NAS_CONN_ESTABLI_CNF: + { + LOG_I(NAS, "[UE %d] Received %s: errCode %u, length %u\n", Mod_id, ITTI_MSG_NAME (msg_p), + NAS_CONN_ESTABLI_CNF (msg_p).errCode, NAS_CONN_ESTABLI_CNF (msg_p).nasMsg.length); + + pdu_buffer = NAS_CONN_ESTABLI_CNF (msg_p).nasMsg.data; + if((pdu_buffer + 1) != NULL){ + if (*(pdu_buffer + 1) > 0 ) { + if((pdu_buffer + 9) != NULL){ + msg_type = *(pdu_buffer + 9); + } else { + LOG_W(NAS, "[UE] Received invalid downlink message\n"); + break; + } + } else { + if((pdu_buffer + 2) != NULL){ + msg_type = *(pdu_buffer + 2); + } else { + LOG_W(NAS, "[UE] Received invalid downlink message\n"); + break; + } + } + } + if(msg_type == REGISTRATION_ACCEPT){ + LOG_I(NAS, "[UE] Received REGISTRATION ACCEPT message\n"); + + as_nas_info_t initialNasMsg; + memset(&initialNasMsg, 0, sizeof(as_nas_info_t)); + generateRegistrationComplete(&initialNasMsg, NULL); + if(initialNasMsg.length > 0){ + MessageDef *message_p; + message_p = itti_alloc_new_message(TASK_NAS_NRUE, 0, NAS_UPLINK_DATA_REQ); + NAS_UPLINK_DATA_REQ(message_p).UEid = Mod_id; + NAS_UPLINK_DATA_REQ(message_p).nasMsg.data = (uint8_t *)initialNasMsg.data; + NAS_UPLINK_DATA_REQ(message_p).nasMsg.length = initialNasMsg.length; + itti_send_msg_to_task(TASK_RRC_NRUE, instance, message_p); + LOG_I(NAS, "Send NAS_UPLINK_DATA_REQ message(RegistrationComplete)\n"); + } + + as_nas_info_t pduEstablishMsg; + memset(&pduEstablishMsg, 0, sizeof(as_nas_info_t)); + generatePduSessionEstablishRequest(&pduEstablishMsg); + if(pduEstablishMsg.length > 0){ + MessageDef *message_p; + message_p = itti_alloc_new_message(TASK_NAS_NRUE, 0, NAS_UPLINK_DATA_REQ); + NAS_UPLINK_DATA_REQ(message_p).UEid = Mod_id; + NAS_UPLINK_DATA_REQ(message_p).nasMsg.data = (uint8_t *)pduEstablishMsg.data; + NAS_UPLINK_DATA_REQ(message_p).nasMsg.length = pduEstablishMsg.length; + itti_send_msg_to_task(TASK_RRC_NRUE, instance, message_p); + LOG_I(NAS, "Send NAS_UPLINK_DATA_REQ message(PduSessionEstablishRequest)\n"); + } + } + + break; + } + + case NAS_CONN_RELEASE_IND: + LOG_I(NAS, "[UE %d] Received %s: cause %u\n", Mod_id, ITTI_MSG_NAME (msg_p), + NAS_CONN_RELEASE_IND (msg_p).cause); + + break; + + case NAS_UPLINK_DATA_CNF: + LOG_I(NAS, "[UE %d] Received %s: UEid %u, errCode %u\n", Mod_id, ITTI_MSG_NAME (msg_p), + NAS_UPLINK_DATA_CNF (msg_p).UEid, NAS_UPLINK_DATA_CNF (msg_p).errCode); + + break; + + case NAS_DOWNLINK_DATA_IND: + { + LOG_I(NAS, "[UE %d] Received %s: UEid %u, length %u , buffer %p\n", Mod_id, + ITTI_MSG_NAME (msg_p), + Mod_id, + NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length, + NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data); + as_nas_info_t initialNasMsg; + memset(&initialNasMsg, 0, sizeof(as_nas_info_t)); + + pdu_buffer = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data; + if((pdu_buffer + 1) != NULL){ + if (*(pdu_buffer + 1) > 0 ) { + msg_type = *(pdu_buffer + 9); + } else { + msg_type = *(pdu_buffer + 2); + } + } + if((pdu_buffer + 2) == NULL){ + LOG_W(NAS, "[UE] Received invalid downlink message\n"); + return 0; + } + + switch(msg_type){ + case FGS_IDENTITY_REQUEST: + generateIdentityResponse(&initialNasMsg,*(pdu_buffer+3)); + break; + case FGS_AUTHENTICATION_REQUEST: + generateAuthenticationResp(&initialNasMsg, pdu_buffer); + break; + case FGS_SECURITY_MODE_COMMAND: + generateSecurityModeComplete(&initialNasMsg); + break; + default: + LOG_W(NR_RRC,"unknow message type %d\n",msg_type); + break; + } + + if(initialNasMsg.length > 0){ + MessageDef *message_p; + message_p = itti_alloc_new_message(TASK_NAS_NRUE, 0, NAS_UPLINK_DATA_REQ); + NAS_UPLINK_DATA_REQ(message_p).UEid = Mod_id; + NAS_UPLINK_DATA_REQ(message_p).nasMsg.data = (uint8_t *)initialNasMsg.data; + NAS_UPLINK_DATA_REQ(message_p).nasMsg.length = initialNasMsg.length; + itti_send_msg_to_task(TASK_RRC_NRUE, instance, message_p); + LOG_I(NAS, "Send NAS_UPLINK_DATA_REQ message\n"); + } + + break; + } + + default: + LOG_E(NAS, "[UE %d] Received unexpected message %s\n", Mod_id, ITTI_MSG_NAME (msg_p)); + break; + } + + result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p); + AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result); + msg_p = NULL; + } + } + + return NULL; +} diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.h b/openair3/NAS/NR_UE/nr_nas_msg_sim.h index fef4671f43..2f123a6f04 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.h +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.h @@ -120,6 +120,7 @@ void generateAuthenticationResp(as_nas_info_t *initialNasMsg, uint8_t *buf); void generateSecurityModeComplete(as_nas_info_t *initialNasMsg); void generateRegistrationComplete(as_nas_info_t *initialNasMsg, SORTransparentContainer *sortransparentcontainer); void generatePduSessionEstablishRequest(as_nas_info_t *initialNasMsg); +void *nas_nrue_task(void *args_p); #endif /* __NR_NAS_MSG_SIM_H__*/ diff --git a/openair3/NGAP/ngap_common.h b/openair3/NGAP/ngap_common.h index f0302a88c7..6803adb0dd 100644 --- a/openair3/NGAP/ngap_common.h +++ b/openair3/NGAP/ngap_common.h @@ -144,7 +144,6 @@ extern int asn1_xer_print; if (ie == NULL ) { \ NGAP_ERROR("NGAP_FIND_PROTOCOLIE_BY_ID: %s %d: ie is NULL\n",__FILE__,__LINE__);\ } \ - if (mandatory) DevAssert(ie != NULL); \ } while(0) /** \brief Function callback prototype. **/ diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index f143002537..e398f8fa3e 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -1061,12 +1061,31 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_InitialContextSetupRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AllowedNSSAI, true); - if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ + //if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ NGAP_AllowedNSSAI_Item_t *allow_nssai_item_p = NULL; - NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count); - DevAssert(ie->value.choice.AllowedNSSAI.list.count > 0); - DevAssert(ie->value.choice.AllowedNSSAI.list.count <= NGAP_maxnoofAllowedS_NSSAIs); + NGAP_WARN("AllowedNSSAI.list.count %d\n", ie != NULL ? ie->value.choice.AllowedNSSAI.list.count : 2); + //NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count); + //DevAssert(ie->value.choice.AllowedNSSAI.list.count > 0); + //DevAssert(ie->value.choice.AllowedNSSAI.list.count <= NGAP_maxnoofAllowedS_NSSAIs); + + if (ie == NULL) { + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nb_allowed_nssais = 2; + + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sST = 01; + + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD_flag = 1; + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD[0] = 01; + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD[1] = 02; + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD[2] = 03; + + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sST = 01; + + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD_flag = 1; + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD[0] = 00;//11; + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD[1] = 00;//22; + NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD[2] = 01;//33; + } else { NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count; @@ -1082,9 +1101,10 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[i].sD[2] = allow_nssai_item_p->s_NSSAI.sD->buf[2]; } } - } else {/* ie != NULL */ - return -1; - } + } + //} else {/* ie != NULL */ + // return -1; + //} /* id-UESecurityCapabilities */ NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_InitialContextSetupRequestIEs_t, ie, container, diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 3711072682..6b61643588 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -932,7 +932,7 @@ int ngap_gNB_ue_capabilities(instance_t instance, /* mandatory */ ie = (NGAP_UERadioCapabilityInfoIndicationIEs_t *)calloc(1, sizeof(NGAP_UERadioCapabilityInfoIndicationIEs_t)); ie->id = NGAP_ProtocolIE_ID_id_UERadioCapability; - ie->criticality = NGAP_Criticality_reject; + ie->criticality = NGAP_Criticality_ignore; ie->value.present = NGAP_UERadioCapabilityInfoIndicationIEs__value_PR_UERadioCapability; ie->value.choice.UERadioCapability.buf = ue_cap_info_ind_p->ue_radio_cap.buffer; ie->value.choice.UERadioCapability.size = ue_cap_info_ind_p->ue_radio_cap.length; diff --git a/openair3/UDP/udp_eNB_task.c b/openair3/UDP/udp_eNB_task.c index 515dd9a17f..05729ba6b1 100644 --- a/openair3/UDP/udp_eNB_task.c +++ b/openair3/UDP/udp_eNB_task.c @@ -169,8 +169,8 @@ int udp_eNB_create_socket(int port, char *ip_addr, task_id_t task_id) if ((rc = bind(sd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in))) < 0) { close(sd); - AssertFatal(rc >= 0, "UDP: Failed to bind socket: (%s:%d) address %s port %d\n", - strerror(errno), errno, ip_addr, port); + LOG_E(UDP_, "Failed to bind socket: (%s:%d) address %s port %d\n", strerror(errno), errno, ip_addr, port); + return -1; } /* Create a new descriptor for this connection */ diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 29633ba6ae..82a1d782ec 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -62,7 +62,7 @@ typedef struct { typedef struct { rnti_t rnti; - ebi_t rb_id; + ebi_t incoming_rb_id; gtpCallback callBack; } rntiData_t; @@ -85,7 +85,7 @@ class gtpEndPoints { gtpEndPoints globGtp; - // note TEid 0 is reserved for specific usage: echo req/resp, error and supported extensions +// note TEid 0 is reserved for specific usage: echo req/resp, error and supported extensions static uint32_t gtpv1uNewTeid(void) { #ifdef GTPV1U_LINEAR_TEID_ALLOCATION g_gtpv1u_teid = g_gtpv1u_teid + 1; @@ -154,14 +154,14 @@ static int gtpv1uCreateAndSendMsg(int h, uint32_t peerIp, uint16_t peerPort, te static void gtpv1uSend(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req, bool seqNumFlag, bool npduNumFlag) { uint8_t *buffer=req->buffer+req->offset; size_t length=req->length; - uint64_t rnti=req->rnti; + rnti_t rnti=req->rnti; int rab_id=req->rab_id; pthread_mutex_lock(&globGtp.gtp_lock); auto inst=&globGtp.instances[compatInst(instance)]; auto ptrRnti=inst->ue2te_mapping.find(rnti); if ( ptrRnti==inst->ue2te_mapping.end() ) { - LOG_E(GTPU, "gtpv1uSend failed: while getting ue rnti %lx in hashtable ue_mapping\n", rnti); + LOG_E(GTPU, "gtpv1uSend failed: while getting ue rnti %x in hashtable ue_mapping\n", rnti); pthread_mutex_unlock(&globGtp.gtp_lock); return; } @@ -169,11 +169,11 @@ static void gtpv1uSend(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req, b auto ptr=ptrRnti->second.bearers; if ( ptr.find(rab_id) == ptr.end() ) { - LOG_E(GTPU,"sending a packet to a non existant RNTI:RAB: %lx/%x\n", rnti, rab_id); + LOG_E(GTPU,"sending a packet to a non existant RNTI:RAB: %x/%x\n", rnti, rab_id); pthread_mutex_unlock(&globGtp.gtp_lock); return; } else - LOG_D(GTPU,"sending a packet to RNTI:RAB:teid %lx/%x/%x, len %lu, oldseq %d, oldnum %d\n", + LOG_D(GTPU,"sending a packet to RNTI:RAB:teid %x/%x/%x, len %lu, oldseq %d, oldnum %d\n", rnti, rab_id,ptr[rab_id].teid_outgoing,length, ptr[rab_id].seqNum,ptr[rab_id].npduNum ); if(seqNumFlag) @@ -191,16 +191,18 @@ static void gtpv1uSend(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req, b tmp.teid_outgoing, buffer, length, seqNumFlag, npduNumFlag, false, tmp.seqNum, tmp.npduNum, 0) ; } - -static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req) { - uint64_t rnti=req->rnti; - int rab_id=req->rab_id; + +static void gtpv1uSend2(instance_t instance, gtpv1u_gnb_tunnel_data_req_t *req, bool seqNumFlag, bool npduNumFlag) { + uint8_t *buffer=req->buffer+req->offset; + size_t length=req->length; + rnti_t rnti=req->rnti; + int rab_id=req->pdusession_id; pthread_mutex_lock(&globGtp.gtp_lock); auto inst=&globGtp.instances[compatInst(instance)]; auto ptrRnti=inst->ue2te_mapping.find(rnti); if ( ptrRnti==inst->ue2te_mapping.end() ) { - LOG_E(GTPU, "gtpv1uSend failed: while getting ue rnti %lx in hashtable ue_mapping\n", rnti); + LOG_E(GTPU, "gtpv1uSend failed: while getting ue rnti %x in hashtable ue_mapping\n", rnti); pthread_mutex_unlock(&globGtp.gtp_lock); return; } @@ -208,15 +210,54 @@ static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_tunnel_data_req_t *r auto ptr=ptrRnti->second.bearers; if ( ptr.find(rab_id) == ptr.end() ) { - LOG_E(GTPU,"sending a packet to a non existant RNTI:RAB: %lx/%x\n", rnti, rab_id); + LOG_E(GTPU,"sending a packet to a non existant RNTI:RAB: %x/%x\n", rnti, rab_id); pthread_mutex_unlock(&globGtp.gtp_lock); return; } else - LOG_D(GTPU,"sending a end packet packet to RNTI:RAB:teid %lx/%x/%x\n", - rnti, rab_id,ptr[rab_id].teid_outgoing); + LOG_D(GTPU,"sending a packet to RNTI:RAB:teid %x/%x/%x, len %lu, oldseq %d, oldnum %d\n", + rnti, rab_id,ptr[rab_id].teid_outgoing,length, ptr[rab_id].seqNum,ptr[rab_id].npduNum ); + + if(seqNumFlag) + ptr[rab_id].seqNum++; + + if(npduNumFlag) + ptr[rab_id].npduNum++; + + // We will release the lock, let's copy data before ocp_gtpv1u_bearer_t tmp=ptr[rab_id]; pthread_mutex_unlock(&globGtp.gtp_lock); + gtpv1uCreateAndSendMsg(compatInst(instance), + tmp.outgoing_ip_addr, + tmp.outgoing_port, + tmp.teid_outgoing, + buffer, length, seqNumFlag, npduNumFlag, false, tmp.seqNum, tmp.npduNum, 0) ; +} +static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req) { + rnti_t rnti=req->rnti; + int rab_id=req->rab_id; + pthread_mutex_lock(&globGtp.gtp_lock); + auto inst=&globGtp.instances[compatInst(instance)]; + auto ptrRnti=inst->ue2te_mapping.find(rnti); + + if ( ptrRnti==inst->ue2te_mapping.end() ) { + LOG_E(GTPU, "gtpv1uSend failed: while getting ue rnti %x in hashtable ue_mapping\n", rnti); + pthread_mutex_unlock(&globGtp.gtp_lock); + return; + } + + auto ptr=ptrRnti->second.bearers; + + if ( ptr.find(rab_id) == ptr.end() ) { + LOG_E(GTPU,"sending a packet to a non existant RNTI:RAB: %x/%x\n", rnti, rab_id); + pthread_mutex_unlock(&globGtp.gtp_lock); + return; + } else + LOG_D(GTPU,"sending a end packet packet to RNTI:RAB:teid %x/%x/%x\n", + rnti, rab_id,ptr[rab_id].teid_outgoing); + + ocp_gtpv1u_bearer_t tmp=ptr[rab_id]; + pthread_mutex_unlock(&globGtp.gtp_lock); Gtpv1uMsgHeaderT msgHdr; // N should be 0 for us (it was used only in 2G and 3G) msgHdr.PN=0; @@ -229,7 +270,6 @@ static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_tunnel_data_req_t *r msgHdr.msgType=GTP_END_MARKER; msgHdr.msgLength=htons(0); msgHdr.teid=htonl(tmp.teid_outgoing); - // Fix me: add IPv6 support, using flag ipVersion static struct sockaddr_in to= {0}; to.sin_family = AF_INET; @@ -243,7 +283,7 @@ static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_tunnel_data_req_t *r compatInst(instance), IPV4_ADDR_FORMAT(tmp.outgoing_ip_addr), tmp.outgoing_port, sizeof(msgHdr)); } } - + static int udpServerSocket(openAddr_s addr) { LOG_I(GTPU, "Initializing UDP for local address %s with port %s\n", addr.originHost, addr.originService); int status; @@ -280,7 +320,7 @@ static int udpServerSocket(openAddr_s addr) { memcpy(globGtp.instances[sockfd].foundAddr, &ipv4->sin_addr.s_addr, sizeof(ipv4->sin_addr.s_addr)); globGtp.instances[sockfd].foundAddrLen=sizeof(ipv4->sin_addr.s_addr); - globGtp.instances[sockfd].ipVersion=4; + globGtp.instances[sockfd].ipVersion=4; break; } else if (p->ai_family == AF_INET6) { LOG_W(GTPU,"Local address is IP v6\n"); @@ -288,7 +328,7 @@ static int udpServerSocket(openAddr_s addr) { memcpy(globGtp.instances[sockfd].foundAddr, &ipv6->sin6_addr.s6_addr, sizeof(ipv6->sin6_addr.s6_addr)); globGtp.instances[sockfd].foundAddrLen=sizeof(ipv6->sin6_addr.s6_addr); - globGtp.instances[sockfd].ipVersion=6; + globGtp.instances[sockfd].ipVersion=6; } else AssertFatal(false,"Local address is not IPv4 or IPv6"); } @@ -344,7 +384,8 @@ instance_t ocp_gtpv1Init(openAddr_t context) { return id; } -teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int bearer_id, teid_t teid, transport_layer_addr_t remoteAddr, int port, gtpCallback callBack) { +teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int incoming_bearer_id, int outgoing_bearer_id, teid_t outgoing_teid, + transport_layer_addr_t remoteAddr, int port, gtpCallback callBack) { pthread_mutex_lock(&globGtp.gtp_lock); auto inst=&globGtp.instances[instance]; auto it=inst->ue2te_mapping.find(rnti); @@ -354,20 +395,20 @@ teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int bearer_id, teid inst->ue2te_mapping.erase(it); } - uint32_t s1u_teid=gtpv1uNewTeid(); + uint32_t incoming_teid=gtpv1uNewTeid(); - while ( inst->te2ue_mapping.find(s1u_teid) != inst->te2ue_mapping.end() ) { - LOG_W(GTPU, "generated a random Teid that exists, re-generating (%u)\n",s1u_teid); - s1u_teid=gtpv1uNewTeid(); + while ( inst->te2ue_mapping.find(incoming_teid) != inst->te2ue_mapping.end() ) { + LOG_W(GTPU, "generated a random Teid that exists, re-generating (%x)\n",incoming_teid); + incoming_teid=gtpv1uNewTeid(); }; - inst->te2ue_mapping[s1u_teid].rnti=rnti; + inst->te2ue_mapping[incoming_teid].rnti=rnti; - inst->te2ue_mapping[s1u_teid].rb_id= teid; + inst->te2ue_mapping[incoming_teid].incoming_rb_id= incoming_bearer_id; - inst->te2ue_mapping[s1u_teid].callBack=callBack; + inst->te2ue_mapping[incoming_teid].callBack=callBack; - auto tmp=&inst->ue2te_mapping[rnti].bearers[bearer_id]; + auto tmp=&inst->ue2te_mapping[rnti].bearers[outgoing_bearer_id]; int addrs_length_in_bytes = remoteAddr.length / 8; @@ -391,23 +432,38 @@ teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int bearer_id, teid AssertFatal(false, "SGW Address size impossible"); } - tmp->teid_incoming = s1u_teid; + tmp->teid_incoming = incoming_teid; tmp->outgoing_port=port; - tmp->teid_outgoing= teid; + tmp->teid_outgoing= outgoing_teid; pthread_mutex_unlock(&globGtp.gtp_lock); - return s1u_teid; + char ip4[INET_ADDRSTRLEN]; + char ip6[INET6_ADDRSTRLEN]; + + LOG_I(GTPU, "Created tunnel for RNTI %x, teid for DL: %d, teid for UL %d to remote IPv4: %s, IPv6 %s\n", + rnti, + tmp->teid_incoming, + tmp->teid_outgoing, + inet_ntop(AF_INET,(void*)&tmp->outgoing_ip_addr, ip4,INET_ADDRSTRLEN ), + inet_ntop(AF_INET6,(void*)&tmp->outgoing_ip6_addr.s6_addr, ip6, INET6_ADDRSTRLEN)); + + return incoming_teid; } int ocp_gtpv1u_create_s1u_tunnel(instance_t instance, const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, gtpv1u_enb_create_tunnel_resp_t *create_tunnel_resp) { - LOG_D(GTPU, "Start create tunnels for RNTI %x, num_tunnels %d, sgw_S1u_teid %d\n", + LOG_D(GTPU, "Start create tunnels for RNTI %x, num_tunnels %d, sgw_S1u_teid %x\n", create_tunnel_req->rnti, create_tunnel_req->num_tunnels, create_tunnel_req->sgw_S1u_teid[0]); for (int i = 0; i < create_tunnel_req->num_tunnels; i++) { + AssertFatal(create_tunnel_req->eps_bearer_id[i] > 4, + "From legacy code not clear, seems impossible (bearer=%d)\n", + create_tunnel_req->eps_bearer_id[i]); + int incoming_rb_id=create_tunnel_req->eps_bearer_id[i]-4; teid_t teid=newGtpuCreateTunnel(compatInst(instance), create_tunnel_req->rnti, + incoming_rb_id, create_tunnel_req->eps_bearer_id[i], create_tunnel_req->sgw_S1u_teid[i], create_tunnel_req->sgw_addr[i], 2152, @@ -430,7 +486,7 @@ int ocp_gtpv1u_update_s1u_tunnel( const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req, const rnti_t prior_rnti ) { - LOG_D(GTPU, "Start update tunnels for old RNTI %x, new RNTI %x, num_tunnels %d, sgw_S1u_teid %d, eps_bearer_id %d\n", + LOG_D(GTPU, "Start update tunnels for old RNTI %x, new RNTI %x, num_tunnels %d, sgw_S1u_teid %x, eps_bearer_id %x\n", prior_rnti, create_tunnel_req->rnti, create_tunnel_req->num_tunnels, @@ -459,6 +515,41 @@ int ocp_gtpv1u_update_s1u_tunnel( return 0; } +int gtpv1u_create_ngu_tunnel( const instance_t instance, + const gtpv1u_gnb_create_tunnel_req_t *const create_tunnel_req, + gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp) { + LOG_D(GTPU, "Start create tunnels for RNTI %x, num_tunnels %d, sgw_S1u_teid %x\n", + create_tunnel_req->rnti, + create_tunnel_req->num_tunnels, + create_tunnel_req->upf_NGu_teid[0]); + + for (int i = 0; i < create_tunnel_req->num_tunnels; i++) { + teid_t teid=newGtpuCreateTunnel(compatInst(instance), create_tunnel_req->rnti, + create_tunnel_req->pdusession_id[i], + create_tunnel_req->pdusession_id[i], + create_tunnel_req->upf_NGu_teid[i], + create_tunnel_req->upf_addr[i], 2152, + pdcp_data_req); + create_tunnel_resp->status=0; + create_tunnel_resp->rnti=create_tunnel_req->rnti; + create_tunnel_resp->num_tunnels=create_tunnel_req->num_tunnels; + create_tunnel_resp->gnb_NGu_teid[i]=teid; + memcpy(create_tunnel_resp->gnb_addr.buffer,globGtp.instances[compatInst(instance)].foundAddr, + globGtp.instances[compatInst(instance)].foundAddrLen); + create_tunnel_resp->gnb_addr.length= globGtp.instances[compatInst(instance)].foundAddrLen; + } + + return !GTPNOK; +} + +int gtpv1u_update_ngu_tunnel( + const instance_t instanceP, + const gtpv1u_gnb_create_tunnel_req_t *const create_tunnel_req_pP, + const rnti_t prior_rnti +) { + return GTPNOK; +} + int ocp_gtpv1u_create_x2u_tunnel( const instance_t instanceP, const gtpv1u_enb_create_x2u_tunnel_req_t *const create_tunnel_req_pP, @@ -479,13 +570,18 @@ int newGtpuDeleteTunnel(instance_t instance, rnti_t rnti) { return -1; } + int nb=0; + for (auto j=it->second.bearers.begin(); j!=it->second.bearers.end(); - ++j) + ++j) { inst->te2ue_mapping.erase(j->second.teid_incoming); + nb++; + } inst->ue2te_mapping.erase(it); pthread_mutex_unlock(&globGtp.gtp_lock); + LOG_I(GTPU, "Deleted all tunnels for RNTI %d (%d tunnels deleted)\n", rnti, nb); return !GTPNOK; } @@ -496,6 +592,12 @@ int ocp_gtpv1u_delete_s1u_tunnel( const instance_t instance, return newGtpuDeleteTunnel(instance, req_pP->rnti); } +int gtpv1u_delete_x2u_tunnel( const instance_t instanceP, + const gtpv1u_enb_delete_tunnel_req_t *const req_pP, + int enbflag) { + return 0; +} + static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, @@ -526,15 +628,16 @@ static int Gtpv1uHandleSupportedExt(int h, return rc; } - // When end marker arrives, we notify the client with buffer size = 0 - // The client will likely call "delete tunnel" - // nevertheless we don't take the initiative +// When end marker arrives, we notify the client with buffer size = 0 +// The client will likely call "delete tunnel" +// nevertheless we don't take the initiative static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) { Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *) msgBuf; + if ( msgHdr->version != 1 || msgHdr->PT != 1 ) { LOG_E(GTPU, "Received a packet that is not GTP header\n"); return GTPNOK; @@ -546,10 +649,11 @@ static int Gtpv1uHandleEndMarker(int h, auto tunnel=inst->te2ue_mapping.find(ntohl(msgHdr->teid)); if ( tunnel == inst->te2ue_mapping.end() ) { - LOG_E(GTPU,"Received a incoming packet on unknown teid (%d) Dropping!\n", msgHdr->teid); + LOG_E(GTPU,"Received a incoming packet on unknown teid (%x) Dropping!\n", msgHdr->teid); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } + // This context is not good for gtp // frame, ... has no meaning // manyother attributes may come from create tunnel @@ -564,7 +668,7 @@ static int Gtpv1uHandleEndMarker(int h, ctxt.configured = 0; ctxt.brOption = 0; const srb_flag_t srb_flag=SRB_FLAG_NO; - const rb_id_t rb_id=tunnel->second.rb_id; + const rb_id_t rb_id=tunnel->second.incoming_rb_id; const mui_t mui=RLC_MUI_UNDEFINED; const confirm_t confirm=RLC_SDU_CONFIRM_NO; const pdcp_transmission_mode_t mode=PDCP_TRANSMISSION_MODE_DATA; @@ -587,7 +691,7 @@ static int Gtpv1uHandleEndMarker(int h, LOG_D(GTPU,"Received END marker packet for: teid:%x\n", ntohl(msgHdr->teid)); return !GTPNOK; } - + static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, @@ -606,7 +710,7 @@ static int Gtpv1uHandleGpdu(int h, auto tunnel=inst->te2ue_mapping.find(ntohl(msgHdr->teid)); if ( tunnel == inst->te2ue_mapping.end() ) { - LOG_E(GTPU,"Received a incoming packet on unknown teid (%d) Dropping!\n", msgHdr->teid); + LOG_E(GTPU,"Received a incoming packet on unknown teid (%x) Dropping!\n", msgHdr->teid); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } @@ -630,7 +734,7 @@ static int Gtpv1uHandleGpdu(int h, ctxt.configured = 0; ctxt.brOption = 0; const srb_flag_t srb_flag=SRB_FLAG_NO; - const rb_id_t rb_id=tunnel->second.rb_id; + const rb_id_t rb_id=tunnel->second.incoming_rb_id; const mui_t mui=RLC_MUI_UNDEFINED; const confirm_t confirm=RLC_SDU_CONFIRM_NO; const sdu_size_t sdu_buffer_size=msgBufLen-offset; @@ -730,20 +834,27 @@ void *ocp_gtpv1uTask(void *args) { } break; + case GTPV1U_GNB_TUNNEL_DATA_REQ: { + gtpv1uSend2(compatInst(ITTI_MSG_DESTINATION_INSTANCE(message_p)), + >PV1U_GNB_TUNNEL_DATA_REQ(message_p), false, false); + itti_free(OCP_GTPV1_U, GTPV1U_GNB_TUNNEL_DATA_REQ(message_p).buffer); + } + break; + case TERMINATE_MESSAGE: break; case TIMER_HAS_EXPIRED: LOG_E(GTPU, "Received unexpected timer expired (no need of timers in this version) %s\n", ITTI_MSG_NAME(message_p)); break; - - case GTPV1U_ENB_END_MARKER_REQ: - gtpv1uEndTunnel(compatInst(ITTI_MSG_DESTINATION_INSTANCE(message_p)), - >PV1U_ENB_TUNNEL_DATA_REQ(message_p)); - itti_free(OCP_GTPV1_U, GTPV1U_ENB_TUNNEL_DATA_REQ(message_p).buffer); - break; - - case GTPV1U_ENB_DATA_FORWARDING_REQ: + + case GTPV1U_ENB_END_MARKER_REQ: + gtpv1uEndTunnel(compatInst(ITTI_MSG_DESTINATION_INSTANCE(message_p)), + >PV1U_ENB_TUNNEL_DATA_REQ(message_p)); + itti_free(OCP_GTPV1_U, GTPV1U_ENB_TUNNEL_DATA_REQ(message_p).buffer); + break; + + case GTPV1U_ENB_DATA_FORWARDING_REQ: case GTPV1U_ENB_DATA_FORWARDING_IND: case GTPV1U_ENB_END_MARKER_IND: LOG_E(GTPU, "to be developped %s\n", ITTI_MSG_NAME(message_p)); @@ -757,6 +868,13 @@ void *ocp_gtpv1uTask(void *args) { AssertFatal((legacyInstanceMapping=ocp_gtpv1Init(addr))!=0,"Instance 0 reserved for legacy\n"); break; + case GTPV1U_GNB_NG_REQ: + // to be dev: should be removed, to use API + strcpy(addr.originHost, GTPV1U_ENB_S1_REQ(message_p).addrStr); + strcpy(addr.originService, GTPV1U_ENB_S1_REQ(message_p).portStr); + AssertFatal((legacyInstanceMapping=ocp_gtpv1Init(addr))!=0,"Instance 0 reserved for legacy\n"); + break; + default: LOG_E(GTPU, "Received unexpected message %s\n", ITTI_MSG_NAME(message_p)); abort(); diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index 507aa1338e..dd406e27cd 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -14,7 +14,7 @@ extern "C" { #define gtpv1u_delete_s1u_tunnel ocp_gtpv1u_delete_s1u_tunnel #define gtpv1u_create_x2u_tunnel ocp_gtpv1u_create_x2u_tunnel #define gtpv1u_eNB_task ocp_gtpv1uTask -#define gtpv1u_gNB_task ocp_gtpv1uTask +#define nr_gtpv1u_gNB_task ocp_gtpv1uTask #define TASK_VARIABLE OCP_GTPV1_U #else #define TASK_VARIABLE TASK_GTPV1_U @@ -60,7 +60,7 @@ int ocp_gtpv1u_create_x2u_tunnel( // New API -teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int bearer_id, teid_t teid, +teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int incoming_bearer_id, int outgoing_rb_id, teid_t teid, transport_layer_addr_t remoteAddr, int port, gtpCallback callBack); instance_t ocp_gtpv1Init(openAddr_t context); void *ocp_gtpv1uTask(void *args); diff --git a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp index 83a6207eec..fd4a830a02 100644 --- a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp +++ b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp @@ -949,8 +949,8 @@ int trx_usrp_reset_stats(openair0_device *device) { extern "C" { int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { - LOG_D(HW, "openair0_cfg[0].sdr_addrs == '%s'\n", openair0_cfg[0].sdr_addrs); - LOG_D(HW, "openair0_cfg[0].clock_source == '%d'\n", openair0_cfg[0].clock_source); + LOG_I(HW, "openair0_cfg[0].sdr_addrs == '%s'\n", openair0_cfg[0].sdr_addrs); + LOG_I(HW, "openair0_cfg[0].clock_source == '%d' (internal = %d, external = %d)\n", openair0_cfg[0].clock_source,internal,external); usrp_state_t *s ; if ( device->priv == NULL) { @@ -1041,15 +1041,15 @@ extern "C" { if (args.find("clock_source")==std::string::npos) { if (openair0_cfg[0].clock_source == internal) { s->usrp->set_clock_source("internal"); - LOG_D(HW,"Setting clock source to internal\n"); + LOG_I(HW,"Setting clock source to internal\n"); } else if (openair0_cfg[0].clock_source == external ) { s->usrp->set_clock_source("external"); - LOG_D(HW,"Setting clock source to external\n"); + LOG_I(HW,"Setting clock source to external\n"); } else if (openair0_cfg[0].clock_source==gpsdo) { s->usrp->set_clock_source("gpsdo"); - LOG_D(HW,"Setting clock source to gpsdo\n"); + LOG_I(HW,"Setting clock source to gpsdo\n"); } else { LOG_W(HW,"Clock source set neither in usrp_args nor on command line, using default!\n"); @@ -1064,15 +1064,15 @@ extern "C" { if (args.find("time_source")==std::string::npos) { if (openair0_cfg[0].time_source == internal) { s->usrp->set_time_source("internal"); - LOG_D(HW,"Setting time source to internal\n"); + LOG_I(HW,"Setting time source to internal\n"); } else if (openair0_cfg[0].time_source == external ) { s->usrp->set_time_source("external"); - LOG_D(HW,"Setting time source to external\n"); + LOG_I(HW,"Setting time source to external\n"); } else if (openair0_cfg[0].time_source==gpsdo) { s->usrp->set_time_source("gpsdo"); - LOG_D(HW,"Setting time source to gpsdo\n"); + LOG_I(HW,"Setting time source to gpsdo\n"); } else { LOG_W(HW,"Time source set neither in usrp_args nor on command line, using default!\n"); @@ -1265,10 +1265,14 @@ extern "C" { set_rx_gain_offset(&openair0_cfg[0],i,bw_gain_adjust); ::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(i); // limit to maximum gain - AssertFatal( openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] <= gain_range.stop(), - "RX Gain too high, lower by %f dB\n", - openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] - gain_range.stop()); - s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],i); + double gain=openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i]; + if ( gain > gain_range.stop()) { + LOG_E(HW,"RX Gain too high, lower by %f dB\n", + gain - gain_range.stop()); + gain=gain_range.stop(); + } + + s->usrp->set_rx_gain(gain,i); LOG_I(HW,"RX Gain %d %f (%f) => %f (max %f)\n",i, openair0_cfg[0].rx_gain[i],openair0_cfg[0].rx_gain_offset[i], openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],gain_range.stop()); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_eNB_band13.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_eNB_band13.conf index 1e32905733..1d8f61e442 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_eNB_band13.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_eNB_band13.conf @@ -240,7 +240,7 @@ MACRLCs = ( THREAD_STRUCT = ( { parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; - worker_config = "ENABLE"; + worker_config = "WORKER_ENABLE"; } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_enb.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_enb.conf index d26ebcd590..7e9a55d924 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_enb.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_enb.conf @@ -241,7 +241,7 @@ MACRLCs = ( THREAD_STRUCT = ( { parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; - worker_config = "ENABLE"; + worker_config = "WORKER_ENABLE"; } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf index bdb8b8518a..c7ffc1a29a 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf @@ -188,7 +188,7 @@ gNBs = ////////// MME parameters: - mme_ip_address = ( { ipv4 = "192.168.18.99"; + amf_ip_address = ( { ipv4 = "192.168.69.131"; ipv6 = "192:168:30::17"; active = "yes"; preference = "ipv4"; @@ -211,10 +211,10 @@ gNBs = NETWORK_INTERFACES : { - GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1_MME = "192.168.18.198/24"; - GNB_INTERFACE_NAME_FOR_S1U = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1U = "192.168.18.198/24"; + GNB_INTERFACE_NAME_FOR_NG_AMF = "eth0"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.18.198/24"; + GNB_INTERFACE_NAME_FOR_NGU = "eth0"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.18.198/24"; GNB_PORT_FOR_S1U = 2152; # Spec 2152 GNB_IPV4_ADDRESS_FOR_X2C = "192.168.18.198/24"; GNB_PORT_FOR_X2C = 36422; # Spec 36422 @@ -249,7 +249,7 @@ RUs = ( max_pdschReferenceSignalPower = -27; max_rxgain = 114; eNB_instances = [0]; - clock_src = "external"; + #clock_src = "external"; } ); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf new file mode 100644 index 0000000000..483fd34f2a --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf @@ -0,0 +1,288 @@ +Active_gNBs = ( "gNB-CU-Eurecom-5GNRBox"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; +Num_Threads_PUSCH = 8; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + + cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-CU-Eurecom-5GNRBox"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + + plmn_list = ({mcc = 208; mnc = 93; mnc_length = 2;}); + + nr_cellid = 12345678L + + tr_s_preference = "f1" + + local_s_if_name = "lo"; + remote_s_address = "127.0.0.3"; + local_s_address = "127.0.0.4"; + local_s_portc = 501; + remote_s_portc = 500; + local_s_portd = 601; + remote_s_portd = 600; + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641032; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640000; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=41,L=24 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 6366; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + #pdsch-ConfigCommon + #pdschTimeDomainAllocationList (up to 16 entries) + initialDLBWPk0_0 = 0; + #initialULBWPmappingType + #0=typeA,1=typeB + initialDLBWPmappingType_0 = 0; + #this is SS=1,L=13 + initialDLBWPstartSymbolAndLength_0 = 40; + + initialDLBWPk0_1 = 0; + initialDLBWPmappingType_1 = 0; + #this is SS=2,L=12 + initialDLBWPstartSymbolAndLength_1 = 53; + + initialDLBWPk0_2 = 0; + initialDLBWPmappingType_2 = 0; + #this is SS=1,L=12 + initialDLBWPstartSymbolAndLength_2 = 54; + + initialDLBWPk0_3 = 0; + initialDLBWPmappingType_3 = 0; + #this is SS=1,L=5 + initialDLBWPstartSymbolAndLength_3 = 57; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 6366; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -118; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + # pusch-ConfigCommon (up to 16 elements) + initialULBWPk2_0 = 6; + initialULBWPmappingType_0 = 1 + # this is SS=0 L=11 + initialULBWPstartSymbolAndLength_0 = 55; + + initialULBWPk2_1 = 6; + initialULBWPmappingType_1 = 1; + # this is SS=0 L=12 + initialULBWPstartSymbolAndLength_1 = 69; + + initialULBWPk2_2 = 7; + initialULBWPmappingType_2 = 1; + # this is SS=10 L=4 + initialULBWPstartSymbolAndLength_2 = 52; + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = 10; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// MME parameters: + mme_ip_address = ( { ipv4 = "192.168.12.26"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + NETWORK_INTERFACES : + { + + GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1_MME = "192.168.12.111/24"; + GNB_INTERFACE_NAME_FOR_S1U = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1U = "192.168.12.111/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +# RUs = ( +# { +# local_rf = "yes" +# nb_tx = 1 +# nb_rx = 1 +# att_tx = 0 +# att_rx = 0; +# bands = [7]; +# max_pdschReferenceSignalPower = -27; +# max_rxgain = 75; +# eNB_instances = [0]; +# ##beamforming 1x2 matrix: 1 layer x 2 antennas +# bf_weights = [0x00007fff, 0x0000]; +# ##beamforming 1x4 matrix: 1 layer x 4 antennas +# #bf_weights = [0x00007fff, 0x0000,0x0000, 0x0000]; +# ## beamforming 2x2 matrix: +# # bf_weights = [0x00007fff, 0x00000000, 0x00000000, 0x00007fff]; +# ## beamforming 4x4 matrix: +# #bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff]; + +# sdr_addrs = "addr=192.168.10.2,mgmt_addr=192.168.10.2,second_addr=192.168.20.2"; +# clock_src = "external"; +# } +# ); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + + log_config : + { + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="debug"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="debug"; + rrc_log_verbosity ="medium"; + f1ap_log_level ="info"; + f1ap_log_verbosity ="medium"; + }; + diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf new file mode 100644 index 0000000000..dcb3010f20 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf @@ -0,0 +1,302 @@ +Active_gNBs = ( "gNB-Eurecom-DU"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; +Num_Threads_PUSCH = 8; + +gNBs = +( + { + ////////// Identification parameters: + gNB_CU_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-Eurecom-DU"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + + plmn_list = ({mcc = 208; mnc = 93; mnc_length = 2;}); + + nr_cellid = 12345678L + +# tr_s_preference = "local_mac" + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641032; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640000; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=41,L=24 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 6366; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + #pdsch-ConfigCommon + #pdschTimeDomainAllocationList (up to 16 entries) + initialDLBWPk0_0 = 0; + #initialULBWPmappingType + #0=typeA,1=typeB + initialDLBWPmappingType_0 = 0; + #this is SS=1,L=13 + initialDLBWPstartSymbolAndLength_0 = 40; + + initialDLBWPk0_1 = 0; + initialDLBWPmappingType_1 = 0; + #this is SS=2,L=12 + initialDLBWPstartSymbolAndLength_1 = 53; + + initialDLBWPk0_2 = 0; + initialDLBWPmappingType_2 = 0; + #this is SS=1,L=12 + initialDLBWPstartSymbolAndLength_2 = 54; + + initialDLBWPk0_3 = 0; + initialDLBWPmappingType_3 = 0; + #this is SS=1,L=5 + initialDLBWPstartSymbolAndLength_3 = 57; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 6366; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -118; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + # pusch-ConfigCommon (up to 16 elements) + initialULBWPk2_0 = 6; + initialULBWPmappingType_0 = 1 + # this is SS=0 L=11 + initialULBWPstartSymbolAndLength_0 = 55; + + initialULBWPk2_1 = 6; + initialULBWPmappingType_1 = 1; + # this is SS=0 L=12 + initialULBWPstartSymbolAndLength_1 = 69; + + initialULBWPk2_2 = 7; + initialULBWPmappingType_2 = 1; + # this is SS=10 L=4 + initialULBWPstartSymbolAndLength_2 = 52; + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = 10; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// MME parameters: + mme_ip_address = ( { ipv4 = "192.168.12.26"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + NETWORK_INTERFACES : + { + + GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1_MME = "192.168.12.111/24"; + GNB_INTERFACE_NAME_FOR_S1U = "eth0"; + GNB_IPV4_ADDRESS_FOR_S1U = "192.168.12.111/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "f1"; + local_n_if_name = "lo"; + remote_n_address = "127.0.0.4"; + local_n_address = "127.0.0.3"; + local_n_portc = 500; + remote_n_portc = 501; + local_n_portd = 600; + remote_n_portd = 601; + } +); + +L1s = ( + { + num_cc = 1; + tr_n_preference = "local_mac"; + } +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [7]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + eNB_instances = [0]; + ##beamforming 1x2 matrix: 1 layer x 2 antennas + bf_weights = [0x00007fff, 0x0000]; + ##beamforming 1x4 matrix: 1 layer x 4 antennas + #bf_weights = [0x00007fff, 0x0000,0x0000, 0x0000]; + ## beamforming 2x2 matrix: + # bf_weights = [0x00007fff, 0x00000000, 0x00000000, 0x00007fff]; + ## beamforming 4x4 matrix: + #bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff]; + + sdr_addrs = "addr=192.168.10.2,mgmt_addr=192.168.10.2,second_addr=192.168.20.2"; + clock_src = "external"; + } +); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + + log_config : + { + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="debug"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + f1ap_log_level ="debug"; + f1ap_log_verbosity ="medium"; + }; + diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf new file mode 100644 index 0000000000..753bd0bed3 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf @@ -0,0 +1,304 @@ +Active_gNBs = ( "gNB-Eurecom-5GNRBox"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + + cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-Eurecom-5GNRBox"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 100; + + plmn_list = ({ + mcc = 208; + mnc = 93; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x010203; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); + + }); + + tr_s_preference = "local_mac" + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641032; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640000; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=41,L=24 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 6366; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + #pdsch-ConfigCommon + #pdschTimeDomainAllocationList (up to 16 entries) + initialDLBWPk0_0 = 0; + #initialULBWPmappingType + #0=typeA,1=typeB + initialDLBWPmappingType_0 = 0; + #this is SS=1,L=13 + initialDLBWPstartSymbolAndLength_0 = 40; + + initialDLBWPk0_1 = 0; + initialDLBWPmappingType_1 = 0; + #this is SS=2,L=12 + initialDLBWPstartSymbolAndLength_1 = 53; + + initialDLBWPk0_2 = 0; + initialDLBWPmappingType_2 = 0; + #this is SS=1,L=12 + initialDLBWPstartSymbolAndLength_2 = 54; + + initialDLBWPk0_3 = 0; + initialDLBWPmappingType_3 = 0; + #this is SS=1,L=5 + initialDLBWPstartSymbolAndLength_3 = 57; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 6366; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -118; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, + +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + # pusch-ConfigCommon (up to 16 elements) + initialULBWPk2_0 = 6; + initialULBWPmappingType_0 = 1 + # this is SS=0 L=11 + initialULBWPstartSymbolAndLength_0 = 55; + + initialULBWPk2_1 = 6; + initialULBWPmappingType_1 = 1; + # this is SS=0 L=12 + initialULBWPstartSymbolAndLength_1 = 69; + + initialULBWPk2_2 = 7; + initialULBWPmappingType_2 = 1; + # this is SS=10 L=4 + initialULBWPstartSymbolAndLength_2 = 52; + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = 10; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "192.168.199.223"; + ipv6 = "192:168:199::223"; + active = "yes"; + preference = "ipv4"; + } + ); + + + + NETWORK_INTERFACES : + { + + GNB_INTERFACE_NAME_FOR_NG_AMF = "eno1"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.199.222/24"; + GNB_INTERFACE_NAME_FOR_NGU = "enx000ec6c0a3ac"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.10.100/24"; + GNB_PORT_FOR_NGU = 2152; # Spec 2152 + GNB_IPV4_ADDRESS_FOR_X2C = "192.168.20.20/24"; + GNB_PORT_FOR_X2C = 36422; # Spec 36422 + }; + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + } +); + +L1s = ( + { + num_cc = 1; + tr_n_preference = "local_mac"; + } +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [7]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + eNB_instances = [0]; + #beamforming 1x4 matrix: + bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000]; + sdr_addrs = "addr=192.168.10.2,mgmt_addr=192.168.10.2,second_addr=192.168.20.2"; + clock_src = "external"; + } +); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + + log_config : + { + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + ngap_log_level ="info"; + ngap_log_verbosity ="medium"; + }; + diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf index 0f66d2fb92..00c92d75a2 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf @@ -1,4 +1,4 @@ -Active_gNBs = ( "gNB-Eurecom-5GNRBox"); +Active_gNBs = ( "gNB-Eurecom-DU"); # Asn1_verbosity, choice in: none, info, annoying Asn1_verbosity = "none"; @@ -7,15 +7,28 @@ gNBs = { ////////// Identification parameters: gNB_ID = 0xe00; - - cell_type = "CELL_MACRO_GNB"; - gNB_name = "gNB-Eurecom-5GNRBox"; + gNB_name = "gNB-Eurecom-DU"; // Tracking area code, 0x0000 and 0xfffe are reserved values tracking_area_code = 1; - plmn_list = ({mcc = 208; mnc = 92; mnc_length = 2;}); + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); - tr_s_preference = "local_mac" + }); + + nr_cellid = 12345678L; ////////// Physical parameters: @@ -51,8 +64,8 @@ gNBs = dl_carrierBandwidth = 106; #initialDownlinkBWP #genericParameters - # this is RBstart=41,L=24 (275*(L-1))+RBstart - initialDLBWPlocationAndBandwidth = 12952; # 6366 12925 12956 28875 + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 12952; # 6366 12925 12956 28875 12952 # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialDLBWPsubcarrierSpacing = 1; @@ -191,35 +204,21 @@ gNBs = ////////// MME parameters: - mme_ip_address = ( { ipv4 = "192.168.12.26"; + amf_ip_address = ( { ipv4 = "192.168.70.132"; ipv6 = "192:168:30::17"; active = "yes"; preference = "ipv4"; } ); - ///X2 - enable_x2 = "no"; - t_reloc_prep = 1000; /* unit: millisecond */ - tx2_reloc_overall = 2000; /* unit: millisecond */ - t_dc_prep = 1000; /* unit: millisecond */ - t_dc_overall = 2000; /* unit: millisecond */ - target_enb_x2_ip_address = ( - { ipv4 = "CI_FR1_CTL_ENB_IP_ADDR"; - ipv6 = "192:168:30::17"; - preference = "ipv4"; - } - ); NETWORK_INTERFACES : { - GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1_MME = "CI_GNB_IP_ADDR"; - GNB_INTERFACE_NAME_FOR_S1U = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1U = "CI_GNB_IP_ADDR"; + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; GNB_PORT_FOR_S1U = 2152; # Spec 2152 - GNB_IPV4_ADDRESS_FOR_X2C = "CI_GNB_IP_ADDR"; - GNB_PORT_FOR_X2C = 36422; # Spec 36422 }; } @@ -238,7 +237,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; - } + } ); RUs = ( @@ -292,5 +291,7 @@ rfsimulator : pdcp_log_verbosity ="medium"; rrc_log_level ="info"; rrc_log_verbosity ="medium"; + ngap_log_level ="debug"; + ngap_log_verbosity ="medium"; }; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index 52805981b8..bd0678e344 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -1,4 +1,4 @@ -Active_gNBs = ( "gNB-Eurecom-5GNRBox"); +Active_gNBs = ( "gNB-Eurecom-DU"); # Asn1_verbosity, choice in: none, info, annoying Asn1_verbosity = "none"; @@ -7,15 +7,28 @@ gNBs = { ////////// Identification parameters: gNB_ID = 0xe00; - - cell_type = "CELL_MACRO_GNB"; - gNB_name = "gNB-Eurecom-5GNRBox"; + gNB_name = "gNB-Eurecom-DU"; // Tracking area code, 0x0000 and 0xfffe are reserved values tracking_area_code = 1; - plmn_list = ({mcc = 208; mnc = 92; mnc_length = 2;}); + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); - tr_s_preference = "local_mac" + }); + + nr_cellid = 12345678L; ////////// Physical parameters: @@ -191,35 +204,21 @@ gNBs = ////////// MME parameters: - mme_ip_address = ( { ipv4 = "192.168.12.26"; + amf_ip_address = ( { ipv4 = "192.168.70.132"; ipv6 = "192:168:30::17"; active = "yes"; preference = "ipv4"; } ); - ///X2 - enable_x2 = "no"; - t_reloc_prep = 1000; /* unit: millisecond */ - tx2_reloc_overall = 2000; /* unit: millisecond */ - t_dc_prep = 1000; /* unit: millisecond */ - t_dc_overall = 2000; /* unit: millisecond */ - target_enb_x2_ip_address = ( - { ipv4 = "CI_FR1_CTL_ENB_IP_ADDR"; - ipv6 = "192:168:30::17"; - preference = "ipv4"; - } - ); NETWORK_INTERFACES : { - GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1_MME = "CI_GNB_IP_ADDR"; - GNB_INTERFACE_NAME_FOR_S1U = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1U = "CI_GNB_IP_ADDR"; + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; GNB_PORT_FOR_S1U = 2152; # Spec 2152 - GNB_IPV4_ADDRESS_FOR_X2C = "CI_GNB_IP_ADDR"; - GNB_PORT_FOR_X2C = 36422; # Spec 36422 }; } @@ -292,5 +291,7 @@ rfsimulator : pdcp_log_verbosity ="medium"; rrc_log_level ="info"; rrc_log_verbosity ="medium"; + ngap_log_level ="debug"; + ngap_log_verbosity ="medium"; }; diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c index c21c04ba05..424a139001 100644 --- a/targets/RT/USER/lte-softmodem.c +++ b/targets/RT/USER/lte-softmodem.c @@ -103,9 +103,6 @@ pthread_cond_t nfapi_sync_cond; pthread_mutex_t nfapi_sync_mutex; int nfapi_sync_var=-1; //!< protected by mutex \ref nfapi_sync_mutex -msc_interface_t msc_interface; - - uint16_t sf_ahead=4; pthread_cond_t sync_cond; @@ -187,6 +184,7 @@ eth_params_t *eth_params; double cpuf; int oaisim_flag=0; +uint8_t proto_agent_flag = 0; /* forward declarations */ diff --git a/targets/RT/USER/lte-uesoftmodem.c b/targets/RT/USER/lte-uesoftmodem.c index 1079f89c3f..026e6d4a60 100644 --- a/targets/RT/USER/lte-uesoftmodem.c +++ b/targets/RT/USER/lte-uesoftmodem.c @@ -86,7 +86,6 @@ #include "lte-softmodem.h" -msc_interface_t msc_interface; /* temporary compilation wokaround (UE/eNB split */ uint16_t sf_ahead; From 1a089a0d6795183f52ad4c53d3fd33cc36aa642c Mon Sep 17 00:00:00 2001 From: Abhijith Date: Tue, 6 Jul 2021 16:37:46 +0530 Subject: [PATCH 025/233] Fixed errors with initialBWP and SA --- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 86 +++++----- openair2/LAYER2/NR_MAC_UE/config_ue.c | 22 +-- .../NR_MAC_UE/nr_ue_dci_configuration.c | 20 +-- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 151 +++++++++--------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 80 +++++----- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 2 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 8 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 4 +- 8 files changed, 186 insertions(+), 187 deletions(-) diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index 9af50d28e0..e7f51bfa69 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -420,7 +420,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ NR_UE_MAC_INST_t *mac = get_mac_inst(0); NR_PUCCH_Resource_t *pucch_resource = NULL; uint16_t crnti = mac->crnti; - NR_BWP_Id_t bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP /* update current context */ @@ -504,7 +504,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ if (pucch_resource_set != MAX_NB_OF_PUCCH_RESOURCE_SETS) { pucch_resource_indicator = 0; /* get the first resource of the set */ - pucch_resource_id = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set]->resourceList.list.array[pucch_resource_indicator][0]; + pucch_resource_id = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set]->resourceList.list.array[pucch_resource_indicator][0]; } else { LOG_W(PHY,"PUCCH no resource set found for CSI at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); @@ -616,11 +616,11 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ } NR_PUCCH_Config_t *pucch_Config; if (bwp_id>0 && - mac->ULbwp[bwp_id-1] && - mac->ULbwp[bwp_id-1]->bwp_Dedicated && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup) - pucch_Config = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup; + mac->ULbwp[bwp_id] && + mac->ULbwp[bwp_id]->bwp_Dedicated && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup) + pucch_Config = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup; else if (bwp_id==0 && mac->cg && mac->cg->spCellConfig && @@ -651,12 +651,12 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ m_0 = get_ics_pucch(pucch_resource, format); AssertFatal(m_0 >= 0, "Invalid m_0\n"); if (format == pucch_format3_nr) { - if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->additionalDMRS[0] == 1) { + if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->additionalDMRS[0] == 1) { index_additional_dmrs = I_PUCCH_ADDITIONAL_DMRS; } } else if (format == pucch_format4_nr) { - if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->additionalDMRS[0] == 1) { + if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->additionalDMRS[0] == 1) { index_additional_dmrs = I_PUCCH_ADDITIONAL_DMRS; } } @@ -704,7 +704,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ case pucch_format3_nr: { nb_of_prbs = pucch_resource->format.choice.format3->nrofPRBs; - //if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->pi2BPSK[0] == 1) { + //if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->pi2BPSK[0] == 1) { // Q_m = BITS_PER_SYMBOL_BPSK; /* set bpsk modulation type with 1 bit per modulation symbol */ //} N_sc_ctrl_RB = N_SC_RB; @@ -713,7 +713,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ } case pucch_format4_nr: { - //if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->pi2BPSK[0] == 1) { + //if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->pi2BPSK[0] == 1) { // Q_m = BITS_PER_SYMBOL_BPSK; /* set bpsk modulation type with 1 bit per modulation symbol */ //} nb_symbols = nb_symbols_excluding_dmrs[nb_symbols_total-4][index_additional_dmrs][index_hopping]; @@ -730,10 +730,10 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ /* drop CSI report if simultaneous HARQ-ACK/SR and periodic/semi-periodic CSI cannot be transmitted at the same time */ if (format != pucch_format0_nr) { - if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format1 != NULL) { - //max_code_rate = code_rate_r_time_100[mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->maxCodeRate[0]]; /* it is code rate * 10 */ + if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format1 != NULL) { + //max_code_rate = code_rate_r_time_100[mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->maxCodeRate[0]]; /* it is code rate * 10 */ - if ((O_ACK != 0) && (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->simultaneousHARQ_ACK_CSI[0] == 0)) { + if ((O_ACK != 0) && (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->simultaneousHARQ_ACK_CSI[0] == 0)) { N_UCI = N_UCI - O_CSI; O_CSI = cqi_status = ri_status = 0; csi_payload = 0; /* csi should be dropped in this case */ @@ -858,11 +858,11 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ switch(format) { case pucch_format0_nr: { - int pucch_GroupHopping = mac->ULbwp[bwp_id-1] ? - mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping: + int pucch_GroupHopping = mac->ULbwp[bwp_id] ? + mac->ULbwp[bwp_id]->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping: mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->pucch_GroupHopping; - int hoppingId = mac->ULbwp[bwp_id-1] ? - mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId[0]: + int hoppingId = mac->ULbwp[bwp_id] ? + mac->ULbwp[bwp_id]->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId[0]: (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->hoppingId? mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->hoppingId[0]: mac->physCellId); @@ -988,14 +988,14 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int N_m_c_rx = 0; int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - if (mac->DLbwp[dl_bwp_id-1] == NULL) return 0; + NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + if (mac->DLbwp[dl_bwp_id] == NULL) return 0; - if (mac->DLbwp[dl_bwp_id-1] && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { + if (mac->DLbwp[dl_bwp_id] && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { two_transport_blocks = TRUE; number_of_code_word = 2; } @@ -1075,7 +1075,7 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t if (mac->cg != NULL && mac->cg->physicalCellGroupConfig != NULL && mac->cg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH != NULL) { - int N_TB_max_DL = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; + int N_TB_max_DL = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; *n_HARQ_ACK = (((V_DAI_m_DL - U_DAI_c)%4) * N_TB_max_DL) + N_m_c_rx + N_SPS_c; NR_TST_PHY_PRINTF("PUCCH power n(%d) = ( V(%d) - U(%d) )mod4 * N_TB(%d) + N(%d) \n", *n_HARQ_ACK, V_DAI_m_DL, U_DAI_c, N_TB_max_DL, N_m_c_rx); } @@ -1180,7 +1180,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 pucch_format_nr_t format_pucch; int ready_pucch_resource_id = FALSE; /* in the case that it is already given */ NR_PUCCH_Resource_t *pucch_resource = NULL; - NR_BWP_Id_t bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP /* ini values to unset */ *initial_pucch_id = NB_INITIAL_PUCCH_RESOURCE; @@ -1199,12 +1199,12 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) || - (mac->ULbwp[bwp_id-1] && - mac->ULbwp[bwp_id-1]->bwp_Dedicated && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) + (mac->ULbwp[bwp_id] && + mac->ULbwp[bwp_id]->bwp_Dedicated && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) ){ /* No resource set has been already configured so pucch_configCommon from Sib1 should be used in this case */ @@ -1272,10 +1272,10 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 // Verify that the value of pucch_resource_indicator is valid struct NR_PUCCH_Config__resourceSetToAddModList *resourceSetToAddModList = NULL; struct NR_PUCCH_Config__resourceToAddModList *resourceToAddModList = NULL; - if (bwp_id > 0 && mac->ULbwp[bwp_id-1]) { - AssertFatal(mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList!=NULL,"mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList is null\n"); - resourceSetToAddModList = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList; - resourceToAddModList = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList; + if (bwp_id > 0 && mac->ULbwp[bwp_id]) { + AssertFatal(mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList!=NULL,"mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList is null\n"); + resourceSetToAddModList = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList; + resourceToAddModList = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList; } else if (bwp_id == 0 && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList!=NULL) { resourceSetToAddModList = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList; resourceToAddModList = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceToAddModList; @@ -1389,12 +1389,12 @@ int find_pucch_resource_set(NR_UE_MAC_INST_t *mac, uint8_t gNB_id, int uci_size) /* look for the first resource set which supports uci_size number of bits for payload */ while (pucch_resource_set_id < MAX_NB_OF_PUCCH_RESOURCE_SETS) { if ((bwp_id>0 && - mac->ULbwp[bwp_id-1] && - mac->ULbwp[bwp_id-1]->bwp_Dedicated && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && - mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id] != NULL) || + mac->ULbwp[bwp_id] && + mac->ULbwp[bwp_id]->bwp_Dedicated && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id] != NULL) || (bwp_id==0 && mac->cg && mac->cg->spCellConfig && diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index bfa5d6a93f..5e158df5f0 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -601,8 +601,8 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ AssertFatal(scd->downlinkBWP_ToAddModList->list.count <= 4, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); config_bwp_ue(mac, NULL, NULL); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP AssertFatal(dl_bwp_id != 0, "DL_BWP_Id is 0!"); AssertFatal(ul_bwp_id != 0, "UL_BWP_Id is 0!"); @@ -639,17 +639,17 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ // check pdcch_Config, pdcch_ConfigCommon and DL BWP // Check dedicated UL BWP and pass to MAC - for (int num_BWP = 0; num_BWP <4 ;num_BWP++) + for (int num_BWP = 1; num_BWP < MAX_NUM_BWP ;num_BWP++) { - mac->DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP]; - mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP]; + mac->DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP-1]; + mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP-1]; } - mac->coreset[dl_bwp_id - 1][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; + mac->coreset[dl_bwp_id][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; - AssertFatal(mac->ULbwp[0]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); + AssertFatal(mac->ULbwp[ul_bwp_id]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); // check available Search Spaces in the searchSpacesToAddModList and pass to MAC // note: the network configures at most 10 Search Spaces per BWP per cell (including UE-specific and common Search Spaces). @@ -657,10 +657,10 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ NR_SearchSpace_t *ss = searchSpacesToAddModList->list.array[ss_id]; AssertFatal(ss->controlResourceSetId != NULL, "ss->controlResourceSetId is null\n"); AssertFatal(ss->searchSpaceType != NULL, "ss->searchSpaceType is null\n"); - AssertFatal(*ss->controlResourceSetId == mac->coreset[dl_bwp_id - 1][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); + AssertFatal(*ss->controlResourceSetId == mac->coreset[dl_bwp_id][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); AssertFatal(ss->monitoringSymbolsWithinSlot != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot->buf != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id-1][0][ss_id] = ss; + mac->SSpace[dl_bwp_id][0][ss_id] = ss; } // Check available CSSs in the commonSearchSpaceList (list of additional common search spaces) @@ -668,11 +668,11 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ for (int css_id = 0; css_id < commonSearchSpaceList->list.count; css_id++) { NR_SearchSpace_t *css = commonSearchSpaceList->list.array[css_id]; AssertFatal(css->controlResourceSetId != NULL, "ss->controlResourceSetId is null\n"); - AssertFatal(*css->controlResourceSetId == mac->coreset[dl_bwp_id - 1][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); + AssertFatal(*css->controlResourceSetId == mac->coreset[dl_bwp_id][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); AssertFatal(css->searchSpaceType != NULL, "css->searchSpaceType is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot != NULL, "css->monitoringSymbolsWithinSlot is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot->buf != NULL, "css->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id -1][coreset_id -1][ss_id] = css; + mac->SSpace[dl_bwp_id][coreset_id -1][ss_id] = css; ss_id++; } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 037287e875..008b936504 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -73,7 +73,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t AssertFatal(mac->scc == NULL || mac->scc_SIB == NULL, "both scc and scc_SIB cannot be non-null\n"); - NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP NR_ServingCellConfigCommon_t *scc = mac->scc; NR_ServingCellConfigCommonSIB_t *scc_SIB = mac->scc_SIB; NR_BWP_DownlinkCommon_t *bwp_Common=NULL; @@ -84,14 +84,14 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t initialDownlinkBWP = scc!=NULL ? scc->downlinkConfigCommon->initialDownlinkBWP : &scc_SIB->downlinkConfigCommon.initialDownlinkBWP; initialUplinkBWP = scc!=NULL ? scc->uplinkConfigCommon->initialUplinkBWP : &scc_SIB->uplinkConfigCommon->initialUplinkBWP; - bwp_Common = bwp_id>0 ? mac->DLbwp[bwp_id-1]->bwp_Common : NULL; + bwp_Common = dl_bwp_id>0 ? mac->DLbwp[dl_bwp_id]->bwp_Common : NULL; } NR_SearchSpace_t *ss; NR_ControlResourceSet_t *coreset; if(ss_id>=0) { - ss = mac->SSpace[bwp_id - 1][coreset_id - 1][ss_id]; - coreset = mac->coreset[bwp_id - 1][coreset_id - 1]; + ss = mac->SSpace[dl_bwp_id][coreset_id - 1][ss_id]; + coreset = mac->coreset[dl_bwp_id][coreset_id - 1]; rel15->coreset.CoreSetType = NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; } else { ss = mac->search_space_zero; @@ -154,7 +154,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->SubcarrierSpacing = bwp_Common->genericParameters.subcarrierSpacing; } for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, bwp_id); + rel15->dci_length_options[i] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, dl_bwp_id); } break; case NR_RNTI_RA: @@ -169,7 +169,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPStart = NRRIV2PRBOFFSET(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; - rel15->dci_length_options[0] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, bwp_id); + rel15->dci_length_options[0] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, dl_bwp_id); break; case NR_RNTI_P: break; @@ -183,7 +183,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPSize = NRRIV2BW(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); rel15->BWPStart = NRRIV2PRBOFFSET(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; - rel15->dci_length_options[0] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_TC, rel15->BWPSize, bwp_id); + rel15->dci_length_options[0] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_TC, rel15->BWPSize, dl_bwp_id); break; case NR_RNTI_SP_CSI: break; @@ -246,16 +246,16 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl uint8_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0, coreset_id = (mac->cg) ? 1 : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; - NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id - 1] : NULL; + NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id] : NULL; LOG_D(MAC, "[DCI_CONFIG] ra_rnti %p (%x) crnti %p (%x) t_crnti %p (%x)\n", &ra->ra_rnti, ra->ra_rnti, &mac->crnti, mac->crnti, &ra->t_crnti, ra->t_crnti); if (mac->cg) { // do this only after we have a Master or Secondary Cell group // loop over all available SS for BWP ID 1, CORESET ID 1 if (bwp) { - for (ss_id = 0; ss_id < FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id - 1][coreset_id - 1][ss_id] != NULL; ss_id++){ + for (ss_id = 0; ss_id < FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id][coreset_id - 1][ss_id] != NULL; ss_id++){ LOG_D(MAC, "[DCI_CONFIG] ss_id %d\n",ss_id); - NR_SearchSpace_t *ss = mac->SSpace[bwp_id - 1][coreset_id - 1][ss_id]; + NR_SearchSpace_t *ss = mac->SSpace[bwp_id][coreset_id - 1][ss_id]; fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; NR_BWP_DownlinkCommon_t *bwp_Common = bwp->bwp_Common; NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index d27c13565a..9e1f9debcd 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -280,8 +280,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t sliv_S=0; uint8_t sliv_L=0; uint8_t mu_pusch = 1; - NR_BWP_Id_t dl_bwp_id = mac->UL_BWP_Id; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP // definition table j Table 6.1.2.1.1-4 uint8_t j = (mu_pusch==3)?3:(mu_pusch==2)?2:1; @@ -327,13 +327,13 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(dlsch_config_pdu != NULL){ NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1] && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id] && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id] && mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; @@ -386,19 +386,19 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(pusch_config_pdu != NULL){ NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[ul_bwp_id-1] && - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config && - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup && - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; + if (mac->ULbwp[ul_bwp_id] && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp_id-1] && - mac->ULbwp[ul_bwp_id-1]->bwp_Common && - mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon && - mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id] && + mac->ULbwp[ul_bwp_id]->bwp_Common && + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon && + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else pusch_TimeDomainAllocationList = mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; @@ -459,11 +459,11 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP uint16_t n_RB_DLBWP; - if (mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->DLbwp[dl_bwp_id]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else if (mac->scc_SIB) n_RB_DLBWP = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); else n_RB_DLBWP = mac->type0_PDCCH_CSS_config.num_rbs; @@ -629,7 +629,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti = rnti; fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_0 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - NR_PDSCH_Config_t *pdsch_config= (mac->DLbwp[dl_bwp_id-1]) ? mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + NR_PDSCH_Config_t *pdsch_config= (mac->DLbwp[dl_bwp_id]) ? mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; uint16_t BWPSize = n_RB_DLBWP; if(rnti == SI_RNTI) { @@ -655,15 +655,15 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } if (!get_softmodem_params()->sa) { // NSA mode is not using the Initial BWP - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + pdsch_config = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup; BWPSize = dlsch_config_pdu_1_0->BWPSize; } - } else if (mac->DLbwp[dl_bwp_id-1]) { - dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; - pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; + } else if (mac->DLbwp[dl_bwp_id]) { + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.subcarrierSpacing; + pdsch_config = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup; } else if (mac->scc_SIB) { dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -685,13 +685,13 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1] && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id] && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id] && mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; @@ -703,7 +703,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->number_symbols, dlsch_config_pdu_1_0->start_symbol, mappingtype); - dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id-1] != NULL) ? (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; + dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id] != NULL) ? (mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; /* number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214 version 15.9.0 Release 15 */ if (dlsch_config_pdu_1_0->number_symbols == 2) dlsch_config_pdu_1_0->n_dmrs_cdm_groups = 1; @@ -752,14 +752,14 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - if (mac->ULbwp[ul_bwp_id-1] && - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config && - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup&& - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList) { - pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + if (mac->ULbwp[ul_bwp_id] && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup&& + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList) { + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -855,16 +855,16 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); - NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; + NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup; dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH; dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti = rnti; fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_1 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; + dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.subcarrierSpacing; /* IDENTIFIER_DCI_FORMATS */ /* CARRIER_IND */ @@ -882,13 +882,13 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1] && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id] && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id] && mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; @@ -900,7 +900,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_1->number_symbols, dlsch_config_pdu_1_1->start_symbol, mappingtype); - dlsch_config_pdu_1_1->dmrsConfigType = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1; + dlsch_config_pdu_1_1->dmrsConfigType = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1; /* TODO: fix number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214, using tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of 3GPP TS 38.212 */ dlsch_config_pdu_1_1->n_dmrs_cdm_groups = 1; @@ -949,9 +949,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_1->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -965,7 +965,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr // according to TS 38.213 Table 9.2.3-1 dlsch_config_pdu_1_1->pdsch_to_harq_feedback_time_ind = - mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; /* ANTENNA_PORTS */ uint8_t n_codewords = 1; // FIXME!!! @@ -1077,8 +1077,8 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr /* TODO same calculation for MCS table as done in UL */ dlsch_config_pdu_1_1->mcs_table = (pdsch_config->mcs_Table) ? (*pdsch_config->mcs_Table + 1) : 0; /*PTRS configuration */ - if(mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) { - valid_ptrs_setup = set_dl_ptrs_values(mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup, + if(mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) { + valid_ptrs_setup = set_dl_ptrs_values(mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup, dlsch_config_pdu_1_1->number_rbs, dlsch_config_pdu_1_1->mcs, dlsch_config_pdu_1_1->mcs_table, &dlsch_config_pdu_1_1->PTRSFreqDensity,&dlsch_config_pdu_1_1->PTRSTimeDensity, &dlsch_config_pdu_1_1->PTRSPortIndex,&dlsch_config_pdu_1_1->nEpreRatioOfPDSCHToPTRS, @@ -1169,15 +1169,14 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ int N_RB = 0, start_RB; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP switch(rnti_type) { case NR_RNTI_RA: case NR_RNTI_TC: case NR_RNTI_P: { - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero) { + if (mac->DLbwp[dl_bwp_id]->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero) { uint8_t coreset_id = 0; // assuming controlResourceSetId is 0 for controlResourceSetZero - NR_ControlResourceSet_t *coreset = mac->coreset[dl_bwp_id-1][coreset_id]; + NR_ControlResourceSet_t *coreset = mac->coreset[dl_bwp_id][coreset_id]; get_coreset_rballoc(coreset->frequencyDomainResources.buf,&N_RB,&start_RB); } else { N_RB = NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -1188,7 +1187,7 @@ int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ N_RB = mac->type0_PDCCH_CSS_config.num_rbs; break; case NR_RNTI_C: - N_RB = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + N_RB = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); break; } return N_RB; @@ -1207,14 +1206,14 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, int fsize = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP int N_RB_UL = 0; if(mac->scc_SIB) { N_RB_UL = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - } else if(mac->ULbwp[ul_bwp_id-1]) { - N_RB_UL = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else if(mac->ULbwp[ul_bwp_id]) { + N_RB_UL = NRRIV2BW(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } else if(mac->scc) { N_RB_UL = NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } @@ -1279,7 +1278,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, #endif // check BWP id - if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->DLbwp[dl_bwp_id]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); // Freq domain assignment (275rb >> fsize = 16) @@ -1462,7 +1461,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, case NR_RNTI_TC: // check BWP id - if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->DLbwp[dl_bwp_id]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); // indicating a DL DCI format - 1 bit @@ -1537,7 +1536,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, break; case NR_UL_DCI_FORMAT_0_0: - if (mac->ULbwp[ul_bwp_id-1]) N_RB_UL=NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->ULbwp[ul_bwp_id]) N_RB_UL=NRRIV2BW(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB_UL=NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); switch(rnti_type) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 5e8774d118..995916b35a 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -94,19 +94,20 @@ void fill_scheduled_response(nr_scheduled_response_t *scheduled_response, */ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { long k2 = -1; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP // Get K2 from RRC configuration - NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id-1] ? mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup : NULL; + NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id] ? mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup : NULL; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; if (pusch_config && pusch_config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp_id-1] && - mac->ULbwp[ul_bwp_id-1]->bwp_Common&& - mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon&& - mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id] && + mac->ULbwp[ul_bwp_id]->bwp_Common&& + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon&& + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else if (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) pusch_TimeDomainAllocationList=mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; @@ -147,8 +148,8 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl // Calculate the index of the UL slot in mac->ul_config_request list. This is // based on the TDD pattern (slot configuration period) and number of UL+mixed // slots in the period. TS 38.213 Sec 11.1 - int mu = mac->ULbwp[ul_bwp_id-1] ? - mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing : + int mu = mac->ULbwp[ul_bwp_id] ? + mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.subcarrierSpacing : mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.subcarrierSpacing; NR_TDD_UL_DL_Pattern_t *tdd_pattern = &tdd_config->pattern1; const int num_slots_per_tdd = nr_slots_per_frame[mu] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity); @@ -166,8 +167,8 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) { NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -307,7 +308,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf uint8_t rank = 0; // We need to initialize rank FIXME!!! NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -512,7 +513,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int N_PRB_oh = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP // Common configuration pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; @@ -529,7 +530,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (rar_grant) { // Note: for Msg3 or MsgA PUSCH transmission the N_PRB_oh is always set to 0 - NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id-1]; + NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id]; NR_BWP_UplinkDedicated_t *ibwp; int scs,abwp_start,abwp_size,startSymbolAndLength,mappingtype; NR_PUSCH_Config_t *pusch_Config=NULL; @@ -634,9 +635,9 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int target_ss; bool valid_ptrs_setup = 0; uint16_t n_RB_ULBWP; - if (mac->ULbwp[ul_bwp_id-1] && mac->ULbwp[ul_bwp_id-1]->bwp_Common) { - n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->ULbwp[ul_bwp_id] && mac->ULbwp[ul_bwp_id]->bwp_Common) { + n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } else { pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -645,7 +646,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->bwp_size = n_RB_ULBWP; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1] ? mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup : NULL; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id] ? mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup : NULL; // Basic sanity check for MCS value to check for a false or erroneous DCI if (dci->mcs > 28) { @@ -665,12 +666,10 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } else if (*dci_format == NR_UL_DCI_FORMAT_0_1) { - /* BANDWIDTH_PART_IND */ - // if (dci->bwp_indicator.val != 1) { - // LOG_W(NR_MAC, "bwp_indicator != 1! Possibly due to false DCI. Ignoring DCI!\n"); - // return -1; - // } config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format); + + ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); ul_ports_config(mac, pusch_config_pdu, dci); @@ -686,12 +685,12 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (pusch_Config && pusch_Config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_Config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[mac->UL_BWP_Id-1] && - mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common&& - mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon&& - mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[mac->UL_BWP_Id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id] && + mac->ULbwp[ul_bwp_id]->bwp_Common&& + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon&& + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else if (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) pusch_TimeDomainAllocationList=mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; @@ -786,9 +785,9 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* DMRS */ l_prime_mask = get_l_prime(pusch_config_pdu->nr_of_symbols, mappingtype, add_pos, dmrslength, pusch_config_pdu->start_symbol_index, mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); - if ((mac->ULbwp[ul_bwp_id-1] && pusch_config_pdu->transform_precoding == transform_precoder_disabled)) + if ((mac->ULbwp[ul_bwp_id] && pusch_config_pdu->transform_precoding == transform_precoder_disabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; - else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[ul_bwp_id-1] && pusch_config_pdu->transform_precoding == transform_precoder_enabled)) + else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[ul_bwp_id] && pusch_config_pdu->transform_precoding == transform_precoder_enabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; // Num PRB Overhead from PUSCH-ServingCellConfig @@ -803,16 +802,16 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, else N_PRB_oh = 0; /* PTRS */ - if (mac->ULbwp[mac->UL_BWP_Id -1] && - mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated && - mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config && - mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup && - mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && - mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + if (mac->ULbwp[ul_bwp_id] && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && + mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; - valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[mac->UL_BWP_Id -1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, + valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, pusch_config_pdu->rb_size, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table, &pusch_config_pdu->pusch_ptrs.ptrs_freq_density,&pusch_config_pdu->pusch_ptrs.ptrs_time_density, &pusch_config_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset,&pusch_config_pdu->pusch_ptrs.num_ptrs_ports, @@ -1122,7 +1121,8 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, uint8_t tda_id){ int delta = 0; - NR_BWP_Uplink_t *ubwp = mac->ULbwp[mac->UL_BWP_Id-1]; + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id]; // Get the numerology to calculate the Tx frame and slot int mu = ubwp ? diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 79a63ceefe..d881847990 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -540,7 +540,7 @@ void nr_initiate_ra_proc(module_id_t module_idP, ra_rnti = 1 + symbol + (slotP * 14) + (freq_index * 14 * 80) + (ul_carrier_id * 14 * 80 * 8); // This should be handled differently when we use the initialBWP for RA - ra->bwp_id = 0; + ra->bwp_id = 0;//TODO NR_BWP_Downlink_t *bwp=NULL; if (ra->CellGroup && ra->CellGroup->spCellConfig && ra->CellGroup->spCellConfig->spCellConfigDedicated && ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 958ceed323..83e96c9bb9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -60,6 +60,7 @@ #define DEBUG_gNB_SCHEDULER 1 #include "common/ran_context.h" +#include "executables/softmodem-common.h" extern RAN_CONTEXT_t RC; @@ -657,8 +658,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, dci_pdu_rel15->mcs, dci_pdu_rel15->tpc, dci_pdu_rel15->ndi, - dci_pdu_rel15->rv, - dci_pdu_rel15->bwp_indicator.val); + dci_pdu_rel15->rv); } const int default_pucch_fmt[] = {0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1}; @@ -1859,7 +1859,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG if (bwpList) AssertFatal(bwpList->list.count <= 4, "downlinkBWP_ToAddModList has %d BWP!\n", bwpList->list.count); - const int bwp_id = *servingCellConfig->firstActiveDownlinkBWP_Id; + const int bwp_id = (get_softmodem_params()->sa ) ? 1 : *servingCellConfig->firstActiveDownlinkBWP_Id; // TODO sched_ctrl->active_bwp = bwpList ? bwpList->list.array[bwp_id - 1] : NULL; const int target_ss = sched_ctrl->active_bwp ? NR_SearchSpace__searchSpaceType_PR_ue_Specific : NR_SearchSpace__searchSpaceType_PR_common; sched_ctrl->search_space = get_searchspace(scc, sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Dedicated : NULL, target_ss); @@ -1992,9 +1992,9 @@ void get_pdsch_to_harq_feedback(int Mod_idP, NR_SearchSpace__searchSpaceType_PR ss_type, uint8_t *pdsch_to_harq_feedback) { - int bwp_id=1; NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; + NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 1 : *CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; NR_BWP_Downlink_t *bwp=NULL; NR_BWP_Uplink_t *ubwp=NULL; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index c7ee3fcff2..2bcc6a7130 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -623,7 +623,7 @@ int checkTargetSSBInTCIStates_pdcchConfig(int ssb_index_t, int Mod_idP, int UE_i int nb_tci_states = CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.count; NR_TCI_State_t *tci =NULL; NR_TCI_StateId_t *tci_id = NULL; - int bwp_id = 1; + int bwp_id = 1;//TODO NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; NR_ControlResourceSet_t *coreset = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]; int i; @@ -691,7 +691,7 @@ void tci_handling(module_id_t Mod_idP, int UE_id, frame_t frame, slot_t slot) { int ssb_index[MAX_NUM_SSB] = {0}; int ssb_rsrp[MAX_NUM_SSB] = {0}; uint8_t idx = 0; - int bwp_id = 1; + int bwp_id = 1;//TODO NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; From f34c9131d9a5af784537f2ddee88b7d326ce7818 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Tue, 13 Jul 2021 14:45:32 +0530 Subject: [PATCH 026/233] Fixed SA mode Error --- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 4 ++-- openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 3 ++- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index e7f51bfa69..1c0e4e22a1 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -989,7 +989,7 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP - if (mac->DLbwp[dl_bwp_id] == NULL) return 0; + if (mac->DLbwp[dl_bwp_id] && mac->DLbwp[dl_bwp_id]->bwp_Dedicated && @@ -1375,7 +1375,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 int find_pucch_resource_set(NR_UE_MAC_INST_t *mac, uint8_t gNB_id, int uci_size) { int pucch_resource_set_id = 0; - NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP //long *pucch_max_pl_bits = NULL; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 008b936504..11c9bbdac9 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -244,7 +244,8 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - uint8_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0, coreset_id = (mac->cg) ? 1 : 0; + NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : ((mac->cg) ? mac->DL_BWP_Id : 0) ; //SA uses initial BWP + uint8_t coreset_id = (mac->cg) ? 1 : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id] : NULL; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 995916b35a..9ce3acd08f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -136,7 +136,7 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { */ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) { - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP NR_TDD_UL_DL_ConfigCommon_t *tdd_config = mac->scc==NULL ? mac->scc_SIB->tdd_UL_DL_ConfigurationCommon : mac->scc->tdd_UL_DL_ConfigurationCommon; //Check if request to access ul_config is for a UL slot @@ -306,7 +306,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf /* ANTENNA_PORTS */ uint8_t rank = 0; // We need to initialize rank FIXME!!! - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP NR_ServingCellConfigCommon_t *scc = mac->scc; NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; From 104e5327317759901b19166db4fe351767c84244 Mon Sep 17 00:00:00 2001 From: Shruthi Date: Fri, 23 Jul 2021 13:42:14 +0530 Subject: [PATCH 027/233] Review comments on SA mode addressed --- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 11 ++++++----- openair2/LAYER2/NR_MAC_UE/config_ue.c | 4 ++-- .../LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 4 ++-- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 14 +++++++------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 14 +++++++------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 6 +++++- 6 files changed, 29 insertions(+), 24 deletions(-) diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index 21672fde2f..45c0ec2df0 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -420,7 +420,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ NR_UE_MAC_INST_t *mac = get_mac_inst(0); NR_PUCCH_Resource_t *pucch_resource = NULL; uint16_t crnti = mac->crnti; - NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t bwp_id = mac->UL_BWP_Id;//SA uses initial BWP /* update current context */ @@ -619,10 +619,11 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ mac->ULbwp[bwp_id] && mac->ULbwp[bwp_id]->bwp_Dedicated && mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup) + mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup) { pucch_Config = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup; BWPsize = NRRIV2BW(mac->ULbwp[bwp_id]->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); BWPstart = NRRIV2PRBOFFSET(mac->ULbwp[bwp_id]->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + } else if (bwp_id==0 && mac->cg && mac->cg->spCellConfig && @@ -990,7 +991,7 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int N_m_c_rx = 0; int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP if (mac->DLbwp[dl_bwp_id] && @@ -1182,7 +1183,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 pucch_format_nr_t format_pucch; int ready_pucch_resource_id = FALSE; /* in the case that it is already given */ NR_PUCCH_Resource_t *pucch_resource = NULL; - NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t bwp_id = mac->UL_BWP_Id;//SA uses initial BWP /* ini values to unset */ *initial_pucch_id = NB_INITIAL_PUCCH_RESOURCE; @@ -1377,7 +1378,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 int find_pucch_resource_set(NR_UE_MAC_INST_t *mac, uint8_t gNB_id, int uci_size) { int pucch_resource_set_id = 0; - NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; //SA uses initial BWP //long *pucch_max_pl_bits = NULL; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 5e158df5f0..059a25aca1 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -601,8 +601,8 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ AssertFatal(scd->downlinkBWP_ToAddModList->list.count <= 4, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); config_bwp_ue(mac, NULL, NULL); - NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; //Changed + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id ;//Changed AssertFatal(dl_bwp_id != 0, "DL_BWP_Id is 0!"); AssertFatal(ul_bwp_id != 0, "UL_BWP_Id is 0!"); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 11c9bbdac9..ff34ca93f0 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -73,7 +73,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t AssertFatal(mac->scc == NULL || mac->scc_SIB == NULL, "both scc and scc_SIB cannot be non-null\n"); - NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; //Changed NR_ServingCellConfigCommon_t *scc = mac->scc; NR_ServingCellConfigCommonSIB_t *scc_SIB = mac->scc_SIB; NR_BWP_DownlinkCommon_t *bwp_Common=NULL; @@ -244,7 +244,7 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 0 : ((mac->cg) ? mac->DL_BWP_Id : 0) ; //SA uses initial BWP + NR_BWP_Id_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0; //Changed uint8_t coreset_id = (mac->cg) ? 1 : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id] : NULL; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index e7348511bb..eb702c57c9 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -280,8 +280,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t sliv_S=0; uint8_t sliv_L=0; uint8_t mu_pusch = 1; - NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP // definition table j Table 6.1.2.1.1-4 uint8_t j = (mu_pusch==3)?3:(mu_pusch==2)?2:1; @@ -459,8 +459,8 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; - NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP uint16_t n_RB_DLBWP; if (mac->DLbwp[dl_bwp_id]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -1169,7 +1169,7 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ int N_RB = 0, start_RB; - NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP switch(rnti_type) { case NR_RNTI_RA: case NR_RNTI_TC: @@ -1206,8 +1206,8 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, int fsize = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ; //SA uses initial BWP - NR_BWP_Id_t dl_bwp_id = get_softmodem_params()->sa ? 0 : mac->DL_BWP_Id ; //SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; //SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP int N_RB_UL = 0; if(mac->scc_SIB) { diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index e38d8069d4..df2a1ba838 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -95,7 +95,7 @@ void fill_scheduled_response(nr_scheduled_response_t *scheduled_response, long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { long k2 = -1; - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP // Get K2 from RRC configuration NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id] ? mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup : NULL; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; @@ -136,7 +136,7 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { */ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) { - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP NR_TDD_UL_DL_ConfigCommon_t *tdd_config = mac->scc==NULL ? mac->scc_SIB->tdd_UL_DL_ConfigurationCommon : mac->scc->tdd_UL_DL_ConfigurationCommon; //Check if request to access ul_config is for a UL slot @@ -167,7 +167,7 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) { NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; @@ -306,7 +306,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf /* ANTENNA_PORTS */ uint8_t rank = 0; // We need to initialize rank FIXME!!! - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP NR_ServingCellConfigCommon_t *scc = mac->scc; NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; @@ -513,7 +513,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int N_PRB_oh = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP // Common configuration pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; @@ -668,7 +668,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format); - ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); @@ -1121,7 +1121,7 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, uint8_t tda_id){ int delta = 0; - NR_BWP_Id_t ul_bwp_id = get_softmodem_params()->sa ? 0 : mac->UL_BWP_Id ;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id]; // Get the numerology to calculate the Tx frame and slot diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 04ac9ea1f1..7f9fcaec12 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1996,7 +1996,11 @@ void get_pdsch_to_harq_feedback(int Mod_idP, NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; - NR_BWP_Id_t bwp_id = get_softmodem_params()->sa ? 1 : *CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; + NR_BWP_Id_t bwp_id; //Changed + if (CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id) + bwp_id = *CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; + else + bwp_id = 1; NR_BWP_Downlink_t *bwp=NULL; NR_BWP_Uplink_t *ubwp=NULL; From 000142c3e3a8de1c7e6e8e81d71934f1f7229fb9 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Thu, 29 Jul 2021 16:00:23 +0530 Subject: [PATCH 028/233] Cosmetic changes --- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 8 ++++---- openair2/LAYER2/NR_MAC_UE/config_ue.c | 7 ++----- .../LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 4 ++-- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 14 +++++++------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 14 +++++++------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 7 ++++--- openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 1 - 7 files changed, 26 insertions(+), 29 deletions(-) diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index 45c0ec2df0..8a62802f35 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -420,7 +420,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ NR_UE_MAC_INST_t *mac = get_mac_inst(0); NR_PUCCH_Resource_t *pucch_resource = NULL; uint16_t crnti = mac->crnti; - NR_BWP_Id_t bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t bwp_id = mac->UL_BWP_Id; /* update current context */ @@ -991,7 +991,7 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int N_m_c_rx = 0; int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; if (mac->DLbwp[dl_bwp_id] && @@ -1183,7 +1183,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 pucch_format_nr_t format_pucch; int ready_pucch_resource_id = FALSE; /* in the case that it is already given */ NR_PUCCH_Resource_t *pucch_resource = NULL; - NR_BWP_Id_t bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t bwp_id = mac->UL_BWP_Id; /* ini values to unset */ *initial_pucch_id = NB_INITIAL_PUCCH_RESOURCE; @@ -1378,7 +1378,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 int find_pucch_resource_set(NR_UE_MAC_INST_t *mac, uint8_t gNB_id, int uci_size) { int pucch_resource_set_id = 0; - NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; //SA uses initial BWP + NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; //long *pucch_max_pl_bits = NULL; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 059a25aca1..5f9dae6e6a 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -601,8 +601,8 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ AssertFatal(scd->downlinkBWP_ToAddModList->list.count <= 4, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); config_bwp_ue(mac, NULL, NULL); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; //Changed - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id ;//Changed + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; AssertFatal(dl_bwp_id != 0, "DL_BWP_Id is 0!"); AssertFatal(ul_bwp_id != 0, "UL_BWP_Id is 0!"); @@ -644,10 +644,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ mac->DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP-1]; mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP-1]; } - mac->coreset[dl_bwp_id][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; - - AssertFatal(mac->ULbwp[ul_bwp_id]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index ff34ca93f0..edc22c6972 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -73,7 +73,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t AssertFatal(mac->scc == NULL || mac->scc_SIB == NULL, "both scc and scc_SIB cannot be non-null\n"); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; //Changed + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; NR_ServingCellConfigCommon_t *scc = mac->scc; NR_ServingCellConfigCommonSIB_t *scc_SIB = mac->scc_SIB; NR_BWP_DownlinkCommon_t *bwp_Common=NULL; @@ -244,7 +244,7 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - NR_BWP_Id_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0; //Changed + NR_BWP_Id_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0; uint8_t coreset_id = (mac->cg) ? 1 : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id] : NULL; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index eb702c57c9..f490a819cb 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -280,8 +280,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t sliv_S=0; uint8_t sliv_L=0; uint8_t mu_pusch = 1; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // definition table j Table 6.1.2.1.1-4 uint8_t j = (mu_pusch==3)?3:(mu_pusch==2)?2:1; @@ -459,8 +459,8 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; uint16_t n_RB_DLBWP; if (mac->DLbwp[dl_bwp_id]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -1169,7 +1169,7 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ int N_RB = 0, start_RB; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; switch(rnti_type) { case NR_RNTI_RA: case NR_RNTI_TC: @@ -1206,8 +1206,8 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, int fsize = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; //SA uses initial BWP - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; //SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; int N_RB_UL = 0; if(mac->scc_SIB) { diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index df2a1ba838..bf192a5c6b 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -95,7 +95,7 @@ void fill_scheduled_response(nr_scheduled_response_t *scheduled_response, long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { long k2 = -1; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Get K2 from RRC configuration NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id] ? mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup : NULL; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; @@ -136,7 +136,7 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { */ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) { - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_TDD_UL_DL_ConfigCommon_t *tdd_config = mac->scc==NULL ? mac->scc_SIB->tdd_UL_DL_ConfigurationCommon : mac->scc->tdd_UL_DL_ConfigurationCommon; //Check if request to access ul_config is for a UL slot @@ -167,7 +167,7 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) { NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; @@ -306,7 +306,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf /* ANTENNA_PORTS */ uint8_t rank = 0; // We need to initialize rank FIXME!!! - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_ServingCellConfigCommon_t *scc = mac->scc; NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; @@ -513,7 +513,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int N_PRB_oh = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Common configuration pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; @@ -668,7 +668,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format); - ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + ul_bwp_id = mac->UL_BWP_Id; target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); @@ -1121,7 +1121,7 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, uint8_t tda_id){ int delta = 0; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id;//SA uses initial BWP + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id]; // Get the numerology to calculate the Tx frame and slot diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 7f9fcaec12..832ee27283 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -60,7 +60,6 @@ #define DEBUG_gNB_SCHEDULER 1 #include "common/ran_context.h" -#include "executables/softmodem-common.h" extern RAN_CONTEXT_t RC; @@ -1861,7 +1860,9 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG if (bwpList) AssertFatal(bwpList->list.count <= 4, "downlinkBWP_ToAddModList has %d BWP!\n", bwpList->list.count); - const int bwp_id = (get_softmodem_params()->sa ) ? 1 : *servingCellConfig->firstActiveDownlinkBWP_Id; // TODO + + const int bwp_id = servingCellConfig ? *servingCellConfig->firstActiveDownlinkBWP_Id : 1; // TODO + sched_ctrl->active_bwp = bwpList ? bwpList->list.array[bwp_id - 1] : NULL; const int target_ss = sched_ctrl->active_bwp ? NR_SearchSpace__searchSpaceType_PR_ue_Specific : NR_SearchSpace__searchSpaceType_PR_common; sched_ctrl->search_space = get_searchspace(scc, sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Dedicated : NULL, target_ss); @@ -1996,7 +1997,7 @@ void get_pdsch_to_harq_feedback(int Mod_idP, NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; - NR_BWP_Id_t bwp_id; //Changed + NR_BWP_Id_t bwp_id; if (CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id) bwp_id = *CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; else diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index 982edf2f9d..fa76fd00bc 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -105,7 +105,6 @@ void handle_nr_uci(NR_UL_IND_t *UL_info) } UL_info->uci_ind.num_ucis = 0; - } void handle_nr_ulsch(NR_UL_IND_t *UL_info) From 94808d4326053909755861a7c1e14297e3a07870 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Fri, 30 Jul 2021 15:57:36 +0530 Subject: [PATCH 029/233] Added switching delay requirements --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 36 ++++++++++--------- .../SCHED_NR_UE/phy_sch_processing_time.h | 34 +++++++++++++++++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 2 -- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 18ef2f66f4..bf39d092bf 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1178,21 +1178,23 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, const double N_TA_max = Ta_max * bw_scaling * tc_factor; NR_UE_MAC_INST_t *mac = get_mac_inst(0); + NR_BWP_Id_t ul_bwp = mac->UL_BWP_Id; + NR_BWP_Id_t dl_bwp = mac->DL_BWP_Id; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[0] && - mac->ULbwp[0]->bwp_Dedicated && - mac->ULbwp[0]->bwp_Dedicated->pusch_Config && - mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup && - mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; + if (mac->ULbwp[ul_bwp] && + mac->ULbwp[ul_bwp]->bwp_Dedicated && + mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config && + mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[0] && - mac->ULbwp[0]->bwp_Common && - mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon && - mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp] && + mac->ULbwp[ul_bwp]->bwp_Common && + mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon && + mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else if (mac->scc_SIB && mac->scc_SIB->uplinkConfigCommon && @@ -1203,12 +1205,12 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, } long mapping_type_ul = pusch_TimeDomainAllocationList ? pusch_TimeDomainAllocationList->list.array[0]->mappingType : NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA; - NR_PDSCH_Config_t *pdsch_Config = (mac->DLbwp[0] && mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup) ? mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + NR_PDSCH_Config_t *pdsch_Config = (mac->DLbwp[dl_bwp] && mac->DLbwp[dl_bwp]->bwp_Dedicated->pdsch_Config->choice.setup) ? mac->DLbwp[dl_bwp]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[0] && mac->DLbwp[0]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + if (mac->DLbwp[dl_bwp] && mac->DLbwp[dl_bwp]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = pdsch_Config->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[0] && mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[0]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + else if (mac->DLbwp[dl_bwp] && mac->DLbwp[dl_bwp]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; long mapping_type_dl = pdsch_TimeDomainAllocationList ? pdsch_TimeDomainAllocationList->list.array[0]->mappingType : NR_PDSCH_TimeDomainResourceAllocation__mappingType_typeA; @@ -1262,7 +1264,7 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, d_2_1 = 1; /* d_2_2 */ - const double d_2_2 = 0.0; // set to 0 because there is only 1 BWP: TODO this should corresponds to the switching time as defined in TS 38.133 + const double d_2_2 = pusch_d_2_2_timing_capability_1[numerology][1]; /* N_t_1 time duration in msec of N_1 symbols corresponding to a PDSCH reception time // N_t_2 time duration in msec of N_2 symbols corresponding to a PUSCH preparation time */ diff --git a/openair1/SCHED_NR_UE/phy_sch_processing_time.h b/openair1/SCHED_NR_UE/phy_sch_processing_time.h index 1962df22ee..81cc4496a2 100644 --- a/openair1/SCHED_NR_UE/phy_sch_processing_time.h +++ b/openair1/SCHED_NR_UE/phy_sch_processing_time.h @@ -106,4 +106,36 @@ float pusch_N_2_timing_capability_2[3][2] = { { 0, 5 }, { 1, 5.5 }, { 2, 11 }, -}; \ No newline at end of file +}; + +/* TS 38.133 Table 8.6.2-1: BWP switch delay capability 1 +// corresponding to the PUSCH preparation time d_2_2 [slots] +// where mu corresponds to the one of {mu_DL, mu_UL} +// resulting with the largest T_proc_2 +// where mu_DL is the SCS with which the PDCCH +// carrying the DCI scheduling the PUSCH was transmitted +// mu_UL is the SCS of the UL channel with which PUSCH to be transmitted +*/ +float pusch_d_2_2_timing_capability_1[4][2] = { +/* mu d_2_2 */ +{ 0, 1 }, +{ 1, 2 }, +{ 2, 3 }, +{ 3, 6 }, +}; + +/* TS 38.133 Table 8.6.2-1: BWP switch delay capability 2 +// corresponding to the PUSCH preparation time d_2_2 [slots] +// where mu corresponds to the one of {mu_DL, mu_UL} +// resulting with the largest T_proc_2 +// where mu_DL is the SCS with which the PDCCH +// carrying the DCI scheduling the PUSCH was transmitted +// mu_UL is the SCS of the UL channel with which PUSCH to be transmitted +*/ +float pusch_d_2_2_timing_capability_2[4][2] = { +/* mu d_2_2 */ +{ 0, 3 }, +{ 1, 5 }, +{ 2, 9 }, +{ 3, 18 }, +}; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 1d0215bb2a..a0df119c1c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -351,8 +351,6 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, protocol_ctxt_t ctxt={0}; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frame, slot,module_idP); - const int bwp_id = 1; - gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; From bd0c5f6030431ed1cc856a4d1b049aeec1341d6c Mon Sep 17 00:00:00 2001 From: Abhijith Date: Thu, 5 Aug 2021 11:07:39 +0530 Subject: [PATCH 030/233] Reverted no. of BWPs to 4 --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 36 ++--- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 87 +++++----- openair2/LAYER2/NR_MAC_UE/config_ue.c | 18 +-- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 2 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 14 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 152 +++++++++--------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 64 ++++---- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 12 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 30 ++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 6 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 28 ++-- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- 14 files changed, 228 insertions(+), 227 deletions(-) diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index bf39d092bf..af429480e0 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1178,23 +1178,23 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, const double N_TA_max = Ta_max * bw_scaling * tc_factor; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_BWP_Id_t ul_bwp = mac->UL_BWP_Id; - NR_BWP_Id_t dl_bwp = mac->DL_BWP_Id; + NR_BWP_Id_t dl_bwp = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; + NR_BWP_Id_t ul_bwp= (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[ul_bwp] && - mac->ULbwp[ul_bwp]->bwp_Dedicated && - mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config && - mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config->choice.setup && - mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; + if (mac->ULbwp[ul_bwp-1] && + mac->ULbwp[ul_bwp-1]->bwp_Dedicated && + mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config && + mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp] && - mac->ULbwp[ul_bwp]->bwp_Common && - mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon && - mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp-1] && + mac->ULbwp[ul_bwp-1]->bwp_Common && + mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon && + mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else if (mac->scc_SIB && mac->scc_SIB->uplinkConfigCommon && @@ -1205,12 +1205,12 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, } long mapping_type_ul = pusch_TimeDomainAllocationList ? pusch_TimeDomainAllocationList->list.array[0]->mappingType : NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA; - NR_PDSCH_Config_t *pdsch_Config = (mac->DLbwp[dl_bwp] && mac->DLbwp[dl_bwp]->bwp_Dedicated->pdsch_Config->choice.setup) ? mac->DLbwp[dl_bwp]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + NR_PDSCH_Config_t *pdsch_Config = (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup) ? mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp] && mac->DLbwp[dl_bwp]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + if (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = pdsch_Config->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp] && mac->DLbwp[dl_bwp]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + else if (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; long mapping_type_dl = pdsch_TimeDomainAllocationList ? pdsch_TimeDomainAllocationList->list.array[0]->mappingType : NR_PDSCH_TimeDomainResourceAllocation__mappingType_typeA; diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index 8a62802f35..468f4716a9 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -504,7 +504,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ if (pucch_resource_set != MAX_NB_OF_PUCCH_RESOURCE_SETS) { pucch_resource_indicator = 0; /* get the first resource of the set */ - pucch_resource_id = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set]->resourceList.list.array[pucch_resource_indicator][0]; + pucch_resource_id = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set]->resourceList.list.array[pucch_resource_indicator][0]; } else { LOG_W(PHY,"PUCCH no resource set found for CSI at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME); @@ -616,13 +616,13 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ } NR_PUCCH_Config_t *pucch_Config; if (bwp_id>0 && - mac->ULbwp[bwp_id] && - mac->ULbwp[bwp_id]->bwp_Dedicated && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup) { - pucch_Config = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup; - BWPsize = NRRIV2BW(mac->ULbwp[bwp_id]->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); - BWPstart = NRRIV2PRBOFFSET(mac->ULbwp[bwp_id]->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + mac->ULbwp[bwp_id-1] && + mac->ULbwp[bwp_id-1]->bwp_Dedicated && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup) { + pucch_Config = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup; + BWPsize = NRRIV2BW(mac->ULbwp[bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + BWPstart = NRRIV2PRBOFFSET(mac->ULbwp[bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); } else if (bwp_id==0 && mac->cg && @@ -654,12 +654,12 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ m_0 = get_ics_pucch(pucch_resource, format); AssertFatal(m_0 >= 0, "Invalid m_0\n"); if (format == pucch_format3_nr) { - if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->additionalDMRS[0] == 1) { + if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->additionalDMRS[0] == 1) { index_additional_dmrs = I_PUCCH_ADDITIONAL_DMRS; } } else if (format == pucch_format4_nr) { - if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->additionalDMRS[0] == 1) { + if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->additionalDMRS[0] == 1) { index_additional_dmrs = I_PUCCH_ADDITIONAL_DMRS; } } @@ -707,7 +707,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ case pucch_format3_nr: { nb_of_prbs = pucch_resource->format.choice.format3->nrofPRBs; - //if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->pi2BPSK[0] == 1) { + //if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format3->choice.setup->pi2BPSK[0] == 1) { // Q_m = BITS_PER_SYMBOL_BPSK; /* set bpsk modulation type with 1 bit per modulation symbol */ //} N_sc_ctrl_RB = N_SC_RB; @@ -716,7 +716,7 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ } case pucch_format4_nr: { - //if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->pi2BPSK[0] == 1) { + //if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format4->choice.setup->pi2BPSK[0] == 1) { // Q_m = BITS_PER_SYMBOL_BPSK; /* set bpsk modulation type with 1 bit per modulation symbol */ //} nb_symbols = nb_symbols_excluding_dmrs[nb_symbols_total-4][index_additional_dmrs][index_hopping]; @@ -733,10 +733,10 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ /* drop CSI report if simultaneous HARQ-ACK/SR and periodic/semi-periodic CSI cannot be transmitted at the same time */ if (format != pucch_format0_nr) { - if (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format1 != NULL) { - //max_code_rate = code_rate_r_time_100[mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->maxCodeRate[0]]; /* it is code rate * 10 */ + if (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format1 != NULL) { + //max_code_rate = code_rate_r_time_100[mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->maxCodeRate[0]]; /* it is code rate * 10 */ - if ((O_ACK != 0) && (mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->simultaneousHARQ_ACK_CSI[0] == 0)) { + if ((O_ACK != 0) && (mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->format1->choice.setup->simultaneousHARQ_ACK_CSI[0] == 0)) { N_UCI = N_UCI - O_CSI; O_CSI = cqi_status = ri_status = 0; csi_payload = 0; /* csi should be dropped in this case */ @@ -861,11 +861,11 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_ switch(format) { case pucch_format0_nr: { - int pucch_GroupHopping = mac->ULbwp[bwp_id] ? - mac->ULbwp[bwp_id]->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping: + int pucch_GroupHopping = mac->ULbwp[bwp_id-1] ? + mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->pucch_GroupHopping: mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->pucch_GroupHopping; - int hoppingId = mac->ULbwp[bwp_id] ? - mac->ULbwp[bwp_id]->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId[0]: + int hoppingId = mac->ULbwp[bwp_id-1] ? + mac->ULbwp[bwp_id-1]->bwp_Common->pucch_ConfigCommon->choice.setup->hoppingId[0]: (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->hoppingId? mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pucch_ConfigCommon->choice.setup->hoppingId[0]: mac->physCellId); @@ -991,14 +991,13 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int N_m_c_rx = 0; int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0 ) ? 1: mac->DL_BWP_Id ; - - if (mac->DLbwp[dl_bwp_id] && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { two_transport_blocks = TRUE; number_of_code_word = 2; } @@ -1078,7 +1077,7 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t if (mac->cg != NULL && mac->cg->physicalCellGroupConfig != NULL && mac->cg->physicalCellGroupConfig->harq_ACK_SpatialBundlingPUCCH != NULL) { - int N_TB_max_DL = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; + int N_TB_max_DL = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0]; *n_HARQ_ACK = (((V_DAI_m_DL - U_DAI_c)%4) * N_TB_max_DL) + N_m_c_rx + N_SPS_c; NR_TST_PHY_PRINTF("PUCCH power n(%d) = ( V(%d) - U(%d) )mod4 * N_TB(%d) + N(%d) \n", *n_HARQ_ACK, V_DAI_m_DL, U_DAI_c, N_TB_max_DL, N_m_c_rx); } @@ -1202,12 +1201,12 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) || - (mac->ULbwp[bwp_id] && - mac->ULbwp[bwp_id]->bwp_Dedicated && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) + (mac->ULbwp[bwp_id-1] && + mac->ULbwp[bwp_id-1]->bwp_Dedicated && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[0] == NULL) ){ /* No resource set has been already configured so pucch_configCommon from Sib1 should be used in this case */ @@ -1275,10 +1274,10 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 // Verify that the value of pucch_resource_indicator is valid struct NR_PUCCH_Config__resourceSetToAddModList *resourceSetToAddModList = NULL; struct NR_PUCCH_Config__resourceToAddModList *resourceToAddModList = NULL; - if (bwp_id > 0 && mac->ULbwp[bwp_id]) { - AssertFatal(mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList!=NULL,"mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList is null\n"); - resourceSetToAddModList = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList; - resourceToAddModList = mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList; + if (bwp_id > 0 && mac->ULbwp[bwp_id-1]) { + AssertFatal(mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList!=NULL,"mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList is null\n"); + resourceSetToAddModList = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList; + resourceToAddModList = mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceToAddModList; } else if (bwp_id == 0 && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList!=NULL) { resourceSetToAddModList = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceSetToAddModList; resourceToAddModList = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->pucch_Config->choice.setup->resourceToAddModList; @@ -1378,7 +1377,7 @@ boolean_t select_pucch_resource(PHY_VARS_NR_UE *ue, NR_UE_MAC_INST_t *mac, uint8 int find_pucch_resource_set(NR_UE_MAC_INST_t *mac, uint8_t gNB_id, int uci_size) { int pucch_resource_set_id = 0; - NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t bwp_id = mac->DL_BWP_Id; //long *pucch_max_pl_bits = NULL; @@ -1392,12 +1391,12 @@ int find_pucch_resource_set(NR_UE_MAC_INST_t *mac, uint8_t gNB_id, int uci_size) /* look for the first resource set which supports uci_size number of bits for payload */ while (pucch_resource_set_id < MAX_NB_OF_PUCCH_RESOURCE_SETS) { if ((bwp_id>0 && - mac->ULbwp[bwp_id] && - mac->ULbwp[bwp_id]->bwp_Dedicated && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && - mac->ULbwp[bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id] != NULL) || + mac->ULbwp[bwp_id-1] && + mac->ULbwp[bwp_id-1]->bwp_Dedicated && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList && + mac->ULbwp[bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[pucch_resource_set_id] != NULL) || (bwp_id==0 && mac->cg && mac->cg->spCellConfig && diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 5f9dae6e6a..c77d72e9e7 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -639,14 +639,14 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ // check pdcch_Config, pdcch_ConfigCommon and DL BWP // Check dedicated UL BWP and pass to MAC - for (int num_BWP = 1; num_BWP < MAX_NUM_BWP ;num_BWP++) + for (int num_BWP = 0; num_BWP < MAX_NUM_BWP ;num_BWP++) { - mac->DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP-1]; - mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP-1]; + mac->DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP]; + mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP]; } - mac->coreset[dl_bwp_id][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; + mac->coreset[dl_bwp_id-1][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; - AssertFatal(mac->ULbwp[ul_bwp_id]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); + AssertFatal(mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); // check available Search Spaces in the searchSpacesToAddModList and pass to MAC // note: the network configures at most 10 Search Spaces per BWP per cell (including UE-specific and common Search Spaces). @@ -654,10 +654,10 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ NR_SearchSpace_t *ss = searchSpacesToAddModList->list.array[ss_id]; AssertFatal(ss->controlResourceSetId != NULL, "ss->controlResourceSetId is null\n"); AssertFatal(ss->searchSpaceType != NULL, "ss->searchSpaceType is null\n"); - AssertFatal(*ss->controlResourceSetId == mac->coreset[dl_bwp_id][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); + AssertFatal(*ss->controlResourceSetId == mac->coreset[dl_bwp_id-1][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); AssertFatal(ss->monitoringSymbolsWithinSlot != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot->buf != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id][0][ss_id] = ss; + mac->SSpace[dl_bwp_id-1][0][ss_id] = ss; } // Check available CSSs in the commonSearchSpaceList (list of additional common search spaces) @@ -665,11 +665,11 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ for (int css_id = 0; css_id < commonSearchSpaceList->list.count; css_id++) { NR_SearchSpace_t *css = commonSearchSpaceList->list.array[css_id]; AssertFatal(css->controlResourceSetId != NULL, "ss->controlResourceSetId is null\n"); - AssertFatal(*css->controlResourceSetId == mac->coreset[dl_bwp_id][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); + AssertFatal(*css->controlResourceSetId == mac->coreset[dl_bwp_id-1][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); AssertFatal(css->searchSpaceType != NULL, "css->searchSpaceType is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot != NULL, "css->monitoringSymbolsWithinSlot is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot->buf != NULL, "css->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id][coreset_id -1][ss_id] = css; + mac->SSpace[dl_bwp_id-1][coreset_id -1][ss_id] = css; ss_id++; } diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 3347ddf19c..6bab72a5a9 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -74,7 +74,7 @@ // ========== #define NB_NR_UE_MAC_INST 1 -#define MAX_NUM_BWP 5 +#define MAX_NUM_BWP 4 #define NUM_SLOT_FRAME 10 /*!\brief value for indicating BSR Timer is not running */ diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index edc22c6972..af500bc31b 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -84,14 +84,14 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t initialDownlinkBWP = scc!=NULL ? scc->downlinkConfigCommon->initialDownlinkBWP : &scc_SIB->downlinkConfigCommon.initialDownlinkBWP; initialUplinkBWP = scc!=NULL ? scc->uplinkConfigCommon->initialUplinkBWP : &scc_SIB->uplinkConfigCommon->initialUplinkBWP; - bwp_Common = dl_bwp_id>0 ? mac->DLbwp[dl_bwp_id]->bwp_Common : NULL; + bwp_Common = dl_bwp_id>0 ? mac->DLbwp[dl_bwp_id-1]->bwp_Common : NULL; } NR_SearchSpace_t *ss; NR_ControlResourceSet_t *coreset; if(ss_id>=0) { - ss = mac->SSpace[dl_bwp_id][coreset_id - 1][ss_id]; - coreset = mac->coreset[dl_bwp_id][coreset_id - 1]; + ss = mac->SSpace[dl_bwp_id-1][coreset_id - 1][ss_id]; + coreset = mac->coreset[dl_bwp_id-1][coreset_id - 1]; rel15->coreset.CoreSetType = NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; } else { ss = mac->search_space_zero; @@ -244,19 +244,19 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - NR_BWP_Id_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0; + NR_BWP_Id_t bwp_id = (mac->cg) ? ((mac->DL_BWP_Id == 0 ) ? 1: mac->DL_BWP_Id ): 0; uint8_t coreset_id = (mac->cg) ? 1 : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; - NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id] : NULL; + NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id-1] : NULL; LOG_D(MAC, "[DCI_CONFIG] ra_rnti %p (%x) crnti %p (%x) t_crnti %p (%x)\n", &ra->ra_rnti, ra->ra_rnti, &mac->crnti, mac->crnti, &ra->t_crnti, ra->t_crnti); if (mac->cg) { // do this only after we have a Master or Secondary Cell group // loop over all available SS for BWP ID 1, CORESET ID 1 if (bwp) { - for (ss_id = 0; ss_id < FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id][coreset_id - 1][ss_id] != NULL; ss_id++){ + for (ss_id = 0; ss_id < FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id-1][coreset_id - 1][ss_id] != NULL; ss_id++){ LOG_D(MAC, "[DCI_CONFIG] ss_id %d\n",ss_id); - NR_SearchSpace_t *ss = mac->SSpace[bwp_id][coreset_id - 1][ss_id]; + NR_SearchSpace_t *ss = mac->SSpace[bwp_id-1][coreset_id - 1][ss_id]; fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; NR_BWP_DownlinkCommon_t *bwp_Common = bwp->bwp_Common; NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index f490a819cb..7bc02b90a8 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -280,8 +280,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t sliv_S=0; uint8_t sliv_L=0; uint8_t mu_pusch = 1; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; + NR_BWP_Id_t ul_bwp_id = (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; // definition table j Table 6.1.2.1.1-4 uint8_t j = (mu_pusch==3)?3:(mu_pusch==2)?2:1; @@ -327,13 +327,13 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(dlsch_config_pdu != NULL){ NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id] && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id] && mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; @@ -386,19 +386,19 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(pusch_config_pdu != NULL){ NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[ul_bwp_id] && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; + if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp_id] && - mac->ULbwp[ul_bwp_id]->bwp_Common && - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon && - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Common && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else pusch_TimeDomainAllocationList = mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; @@ -459,11 +459,12 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; + NR_BWP_Id_t ul_bwp_id = (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; uint16_t n_RB_DLBWP; - if (mac->DLbwp[dl_bwp_id]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + + if (mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else if (mac->scc_SIB) n_RB_DLBWP = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); else n_RB_DLBWP = mac->type0_PDCCH_CSS_config.num_rbs; @@ -629,7 +630,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti = rnti; fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_0 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - NR_PDSCH_Config_t *pdsch_config= (mac->DLbwp[dl_bwp_id]) ? mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + NR_PDSCH_Config_t *pdsch_config = (mac->DLbwp[dl_bwp_id-1]) ? mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; uint16_t BWPSize = n_RB_DLBWP; if(rnti == SI_RNTI) { @@ -655,15 +656,15 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } if (!get_softmodem_params()->sa) { // NSA mode is not using the Initial BWP - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pdsch_config = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup; + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; BWPSize = dlsch_config_pdu_1_0->BWPSize; } - } else if (mac->DLbwp[dl_bwp_id]) { - dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.subcarrierSpacing; - pdsch_config = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup; + } else if (mac->DLbwp[dl_bwp_id-1]) { + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; + pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; } else if (mac->scc_SIB) { dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -685,13 +686,13 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id] && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id] && mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; @@ -703,7 +704,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->number_symbols, dlsch_config_pdu_1_0->start_symbol, mappingtype); - dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id] != NULL) ? (mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; + dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id-1] != NULL) ? (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; /* number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214 version 15.9.0 Release 15 */ if (dlsch_config_pdu_1_0->number_symbols == 2) dlsch_config_pdu_1_0->n_dmrs_cdm_groups = 1; @@ -752,14 +753,14 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - if (mac->ULbwp[ul_bwp_id] && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup&& - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList) { - pucch_res_set_cnt = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup&& + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList) { + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_0->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -855,16 +856,16 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); - NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup; + NR_PDSCH_Config_t *pdsch_config=mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DLSCH; dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti = rnti; fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_1 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.subcarrierSpacing; + dlsch_config_pdu_1_1->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_1->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; /* IDENTIFIER_DCI_FORMATS */ /* CARRIER_IND */ @@ -882,13 +883,13 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id] && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id] && mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; @@ -900,7 +901,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_1->number_symbols, dlsch_config_pdu_1_1->start_symbol, mappingtype); - dlsch_config_pdu_1_1->dmrsConfigType = mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? NFAPI_NR_DMRS_TYPE1 : NFAPI_NR_DMRS_TYPE2; + dlsch_config_pdu_1_1->dmrsConfigType = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? NFAPI_NR_DMRS_TYPE1 : NFAPI_NR_DMRS_TYPE2; /* TODO: fix number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214, using tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of 3GPP TS 38.212 */ dlsch_config_pdu_1_1->n_dmrs_cdm_groups = 1; @@ -949,9 +950,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_1->pucch_resource_id = dci->pucch_resource_indicator; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - pucch_res_set_cnt = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dlsch_config_pdu_1_1->pucch_resource_id < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } @@ -965,7 +966,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr // according to TS 38.213 Table 9.2.3-1 dlsch_config_pdu_1_1->pdsch_to_harq_feedback_time_ind = - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pucch_Config->choice.setup->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; /* ANTENNA_PORTS */ uint8_t n_codewords = 1; // FIXME!!! @@ -1077,8 +1078,8 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr /* TODO same calculation for MCS table as done in UL */ dlsch_config_pdu_1_1->mcs_table = (pdsch_config->mcs_Table) ? (*pdsch_config->mcs_Table + 1) : 0; /*PTRS configuration */ - if(mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) { - valid_ptrs_setup = set_dl_ptrs_values(mac->DLbwp[dl_bwp_id]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup, + if(mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) { + valid_ptrs_setup = set_dl_ptrs_values(mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup, dlsch_config_pdu_1_1->number_rbs, dlsch_config_pdu_1_1->mcs, dlsch_config_pdu_1_1->mcs_table, &dlsch_config_pdu_1_1->PTRSFreqDensity,&dlsch_config_pdu_1_1->PTRSTimeDensity, &dlsch_config_pdu_1_1->PTRSPortIndex,&dlsch_config_pdu_1_1->nEpreRatioOfPDSCHToPTRS, @@ -1169,14 +1170,14 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ int N_RB = 0, start_RB; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; switch(rnti_type) { case NR_RNTI_RA: case NR_RNTI_TC: case NR_RNTI_P: { - if (mac->DLbwp[dl_bwp_id]->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero) { + if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero) { uint8_t coreset_id = 0; // assuming controlResourceSetId is 0 for controlResourceSetZero - NR_ControlResourceSet_t *coreset = mac->coreset[dl_bwp_id][coreset_id]; + NR_ControlResourceSet_t *coreset = mac->coreset[dl_bwp_id-1][coreset_id]; get_coreset_rballoc(coreset->frequencyDomainResources.buf,&N_RB,&start_RB); } else { N_RB = NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -1187,7 +1188,7 @@ int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ N_RB = mac->type0_PDCCH_CSS_config.num_rbs; break; case NR_RNTI_C: - N_RB = NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + N_RB = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); break; } return N_RB; @@ -1206,14 +1207,15 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, int fsize = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; + NR_BWP_Id_t ul_bwp_id = (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; + int N_RB_UL = 0; if(mac->scc_SIB) { N_RB_UL = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - } else if(mac->ULbwp[ul_bwp_id]) { - N_RB_UL = NRRIV2BW(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else if(mac->ULbwp[ul_bwp_id-1]) { + N_RB_UL = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } else if(mac->scc) { N_RB_UL = NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } @@ -1278,7 +1280,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, #endif // check BWP id - if (mac->DLbwp[dl_bwp_id]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); // Freq domain assignment (275rb >> fsize = 16) @@ -1461,7 +1463,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, case NR_RNTI_TC: // check BWP id - if (mac->DLbwp[dl_bwp_id]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); // indicating a DL DCI format - 1 bit @@ -1536,7 +1538,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, break; case NR_UL_DCI_FORMAT_0_0: - if (mac->ULbwp[ul_bwp_id]) N_RB_UL=NRRIV2BW(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->ULbwp[ul_bwp_id-1]) N_RB_UL=NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB_UL=NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); switch(rnti_type) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index bf192a5c6b..0d092c84c4 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -97,17 +97,17 @@ long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) { NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; // Get K2 from RRC configuration - NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id] ? mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup : NULL; + NR_PUSCH_Config_t *pusch_config=mac->ULbwp[ul_bwp_id-1] ? mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup : NULL; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; if (pusch_config && pusch_config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp_id] && - mac->ULbwp[ul_bwp_id]->bwp_Common&& - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon&& - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Common&& + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon&& + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else if (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) pusch_TimeDomainAllocationList=mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; @@ -148,8 +148,8 @@ fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int sl // Calculate the index of the UL slot in mac->ul_config_request list. This is // based on the TDD pattern (slot configuration period) and number of UL+mixed // slots in the period. TS 38.213 Sec 11.1 - int mu = mac->ULbwp[ul_bwp_id] ? - mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.subcarrierSpacing : + int mu = mac->ULbwp[ul_bwp_id-1] ? + mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing : mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.subcarrierSpacing; NR_TDD_UL_DL_Pattern_t *tdd_pattern = &tdd_config->pattern1; const int num_slots_per_tdd = nr_slots_per_frame[mu] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity); @@ -168,7 +168,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con NR_ServingCellConfigCommon_t *scc = mac->scc; NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -308,7 +308,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf uint8_t rank = 0; // We need to initialize rank FIXME!!! NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; NR_ServingCellConfigCommon_t *scc = mac->scc; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup; long transformPrecoder; if (pusch_Config->transformPrecoder) @@ -530,7 +530,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (rar_grant) { // Note: for Msg3 or MsgA PUSCH transmission the N_PRB_oh is always set to 0 - NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id]; + NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id-1]; NR_BWP_UplinkDedicated_t *ibwp; int scs,abwp_start,abwp_size,startSymbolAndLength,mappingtype; NR_PUSCH_Config_t *pusch_Config=NULL; @@ -635,9 +635,9 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int target_ss; bool valid_ptrs_setup = 0; uint16_t n_RB_ULBWP; - if (mac->ULbwp[ul_bwp_id] && mac->ULbwp[ul_bwp_id]->bwp_Common) { - n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->ULbwp[ul_bwp_id]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (mac->ULbwp[ul_bwp_id-1] && mac->ULbwp[ul_bwp_id-1]->bwp_Common) { + n_RB_ULBWP = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } else { pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -646,7 +646,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->bwp_size = n_RB_ULBWP; - NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id] ? mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup : NULL; + NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[ul_bwp_id-1] ? mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup : NULL; // Basic sanity check for MCS value to check for a false or erroneous DCI if (dci->mcs > 28) { @@ -685,12 +685,12 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (pusch_Config && pusch_Config->pusch_TimeDomainAllocationList) { pusch_TimeDomainAllocationList = pusch_Config->pusch_TimeDomainAllocationList->choice.setup; } - else if (mac->ULbwp[ul_bwp_id] && - mac->ULbwp[ul_bwp_id]->bwp_Common&& - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon&& - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + else if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Common&& + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon&& + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp_id-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; } else if (mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) pusch_TimeDomainAllocationList=mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; @@ -785,9 +785,9 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* DMRS */ l_prime_mask = get_l_prime(pusch_config_pdu->nr_of_symbols, mappingtype, add_pos, dmrslength, pusch_config_pdu->start_symbol_index, mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); - if ((mac->ULbwp[ul_bwp_id] && pusch_config_pdu->transform_precoding == transform_precoder_disabled)) + if ((mac->ULbwp[ul_bwp_id-1] && pusch_config_pdu->transform_precoding == transform_precoder_disabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; - else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[ul_bwp_id] && pusch_config_pdu->transform_precoding == transform_precoder_enabled)) + else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[ul_bwp_id-1] && pusch_config_pdu->transform_precoding == transform_precoder_enabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; // Num PRB Overhead from PUSCH-ServingCellConfig @@ -802,16 +802,16 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, else N_PRB_oh = 0; /* PTRS */ - if (mac->ULbwp[ul_bwp_id] && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && - mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; - valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[ul_bwp_id]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, + valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, pusch_config_pdu->rb_size, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table, &pusch_config_pdu->pusch_ptrs.ptrs_freq_density,&pusch_config_pdu->pusch_ptrs.ptrs_time_density, &pusch_config_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset,&pusch_config_pdu->pusch_ptrs.num_ptrs_ports, @@ -1122,7 +1122,7 @@ int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac, int delta = 0; NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; - NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id]; + NR_BWP_Uplink_t *ubwp = mac->ULbwp[ul_bwp_id-1]; // Get the numerology to calculate the Tx frame and slot int mu = ubwp ? diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index a0df119c1c..078cabc5ce 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -380,7 +380,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, } memset(RC.nrmac[module_idP]->cce_list[0][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - for(int i_bwp = 1; i_bwp < MAX_NUM_BWP; i_bwp++) + for(int i_bwp = 0; i_bwp < MAX_NUM_BWP; i_bwp++) memset(RC.nrmac[module_idP]->cce_list[i_bwp][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid i NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 8158e5fcca..d1680f0898 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -988,7 +988,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them const int bwpid = bwp ? bwp->bwp_Id : 0; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -997,7 +997,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1335,9 +1335,9 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them - const int bwpid = bwp ? bwp->bwp_Id : 0; + const int bwpid = bwp ? bwp->bwp_Id : 1; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -1346,7 +1346,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1459,7 +1459,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra NR_DL_DCI_FORMAT_1_0, NR_RNTI_TC, pdsch_pdu_rel15->BWPSize, - bwpid); + (bwpid-1)); // Add padding header and zero rest out if there is space left if (mac_pdu_length < harq->tb_size) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 93d2e959dd..caf1735cb3 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -60,8 +60,8 @@ static int loop_dcch_dtch = DL_SCH_LCID_DTCH; void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t *bwp) { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; - const int bwp_id = bwp ? bwp->bwp_Id : 0; - if (nrmac->preferred_dl_tda[bwp_id]) + const int bwp_id = bwp ? bwp->bwp_Id : 1; + if (nrmac->preferred_dl_tda[bwp_id-1]) return; /* there is a mixed slot only when in TDD */ @@ -120,17 +120,17 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_dl_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id])); + nrmac->preferred_dl_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id-1])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int i = 0; i < n; ++i) { - nrmac->preferred_dl_tda[bwp_id][i] = -1; + nrmac->preferred_dl_tda[bwp_id-1][i] = -1; if (!tdd || i % nr_slots_period < tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id][i] = 0; + nrmac->preferred_dl_tda[bwp_id-1][i] = 0; else if (tdd && nr_mix_slots && i % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id][i] = tdaMi; - LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id][i]); + nrmac->preferred_dl_tda[bwp_id-1][i] = tdaMi; + LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id-1][i]); } } @@ -458,7 +458,7 @@ bool allocate_dl_retransmission(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_bwp ? (f ? 1 : (ps->nrOfSymbols == 2 ? 1 : 2)) : (ps->nrOfSymbols == 2 ? 1 : 2); int rbSize = 0; - const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot] : 1; if (tda == retInfo->time_domain_allocation) { /* Check that there are enough resources for retransmission */ while (rbSize < retInfo->rbSize) { @@ -531,7 +531,7 @@ bool allocate_dl_retransmission(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return false; @@ -634,7 +634,7 @@ void pf_dl(module_id_t module_id, *p = -1; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - int bwp_Id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; + int bwp_Id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 1; const uint16_t rnti = UE_info->rnti[UE_id]; NR_BWP_t *genericParameters = sched_ctrl->active_bwp ? &sched_ctrl->active_bwp->bwp_Common->genericParameters: @@ -666,7 +666,7 @@ void pf_dl(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = mac->cce_list[bwp_Id][cid]; + int *cce_list = mac->cce_list[bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -679,7 +679,7 @@ void pf_dl(module_id_t module_id, max_rbSize++; /* MCS has been set above */ - const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot] : 1; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; @@ -877,9 +877,9 @@ void nr_schedule_ue_spec(module_id_t module_id, /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not * exist, create it */ - const int bwpid = bwp ? bwp->bwp_Id : 0; + const int bwpid = bwp ? bwp->bwp_Id : 1; const int coresetid = bwp ? sched_ctrl->coreset->controlResourceSetId : gNB_mac->sched_ctrlCommon->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; if (!pdcch_pdu) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -891,7 +891,7 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_SearchSpace_t *ss = bwp ? sched_ctrl->search_space:gNB_mac->sched_ctrlCommon->search_space; NR_ControlResourceSet_t *coreset = bwp? sched_ctrl->coreset:gNB_mac->sched_ctrlCommon->coreset; nr_configure_pdcch(pdcch_pdu, ss, coreset, scc, bwp); - gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu; + gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 50a0b241b6..7a824eaec0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -354,7 +354,7 @@ void nr_preprocessor_phytest(module_id_t module_id, frame, slot); UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -369,7 +369,7 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_pdsch->pucch_allocation = alloc; sched_pdsch->rbStart = rbStart; sched_pdsch->rbSize = rbSize; - const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot] : 1; const uint8_t num_dmrs_cdm_grps_no_data = 1; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( @@ -422,7 +422,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 1; + const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 1; if (tda < 0) return false; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 832ee27283..0452c658d8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -188,7 +188,7 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, if(bwp == NULL || bwp->bwp_Id == 0) { cce_list = nr_mac->cce_list[0][0]; } else { - cce_list = nr_mac->cce_list[bwp->bwp_Id][coreset_id]; + cce_list = nr_mac->cce_list[bwp->bwp_Id-1][coreset_id]; } int n_rb=0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 17c7bb92eb..597ef741f0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -67,7 +67,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = ubwp->bwp_Id; - if (nrmac->preferred_ul_tda[bwp_id]) + if (nrmac->preferred_ul_tda[bwp_id-1]) return; /* there is a mixed slot only when in TDD */ @@ -153,18 +153,18 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_ul_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id])); + nrmac->preferred_ul_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id-1])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int slot = 0; slot < n; ++slot) { const int sched_slot = (slot + k2) % n; - nrmac->preferred_ul_tda[bwp_id][slot] = -1; + nrmac->preferred_ul_tda[bwp_id-1][slot] = -1; if (!tdd || sched_slot % nr_slots_period >= tdd->nrofDownlinkSlots + nr_mix_slots) - nrmac->preferred_ul_tda[bwp_id][slot] = 0; + nrmac->preferred_ul_tda[bwp_id-1][slot] = 0; else if (tdd && nr_mix_slots && sched_slot % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_ul_tda[bwp_id][slot] = tdaMi; - LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id][slot]); + nrmac->preferred_ul_tda[bwp_id-1][slot] = tdaMi; + LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id-1][slot]); } if (k2 < tdd->nrofUplinkSlots) @@ -853,7 +853,7 @@ bool allocate_ul_retransmission(module_id_t module_id, const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_bwp ? 1 : 2; - const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; LOG_D(NR_MAC,"retInfo->time_domain_allocation = %d, tda = %d\n", retInfo->time_domain_allocation, tda); if (tda == retInfo->time_domain_allocation) { /* Check the resource is enough for retransmission */ @@ -1046,7 +1046,7 @@ void pf_ul(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_ubwp ? 1 : 2; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; - const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1142,7 +1142,7 @@ void pf_ul(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_ubwp ? 1 : 2; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; - const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1192,7 +1192,7 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * schedule now (slot + k2 is not UL slot) */ int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = sched_ctrl->active_ubwp ? nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; if (tda < 0) return false; int K2 = get_K2(scc, sched_ctrl->active_ubwp, tda, mu); @@ -1543,11 +1543,11 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) /* look up the PDCCH PDU for this BWP and CORESET. If it does not exist, * create it */ - const int bwpid = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; + const int bwpid = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 1; NR_SearchSpace_t *ss = sched_ctrl->active_bwp ? sched_ctrl->search_space: RC.nrmac[module_id]->sched_ctrlCommon->search_space; NR_ControlResourceSet_t *coreset = sched_ctrl->active_bwp? sched_ctrl->coreset: RC.nrmac[module_id]->sched_ctrlCommon->coreset; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid-1][coresetid]; if (!pdcch_pdu) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); @@ -1556,7 +1556,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; nr_configure_pdcch(pdcch_pdu, ss, coreset, scc, sched_ctrl->active_bwp); - pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu; + pdcch_pdu_bwp_coreset[bwpid-1][coresetid] = pdcch_pdu; } LOG_D(NR_MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); @@ -1603,7 +1603,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) ps->dci_format, rnti_types[0], pusch_pdu->bwp_size, - bwpid); + sched_ctrl->active_bwp? bwpid: 0); memset(sched_pusch, 0, sizeof(*sched_pusch)); } diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index ec98e61060..362ebf28ef 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -72,7 +72,7 @@ /* Defs */ -#define MAX_NUM_BWP 5 +#define MAX_NUM_BWP 4 #define MAX_NUM_CORESET 2 #define MAX_NUM_CCE 90 #define MAX_HARQ_ROUNDS 4 From 274ea107ffc0f1c13cd7eea5e41b7df38d193d6a Mon Sep 17 00:00:00 2001 From: Abhijith Date: Mon, 9 Aug 2021 17:38:09 +0530 Subject: [PATCH 031/233] Reverted number of BWPs error at gNB --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 12 ++++---- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 30 +++++++++---------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 6 ++-- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 28 ++++++++--------- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 078cabc5ce..a0df119c1c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -380,7 +380,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, } memset(RC.nrmac[module_idP]->cce_list[0][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - for(int i_bwp = 0; i_bwp < MAX_NUM_BWP; i_bwp++) + for(int i_bwp = 1; i_bwp < MAX_NUM_BWP; i_bwp++) memset(RC.nrmac[module_idP]->cce_list[i_bwp][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid i NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index d1680f0898..8158e5fcca 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -988,7 +988,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them const int bwpid = bwp ? bwp->bwp_Id : 0; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -997,7 +997,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1335,9 +1335,9 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially // important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them - const int bwpid = bwp ? bwp->bwp_Id : 1; + const int bwpid = bwp ? bwp->bwp_Id : 0; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -1346,7 +1346,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra dl_req->nPDUs += 1; pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); - nr_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu_rel15; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; @@ -1459,7 +1459,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra NR_DL_DCI_FORMAT_1_0, NR_RNTI_TC, pdsch_pdu_rel15->BWPSize, - (bwpid-1)); + bwpid); // Add padding header and zero rest out if there is space left if (mac_pdu_length < harq->tb_size) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index caf1735cb3..93d2e959dd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -60,8 +60,8 @@ static int loop_dcch_dtch = DL_SCH_LCID_DTCH; void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t *bwp) { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; - const int bwp_id = bwp ? bwp->bwp_Id : 1; - if (nrmac->preferred_dl_tda[bwp_id-1]) + const int bwp_id = bwp ? bwp->bwp_Id : 0; + if (nrmac->preferred_dl_tda[bwp_id]) return; /* there is a mixed slot only when in TDD */ @@ -120,17 +120,17 @@ void calculate_preferred_dl_tda(module_id_t module_id, const NR_BWP_Downlink_t * const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_dl_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id-1])); + nrmac->preferred_dl_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_dl_tda[bwp_id])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int i = 0; i < n; ++i) { - nrmac->preferred_dl_tda[bwp_id-1][i] = -1; + nrmac->preferred_dl_tda[bwp_id][i] = -1; if (!tdd || i % nr_slots_period < tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id-1][i] = 0; + nrmac->preferred_dl_tda[bwp_id][i] = 0; else if (tdd && nr_mix_slots && i % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_dl_tda[bwp_id-1][i] = tdaMi; - LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id-1][i]); + nrmac->preferred_dl_tda[bwp_id][i] = tdaMi; + LOG_I(MAC, "slot %d preferred_dl_tda %d\n", i, nrmac->preferred_dl_tda[bwp_id][i]); } } @@ -458,7 +458,7 @@ bool allocate_dl_retransmission(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_bwp ? (f ? 1 : (ps->nrOfSymbols == 2 ? 1 : 2)) : (ps->nrOfSymbols == 2 ? 1 : 2); int rbSize = 0; - const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot] : 1; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; if (tda == retInfo->time_domain_allocation) { /* Check that there are enough resources for retransmission */ while (rbSize < retInfo->rbSize) { @@ -531,7 +531,7 @@ bool allocate_dl_retransmission(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return false; @@ -634,7 +634,7 @@ void pf_dl(module_id_t module_id, *p = -1; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - int bwp_Id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 1; + int bwp_Id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; const uint16_t rnti = UE_info->rnti[UE_id]; NR_BWP_t *genericParameters = sched_ctrl->active_bwp ? &sched_ctrl->active_bwp->bwp_Common->genericParameters: @@ -666,7 +666,7 @@ void pf_dl(module_id_t module_id, slot); int cid = sched_ctrl->coreset->controlResourceSetId; UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = mac->cce_list[bwp_Id-1][cid]; + int *cce_list = mac->cce_list[bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -679,7 +679,7 @@ void pf_dl(module_id_t module_id, max_rbSize++; /* MCS has been set above */ - const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot] : 1; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; @@ -877,9 +877,9 @@ void nr_schedule_ue_spec(module_id_t module_id, /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not * exist, create it */ - const int bwpid = bwp ? bwp->bwp_Id : 1; + const int bwpid = bwp ? bwp->bwp_Id : 0; const int coresetid = bwp ? sched_ctrl->coreset->controlResourceSetId : gNB_mac->sched_ctrlCommon->coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); @@ -891,7 +891,7 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_SearchSpace_t *ss = bwp ? sched_ctrl->search_space:gNB_mac->sched_ctrlCommon->search_space; NR_ControlResourceSet_t *coreset = bwp? sched_ctrl->coreset:gNB_mac->sched_ctrlCommon->coreset; nr_configure_pdcch(pdcch_pdu, ss, coreset, scc, bwp); - gNB_mac->pdcch_pdu_idx[CC_id][bwpid-1][coresetid] = pdcch_pdu; + gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu; } nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 7a824eaec0..50a0b241b6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -354,7 +354,7 @@ void nr_preprocessor_phytest(module_id_t module_id, frame, slot); UE_info->num_pdcch_cand[UE_id][cid]--; - int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id-1][cid]; + int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid]; for (int i = 0; i < sched_ctrl->aggregation_level; i++) cce_list[sched_ctrl->cce_index + i] = 0; return; @@ -369,7 +369,7 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_pdsch->pucch_allocation = alloc; sched_pdsch->rbStart = rbStart; sched_pdsch->rbSize = rbSize; - const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id-1][slot] : 1; + const int tda = sched_ctrl->active_bwp ? RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : 1; const uint8_t num_dmrs_cdm_grps_no_data = 1; if (ps->time_domain_allocation != tda || ps->numDmrsCdmGrpsNoData != num_dmrs_cdm_grps_no_data) nr_set_pdsch_semi_static( @@ -422,7 +422,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 1; + const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 1; if (tda < 0) return false; const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList = diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 0452c658d8..832ee27283 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -188,7 +188,7 @@ int allocate_nr_CCEs(gNB_MAC_INST *nr_mac, if(bwp == NULL || bwp->bwp_Id == 0) { cce_list = nr_mac->cce_list[0][0]; } else { - cce_list = nr_mac->cce_list[bwp->bwp_Id-1][coreset_id]; + cce_list = nr_mac->cce_list[bwp->bwp_Id][coreset_id]; } int n_rb=0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 597ef741f0..17c7bb92eb 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -67,7 +67,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub { gNB_MAC_INST *nrmac = RC.nrmac[module_id]; const int bwp_id = ubwp->bwp_Id; - if (nrmac->preferred_ul_tda[bwp_id-1]) + if (nrmac->preferred_ul_tda[bwp_id]) return; /* there is a mixed slot only when in TDD */ @@ -153,18 +153,18 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160}; const int n = slots_per_frame[*scc->ssbSubcarrierSpacing]; - nrmac->preferred_ul_tda[bwp_id-1] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id-1])); + nrmac->preferred_ul_tda[bwp_id] = malloc(n * sizeof(*nrmac->preferred_ul_tda[bwp_id])); const int nr_mix_slots = tdd ? tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0 : 0; const int nr_slots_period = tdd ? tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots : n; for (int slot = 0; slot < n; ++slot) { const int sched_slot = (slot + k2) % n; - nrmac->preferred_ul_tda[bwp_id-1][slot] = -1; + nrmac->preferred_ul_tda[bwp_id][slot] = -1; if (!tdd || sched_slot % nr_slots_period >= tdd->nrofDownlinkSlots + nr_mix_slots) - nrmac->preferred_ul_tda[bwp_id-1][slot] = 0; + nrmac->preferred_ul_tda[bwp_id][slot] = 0; else if (tdd && nr_mix_slots && sched_slot % nr_slots_period == tdd->nrofDownlinkSlots) - nrmac->preferred_ul_tda[bwp_id-1][slot] = tdaMi; - LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id-1][slot]); + nrmac->preferred_ul_tda[bwp_id][slot] = tdaMi; + LOG_I(MAC, "DL slot %d UL slot %d preferred_ul_tda %d\n", slot, sched_slot, nrmac->preferred_ul_tda[bwp_id][slot]); } if (k2 < tdd->nrofUplinkSlots) @@ -853,7 +853,7 @@ bool allocate_ul_retransmission(module_id_t module_id, const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_bwp ? 1 : 2; - const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; LOG_D(NR_MAC,"retInfo->time_domain_allocation = %d, tda = %d\n", retInfo->time_domain_allocation, tda); if (tda == retInfo->time_domain_allocation) { /* Check the resource is enough for retransmission */ @@ -1046,7 +1046,7 @@ void pf_ul(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_ubwp ? 1 : 2; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; - const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1142,7 +1142,7 @@ void pf_ul(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = sched_ctrl->active_ubwp ? 1 : 2; const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = sched_ctrl->active_ubwp ? (f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0) : NR_UL_DCI_FORMAT_0_0; - const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) @@ -1192,7 +1192,7 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t * schedule now (slot + k2 is not UL slot) */ int UE_id = UE_info->list.head; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int tda = sched_ctrl->active_ubwp ? nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id-1][slot] : 0; + const int tda = sched_ctrl->active_ubwp ? nr_mac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; if (tda < 0) return false; int K2 = get_K2(scc, sched_ctrl->active_ubwp, tda, mu); @@ -1543,11 +1543,11 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) /* look up the PDCCH PDU for this BWP and CORESET. If it does not exist, * create it */ - const int bwpid = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 1; + const int bwpid = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; NR_SearchSpace_t *ss = sched_ctrl->active_bwp ? sched_ctrl->search_space: RC.nrmac[module_id]->sched_ctrlCommon->search_space; NR_ControlResourceSet_t *coreset = sched_ctrl->active_bwp? sched_ctrl->coreset: RC.nrmac[module_id]->sched_ctrlCommon->coreset; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid-1][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid]; if (!pdcch_pdu) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); @@ -1556,7 +1556,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; nr_configure_pdcch(pdcch_pdu, ss, coreset, scc, sched_ctrl->active_bwp); - pdcch_pdu_bwp_coreset[bwpid-1][coresetid] = pdcch_pdu; + pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu; } LOG_D(NR_MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot); @@ -1603,7 +1603,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) ps->dci_format, rnti_types[0], pusch_pdu->bwp_size, - sched_ctrl->active_bwp? bwpid: 0); + bwpid); memset(sched_pusch, 0, sizeof(*sched_pusch)); } diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 362ebf28ef..ec98e61060 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -72,7 +72,7 @@ /* Defs */ -#define MAX_NUM_BWP 4 +#define MAX_NUM_BWP 5 #define MAX_NUM_CORESET 2 #define MAX_NUM_CCE 90 #define MAX_HARQ_ROUNDS 4 From 8b1138c89f5dbdba390dfff26ac53844c27483b2 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Mon, 16 Aug 2021 14:12:04 +0530 Subject: [PATCH 032/233] Hardcoding of bwp_id at UE removed --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 39 ++++++----- openair1/SCHED_NR_UE/pucch_uci_ue_nr.c | 18 ++--- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 10 +-- .../NR_MAC_UE/nr_ue_dci_configuration.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 67 ++++++++++++------- 6 files changed, 82 insertions(+), 58 deletions(-) diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index af429480e0..0c3ce4779f 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1178,24 +1178,26 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, const double N_TA_max = Ta_max * bw_scaling * tc_factor; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_BWP_Id_t dl_bwp = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; - NR_BWP_Id_t ul_bwp= (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; + NR_BWP_Id_t dl_bwp = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp = mac->UL_BWP_Id; NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; - if (mac->ULbwp[ul_bwp-1] && + if(ul_bwp){ + if (mac->ULbwp[ul_bwp-1] && mac->ULbwp[ul_bwp-1]->bwp_Dedicated && mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config && mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config->choice.setup && mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; - } - else if (mac->ULbwp[ul_bwp-1] && - mac->ULbwp[ul_bwp-1]->bwp_Common && - mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon && - mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup && - mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { - pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; - } + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp-1]->bwp_Dedicated->pusch_Config->choice.setup->pusch_TimeDomainAllocationList->choice.setup; + } + else if (mac->ULbwp[ul_bwp-1] && + mac->ULbwp[ul_bwp-1]->bwp_Common && + mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon && + mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup && + mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) { + pusch_TimeDomainAllocationList = mac->ULbwp[ul_bwp-1]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + } + } else if (mac->scc_SIB && mac->scc_SIB->uplinkConfigCommon && mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.pusch_ConfigCommon && @@ -1205,12 +1207,15 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, } long mapping_type_ul = pusch_TimeDomainAllocationList ? pusch_TimeDomainAllocationList->list.array[0]->mappingType : NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA; - NR_PDSCH_Config_t *pdsch_Config = (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup) ? mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + NR_PDSCH_Config_t *pdsch_Config = NULL; NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = pdsch_Config->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + if(dl_bwp){ + pdsch_Config = (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup) ? mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + if (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = pdsch_Config->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp-1] && mac->DLbwp[dl_bwp-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + } else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; long mapping_type_dl = pdsch_TimeDomainAllocationList ? pdsch_TimeDomainAllocationList->list.array[0]->mappingType : NR_PDSCH_TimeDomainResourceAllocation__mappingType_typeA; diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c index 468f4716a9..2fe6bc937c 100644 --- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c +++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c @@ -991,15 +991,17 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_t int N_m_c_rx = 0; int V_DAI_m_DL = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0 ) ? 1: mac->DL_BWP_Id ; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; - if (mac->DLbwp[dl_bwp_id-1] && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup && - mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { - two_transport_blocks = TRUE; - number_of_code_word = 2; + if(dl_bwp_id){ + if (mac->DLbwp[dl_bwp_id-1] && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup && + mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI[0] == 2) { + two_transport_blocks = TRUE; + number_of_code_word = 2; + } } else { number_of_code_word = 1; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index c77d72e9e7..b60067ecb8 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -639,7 +639,7 @@ void config_control_ue(NR_UE_MAC_INST_t *mac){ // check pdcch_Config, pdcch_ConfigCommon and DL BWP // Check dedicated UL BWP and pass to MAC - for (int num_BWP = 0; num_BWP < MAX_NUM_BWP ;num_BWP++) + for (int num_BWP = 0; num_BWP < MAX_NUM_BWP_UE ;num_BWP++) { mac->DLbwp[num_BWP] = scd->downlinkBWP_ToAddModList->list.array[num_BWP]; mac->ULbwp[num_BWP] = uplinkBWP_ToAddModList->list.array[num_BWP]; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 6bab72a5a9..9c47c15ad9 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -74,7 +74,7 @@ // ========== #define NB_NR_UE_MAC_INST 1 -#define MAX_NUM_BWP 4 +#define MAX_NUM_BWP_UE 4 #define NUM_SLOT_FRAME 10 /*!\brief value for indicating BSR Timer is not running */ @@ -330,10 +330,10 @@ typedef struct { NR_RNTI_Value_t *cs_RNTI; NR_MIB_t *mib; - NR_BWP_Downlink_t *DLbwp[MAX_NUM_BWP]; - NR_BWP_Uplink_t *ULbwp[MAX_NUM_BWP]; - NR_ControlResourceSet_t *coreset[MAX_NUM_BWP][FAPI_NR_MAX_CORESET_PER_BWP]; - NR_SearchSpace_t *SSpace[MAX_NUM_BWP][FAPI_NR_MAX_CORESET_PER_BWP][FAPI_NR_MAX_SS_PER_CORESET]; + NR_BWP_Downlink_t *DLbwp[MAX_NUM_BWP_UE]; + NR_BWP_Uplink_t *ULbwp[MAX_NUM_BWP_UE]; + NR_ControlResourceSet_t *coreset[MAX_NUM_BWP_UE][FAPI_NR_MAX_CORESET_PER_BWP]; + NR_SearchSpace_t *SSpace[MAX_NUM_BWP_UE][FAPI_NR_MAX_CORESET_PER_BWP][FAPI_NR_MAX_SS_PER_CORESET]; lte_frame_type_t frame_type; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index af500bc31b..42296254a5 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -244,10 +244,10 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - NR_BWP_Id_t bwp_id = (mac->cg) ? ((mac->DL_BWP_Id == 0 ) ? 1: mac->DL_BWP_Id ): 0; + NR_BWP_Id_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0; uint8_t coreset_id = (mac->cg) ? 1 : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; - NR_BWP_Downlink_t *bwp = (mac->cg) ? mac->DLbwp[bwp_id-1] : NULL; + NR_BWP_Downlink_t *bwp = (mac->cg) ? ( (bwp_id) ? mac->DLbwp[bwp_id-1] :NULL) : NULL; LOG_D(MAC, "[DCI_CONFIG] ra_rnti %p (%x) crnti %p (%x) t_crnti %p (%x)\n", &ra->ra_rnti, ra->ra_rnti, &mac->crnti, mac->crnti, &ra->t_crnti, ra->t_crnti); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 7bc02b90a8..a0fa43cd33 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -280,8 +280,8 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t sliv_S=0; uint8_t sliv_L=0; uint8_t mu_pusch = 1; - NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; - NR_BWP_Id_t ul_bwp_id = (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id ; // definition table j Table 6.1.2.1.1-4 uint8_t j = (mu_pusch==3)?3:(mu_pusch==2)?2:1; @@ -327,14 +327,16 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, */ if(dlsch_config_pdu != NULL){ NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1] && + if(dl_bwp_id){ + if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; - else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + } + else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; if (pdsch_TimeDomainAllocationList && use_default==false) { @@ -459,12 +461,13 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; - NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; - NR_BWP_Id_t ul_bwp_id = (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id ; - uint16_t n_RB_DLBWP; - - if (mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + uint16_t n_RB_DLBWP = 0; + if(dl_bwp_id){ + if (mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else if (mac->scc_SIB) n_RB_DLBWP = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); else n_RB_DLBWP = mac->type0_PDCCH_CSS_config.num_rbs; @@ -630,7 +633,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti = rnti; fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_0 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - NR_PDSCH_Config_t *pdsch_config = (mac->DLbwp[dl_bwp_id-1]) ? mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + NR_PDSCH_Config_t *pdsch_config = NULL; + if(dl_bwp_id) + pdsch_config = (mac->DLbwp[dl_bwp_id-1]) ? mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; uint16_t BWPSize = n_RB_DLBWP; if(rnti == SI_RNTI) { @@ -660,11 +665,13 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; BWPSize = dlsch_config_pdu_1_0->BWPSize; } - } else if (mac->DLbwp[dl_bwp_id-1]) { + } else if (dl_bwp_id){ + if(mac->DLbwp[dl_bwp_id-1]) { dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; + } } else if (mac->scc_SIB) { dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -686,14 +693,16 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr } NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1] && + if(dl_bwp_id){ + if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) - pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; - else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; + else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; + } + else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; int mappingtype = pdsch_TimeDomainAllocationList ? pdsch_TimeDomainAllocationList->list.array[dci->time_domain_assignment.val]->mappingType : ((dlsch_config_pdu_1_0->start_symbol <= 3)? typeA: typeB); @@ -704,7 +713,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->number_symbols, dlsch_config_pdu_1_0->start_symbol, mappingtype); - dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id-1] != NULL) ? (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; + if(dl_bwp_id){ + dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id-1] != NULL) ? (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; + } /* number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214 version 15.9.0 Release 15 */ if (dlsch_config_pdu_1_0->number_symbols == 2) dlsch_config_pdu_1_0->n_dmrs_cdm_groups = 1; @@ -1170,7 +1181,7 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ int N_RB = 0, start_RB; - NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; switch(rnti_type) { case NR_RNTI_RA: case NR_RNTI_TC: @@ -1207,8 +1218,8 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, int fsize = 0; int rnti_type = get_rnti_type(mac, rnti); - NR_BWP_Id_t dl_bwp_id = (mac->DL_BWP_Id == 0)? 1 : mac->DL_BWP_Id ; - NR_BWP_Id_t ul_bwp_id = (mac->UL_BWP_Id == 0)? 1 : mac->UL_BWP_Id ; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id ; int N_RB_UL = 0; @@ -1280,7 +1291,10 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, #endif // check BWP id - if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if(dl_bwp_id){ + if (mac->DLbwp[dl_bwp_id-1]) + N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); // Freq domain assignment (275rb >> fsize = 16) @@ -1463,7 +1477,10 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, case NR_RNTI_TC: // check BWP id - if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if(dl_bwp_id){ + if (mac->DLbwp[dl_bwp_id-1]) + N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); // indicating a DL DCI format - 1 bit From 208a6821523292e05fa0b2855d7a829b3b6bfe3b Mon Sep 17 00:00:00 2001 From: Abhijith Date: Fri, 20 Aug 2021 11:50:41 +0530 Subject: [PATCH 033/233] Added documentation --- doc/RUN_NR_multiple_BWPs.md | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 doc/RUN_NR_multiple_BWPs.md diff --git a/doc/RUN_NR_multiple_BWPs.md b/doc/RUN_NR_multiple_BWPs.md new file mode 100644 index 0000000000..f79dc21947 --- /dev/null +++ b/doc/RUN_NR_multiple_BWPs.md @@ -0,0 +1,56 @@ +# Procedure to add dedicated Bandwidth part (BWP) + +## Contributed by 5G Testbed IISc + +### Developers: Abhijith A, Shruthi S + +# Terminology # + +## Bandwidth part (BWP) ## +Bandwidth Part (BWP) is a set of contiguous Resource Blocks in the resource grid. + +Parameters of a Bandwidth Part are communicated to UE using RRC parameters: BWP-Downlink and BWP-Uplink. + +A UE can be configured with a set of 4 BWPs in uplink (UL) and downlink (DL) direction. But only 1 BWP can be active in UL and DL direction at a given time. + +# Procedure to run multiple dedicated BWPs # + +A maximum of 4 dedicated BWPs can be configured for a UE. + +To configure multiple BWPs, add the following parameters to the gNB configuration file under "servingCellConfigDedicated": + +## Setup of the Configuration files ## +``` + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + firstActiveUplinkBWP-Id = 1; #BWP-Id +``` + +Each dedicated BWP must have: +``` + # BWP 1 Configuration + bwp-Id_1 = 1; + bwp1_locationAndBandwidth = 28908; // RBstart=33,L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + bwp1_subcarrierSpacing = 1; +``` + + Find these parameters in this configuration file: "targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf" + +### NOTE: + * For changing bandwidth, 'dl_carrierBandwidth' and 'ul_carrierBandwidth' must be modified according + to the bandwidth of the BWP + * Currently only 'firstActiveDownlinkBWP-Id' is used to configure both DL and UL BWPs + +# Testing gNB and UE in RF simulator + +## gNB command: +``` + sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf --rfsim --phy-test -d +``` + +## UE command: +``` + sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem --rfsim --phy-test -d +``` \ No newline at end of file From ad9e0f5db07f2ba1f8ff0063c2d06977e83bf7c4 Mon Sep 17 00:00:00 2001 From: Abhijith Date: Fri, 27 Aug 2021 11:36:15 +0530 Subject: [PATCH 034/233] Fixed error in fill_default_reconfig() --- openair2/RRC/NR/rrc_gNB_reconfig.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 59d486a62f..420238e751 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -754,7 +754,11 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - + +if (!servingcellconfigdedicated) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + } secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); From 1c60147e866c7a65493643b4b44c1c963292f2ac Mon Sep 17 00:00:00 2001 From: Abhijith Date: Mon, 30 Aug 2021 16:54:57 +0530 Subject: [PATCH 035/233] Merge remote-tracking branch 'origin/develop' into scs_60_iisc --- .../gnb.band261.tm1.32PRB.usrpn300.conf | 7 +- .../gnb.band66.tm1.106PRB.usrpn300.conf | 1 + .../gnb.band78.tm1.fr1.106PRB.usrpb210.conf | 7 +- ci-scripts/xml_files/fr1_nsa_quectel.xml | 8 +- cmake_targets/CMakeLists.txt | 4 +- doc/NR_NFAPI_archi.md | 141 + doc/RUN_NR_NFAPI.md | 54 + executables/nr-gnb.c | 13 +- executables/nr-uesoftmodem.c | 8 +- executables/nr-uesoftmodem.h | 6 +- executables/softmodem-common.h | 10 +- nfapi/nrNFAPI.md | 35 - nfapi/oai_integration/nfapi_pnf.c | 190 +- nfapi/oai_integration/nfapi_pnf.h | 15 +- nfapi/oai_integration/nfapi_vnf.c | 146 +- nfapi/oai_integration/nfapi_vnf.h | 6 +- nfapi/open-nFAPI/common/src/debug.c | 152 +- .../nfapi/public_inc/nfapi_nr_interface_scf | Bin 3986416 -> 0 bytes .../nfapi/public_inc/nfapi_nr_interface_scf.h | 8 +- nfapi/open-nFAPI/nfapi/src/nfapi_p5.c | 4757 ++--- nfapi/open-nFAPI/nfapi/src/nfapi_p7.c | 15310 ++++++++-------- nfapi/open-nFAPI/pnf/inc/pnf_p7.h | 9 +- .../pnf/public_inc/nfapi_pnf_interface.h | 1 + nfapi/open-nFAPI/pnf/src/pnf_p7.c | 305 +- nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c | 74 + nfapi/open-nFAPI/pnf_sim/src/fapi_stub.cpp | 4 +- nfapi/open-nFAPI/vnf/inc/vnf_p7.h | 1 - .../vnf/public_inc/nfapi_vnf_interface.h | 10 +- nfapi/open-nFAPI/vnf/src/vnf_p7.c | 233 +- nfapi/open-nFAPI/vnf/src/vnf_p7_interface.c | 289 +- openair1/PHY/INIT/nr_init.c | 3 + openair1/PHY/INIT/nr_init_ue.c | 2 +- openair1/PHY/MODULATION/modulation_common.h | 2 +- openair1/PHY/MODULATION/nr_modulation.c | 17 + openair1/PHY/MODULATION/nr_modulation.h | 2 + openair1/PHY/MODULATION/ofdm_mod.c | 68 +- openair1/PHY/MODULATION/slot_fep_nr.c | 38 +- .../PHY/NR_ESTIMATION/nr_measurements_gNB.c | 18 +- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 435 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 6 +- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 4 +- openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c | 21 + openair1/PHY/NR_UE_ESTIMATION/filt16a_32.h | 15 +- .../PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c | 116 +- .../nr_dl_channel_estimation.c | 863 +- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 1 + .../NR_UE_TRANSPORT/nr_dlsch_demodulation.c | 6 +- openair1/PHY/NR_UE_TRANSPORT/nr_prach.c | 6 +- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 3 +- .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 23 +- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 5 +- openair1/PHY/TOOLS/signal_energy.c | 4 +- openair1/PHY/TOOLS/tools_defs.h | 7 +- openair1/PHY/defs_gNB.h | 4 + openair1/PHY/defs_nr_UE.h | 5 +- openair1/PHY/defs_nr_common.h | 7 +- openair1/SCHED/nfapi_lte_dummy.c | 4 + openair1/SCHED/nfapi_nr_dummy.c | 4 + openair1/SCHED_NR/fapi_nr_l1.c | 137 +- openair1/SCHED_NR/nr_ru_procedures.c | 2 + openair1/SCHED_NR/phy_procedures_nr_gNB.c | 20 +- openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 5 +- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 67 +- openair1/SIMULATION/LTE_PHY/dlsim.c | 16 +- openair1/SIMULATION/LTE_PHY/test.c | 6 +- openair1/SIMULATION/LTE_PHY/ulsim.c | 4 +- openair1/SIMULATION/LTE_PHY/ulsim2.c | 6 +- openair1/SIMULATION/NR_PHY/dlsim.c | 27 +- .../SIMULATION/NR_PHY/nr_dummy_functions.c | 6 + .../NR_PHY/nr_dummy_functions_prach.c | 14 +- openair1/SIMULATION/NR_PHY/pbchsim.c | 3 + openair1/SIMULATION/NR_PHY/prachsim.c | 5 + openair1/SIMULATION/NR_PHY/ulschsim.c | 2 +- openair1/SIMULATION/NR_PHY/ulsim.c | 62 +- .../unit_tests/src/dummy_functions.c | 5 + openair1/SIMULATION/TOOLS/abstraction.c | 80 +- openair1/SIMULATION/TOOLS/multipath_channel.c | 28 +- openair1/SIMULATION/TOOLS/random_channel.c | 326 +- openair1/SIMULATION/TOOLS/sim.h | 8 +- openair2/GNB_APP/L1_nr_paramdef.h | 11 +- openair2/GNB_APP/gnb_app.c | 41 +- openair2/GNB_APP/gnb_config.c | 2 +- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 20 +- openair2/LAYER2/NR_MAC_UE/mac_proto.h | 26 +- openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 229 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 3 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 302 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 281 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 64 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 446 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 1 + .../NR_MAC_gNB/gNB_scheduler_primitives.c | 55 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 191 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 14 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 5 +- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 9 +- openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 234 +- .../ASN1_files/extract_asn1_from_spce.pl | 60 +- .../asn1c/ASN1_files/nr-rrc-16.4.1.asn1 | 14304 +++++++++++++++ openair2/RRC/NR/rrc_gNB_reconfig.c | 2 +- targets/ARCH/rfsimulator/apply_channelmod.c | 12 +- .../CONF/gnb.band257.tm1.32PRB.usrpn300.conf | 6 +- .../CONF/gnb.band257.tm1.32PRB.usrpx300.conf | 6 +- .../CONF/gnb.band257.tm1.66PRB.usrpn300.conf | 6 +- .../CONF/gnb.band261.tm1.32PRB.usrpn300.conf | 1 + .../CONF/gnb.band66.tm1.106PRB.usrpn300.conf | 1 + .../CONF/gnb.band66.tm1.106PRB.usrpx300.conf | 1 + .../CONF/gnb.band78.106PRB.slave.conf | 6 +- .../gnb.band78.tm1.106PRB.PTRS.usrpx300.conf | 1 + .../CONF/gnb.band78.tm1.106PRB.usrpb210.conf | 6 +- .../CONF/gnb.band78.tm1.106PRB.usrpn300.conf | 1 + .../CONF/gnb.band78.tm1.106PRB.usrpx300.conf | 1 + .../CONF/gnb.band78.tm1.217PRB.usrpn300.conf | 11 +- .../CONF/gnb.band78.tm1.217PRB.usrpx300.conf | 11 +- .../CONF/gnb.band78.tm1.24PRB.usrpb210.conf | 1 + .../CONF/gnb.band78.tm1.24PRB.usrpn300.conf | 1 + .../CONF/gnb.band78.tm1.24PRB.usrpx300.conf | 1 + .../CONF/gnb.band78.tm1.273PRB.usrpn300.conf | 11 +- .../CONF/oaiL1.nfapi.usrpx300.conf | 3 + .../GENERIC-LTE-EPC/CONF/testing_gnb.conf | 1 + .../CONF/testing_gnb_24PRB.conf | 1 + .../gnb.sa.band41.fr1.106PRB.usrpb210.conf | 1 + .../gnb.sa.band78.fr1.106PRB.usrpb210.conf | 1 + 123 files changed, 28619 insertions(+), 12094 deletions(-) create mode 100644 doc/NR_NFAPI_archi.md create mode 100644 doc/RUN_NR_NFAPI.md delete mode 100644 nfapi/nrNFAPI.md delete mode 100644 nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf create mode 100644 openair2/RRC/NR/MESSAGES/asn1c/ASN1_files/nr-rrc-16.4.1.asn1 diff --git a/ci-scripts/conf_files/gnb.band261.tm1.32PRB.usrpn300.conf b/ci-scripts/conf_files/gnb.band261.tm1.32PRB.usrpn300.conf index a4d9985dca..8d403cf4ce 100644 --- a/ci-scripts/conf_files/gnb.band261.tm1.32PRB.usrpn300.conf +++ b/ci-scripts/conf_files/gnb.band261.tm1.32PRB.usrpn300.conf @@ -236,9 +236,10 @@ MACRLCs = ( L1s = ( { - num_cc = 1; - tr_n_preference = "local_mac"; - pusch_proc_threads = 8; + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/ci-scripts/conf_files/gnb.band66.tm1.106PRB.usrpn300.conf b/ci-scripts/conf_files/gnb.band66.tm1.106PRB.usrpn300.conf index 1209a6f227..05c71c082e 100644 --- a/ci-scripts/conf_files/gnb.band66.tm1.106PRB.usrpn300.conf +++ b/ci-scripts/conf_files/gnb.band66.tm1.106PRB.usrpn300.conf @@ -219,6 +219,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf index 33855f2c0e..df2fd874ca 100644 --- a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf +++ b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf @@ -231,10 +231,11 @@ MACRLCs = ( L1s = ( { - num_cc = 1; - tr_n_preference = "local_mac"; + num_cc = 1; + tr_n_preference = "local_mac"; pusch_proc_threads = 6; - } + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 + } ); RUs = ( diff --git a/ci-scripts/xml_files/fr1_nsa_quectel.xml b/ci-scripts/xml_files/fr1_nsa_quectel.xml index 7ec3a5f140..6c11a66d91 100644 --- a/ci-scripts/xml_files/fr1_nsa_quectel.xml +++ b/ci-scripts/xml_files/fr1_nsa_quectel.xml @@ -97,7 +97,7 @@ Ping: 20pings in 20sec idefix -c 20 - 50 + 1 @@ -105,7 +105,7 @@ Ping: 100pings in 20sec idefix -c 100 -i 0.2 - 50 + 1 @@ -114,7 +114,7 @@ -u -b 20M -t 60 DL idefix - 50 + 3 single-ue @@ -124,7 +124,7 @@ -u -b 3M -t 60 UL idefix - 50 + 1 single-ue diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 44678b4c91..9592334125 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -457,8 +457,8 @@ include_directories ("${RRC_FULL_DIR}") #NR RRC ####### set (NR_RRC_ASN1_VERSION "NR_Rel16" ) -make_version(NR_RRC_VERSION 16 1 0) -set (NR_RRC_GRAMMAR ${OPENAIR2_DIR}/RRC/NR/MESSAGES/asn1c/ASN1_files/nr-rrc-16.1.0.asn1) +make_version(NR_RRC_VERSION 16 4 1) +set (NR_RRC_GRAMMAR ${OPENAIR2_DIR}/RRC/NR/MESSAGES/asn1c/ASN1_files/nr-rrc-16.4.1.asn1) add_definitions(-DNR_RRC_VERSION=${NR_RRC_VERSION}) set (NR_RRC_FULL_DIR ${asn1_generated_dir}/RRC_${NR_RRC_ASN1_VERSION}) diff --git a/doc/NR_NFAPI_archi.md b/doc/NR_NFAPI_archi.md new file mode 100644 index 0000000000..59c511fb52 --- /dev/null +++ b/doc/NR_NFAPI_archi.md @@ -0,0 +1,141 @@ +# global + +xnf is pnf or vnf + +The xnf starting functions are configure_nfapi_xnf() + +The OAI code that read the configuration parameters call these functions that will create autonomous thread for xnf control part + +These control threads create a SCTP socket by Linux API call (no use of OpenAir internal architecture with SCTP itti thread). + +The main() function of softmodem has to create and start the other threads+loop on event for appropriate work: RF Rx in pnf, NGAP, GTP-U, X2, RLC, PDCP for vnf + +NFAPI has it's own code directly on Linux+Posix, it doesn't reuse any piece of the OpenAir framework: SCTP thread, thread creation and management, ... + +## signaling (P5) xnf main loop + +nfapi_vnf_start() create the SCTP socket, then do event waiting in a infinite loop while(vnf->terminate == 0). It accepts the pnf connections, then process the messages on reception + +nfapi_pnf_start() initiate connection to vnf until it has a link. Then it enter a similar infinite loop in pnf_message_pump() + +After some checks, when a message is obtained, it calls pnf_handle_p5_message() or vnf_handle_p4_p5_message() that have a switch on each p5 message types: the enum nfapi_message_id_e + +Each message type has it's own processing function in the switch, like +``` +case NFAPI_START_RESPONSE: + vnf_handle_start_response(pRecvMsg, recvMsgLen, config, p5_idx); + break; +``` + +These loops are autonomous in their thread waiting incoming message. + +## P7 xnf main loop + +When the p5 interface receives appropriate message, it starts the p7 interface by launching a thread (see the calls to pthread_create() in nfapi/oai_integration/nfapi_xnf.c) + +The p7 main loops starting is a bit simpler, thanks to UDP non connected protocol. The xnf_dispatch_p7_message() do p7 fragments re-assembly, then +calls xnf_dispatch_p7_message() that have the big switch on message type (as in p5 above description) + +So, we have the logic for UL reception in vnf, and DL reception in pnf + +## P7 UL transmission by PNF + +RF samples are received, and decoding is done by the PNF using control data transmitted by the VNF to the PNF through downlink p7 messages (UL_TTI_req and UL_DCI_req). + +After decoding, results are accumulated into the xNB->UL_INFO structure at the PNF. + +The data in the UL_INFO struct is transmitted through a socket in the form of 'uplink indication functions' from the PNF to the VNF. Each uplink indication message is transmitted from their respective handle functions in NR_UL_indication(). For example, + +``` + +void handle_nr_rach(NR_UL_IND_t *UL_info) { + if(NFAPI_MODE == NFAPI_MODE_PNF) { + if (UL_info->rach_ind.number_of_pdus>0) { + oai_nfapi_nr_rach_indication(&UL_info->rach_ind); //This function calls the routines required for packing + transmission through socket + UL_info->rach_ind.number_of_pdus = 0; + } + } + +``` + +## P7 UL reception at VNF + +Through the infinite loop [while(vnf_p7->terminate == 0)] running in nfapi_nr_vnf_p7_start(), the VNF receives and unpacks the uplink indication message received on its socket. Based on the unpacked messages, UL_INFO struct on the VNF side gets populated. + +``` + +// have a p7 message + if(FD_ISSET(vnf_p7->socket, &rfds)) + { + vnf_nr_p7_read_dispatch_message(vnf_p7); + } + +``` + +vnf_nr_dispatch_p7_message() is the function that contains the switch on various message headers so that the appropriate unpack function is called. + +## P7 DL Transmission by VNF + +DL messages are scheduled at the VNF, through NR_UL_indication(). NR_UL_indication() is called when the SFN/slot in the UL_info structure changes (this acts as a trigger for next slot processing, instead of running a separate clock at the VNF). The SFN/slot at the VNF in UL_info is updated using the slot_indication uplink p7 message, which is sent at the beginning of every slot by the PNF. The slot_indication message contains SFN/slot of the TX_thread, so that the scheduler operates slot_ahead slots ahead of the RX thread. This ensures that UL_tti_req is received before RX slot processing at the PNF. + +The function NR_schedule_response calls oai_nfapi_[DL P7 msg]_req(), which contains the routines for packing + transmission of scheduled messages through the socket to the PNF. For example, to send the 'TX_data_req' p7 message + +``` + +if (Sched_INFO->TX_req->Number_of_PDUs > 0) +{ + oai_nfapi_tx_data_req(Sched_INFO->TX_req); +} + +``` + +```mermaid + +graph TD + pselect[VNF socket pselect] -- timed out : end of slot --> call_sched[Call Scheduler NR_UL_indication] -- oai_nfapi_***_req sends the DL p7 msg--> slot_inc[Increment sfn/slot]; + pselect[VNF socket pselect] -- UL p7 xyz msg recvd --> msg_recvd[Read message vnf_nr_p7_read_dispatch_message] --> header_unpack[Unpack Header] -- switch cases on header --> unpack_msg[Handle Message vnf_handle_nr_xyz] --> fill_ul_info[Fill UL info struct with fn pointer vnf_p7->_public.nr_rx_xyz]; + fill_ul_info -- update pselect_timeout: next_slot_start - time_xyz_msg_recvd-->pselect; + slot_inc -- next slot --> pselect + +``` +Note that since DL P7 message reception and TX/RX processing are done on separate threads, there is the issue of the L1 processing threads trying to do their job before the required P7 message is received. In the case of RX processing, since the scheduler operates slot_ahead slots ahead of the RX thread, the required messages conveniently arrive earlier than they are required. However, in the case of TX processing, this cannot be ensured if the scheduler is exactly in sync with the TX thread. + +Therefore, we operate the VNF vnf_slot_ahead (which is currently 2) slots ahead of the PNF. This is to ensure that the DL fapi structures for a particular TX slot are all received before TX processing for that slot. + +## P7 DL Reception at PNF + +Through the infinite loop [while(pnf_p7->terminate == 0)] running in pnf_nr_p7_message_pump(), the PNF receives and unpacks the downlink P7 message received on its socket. Based on the unpacked message, the appropriate message structures are filled in the PNF, and these are used further down the pipeline for processing. + +While receiving the DL P7 message, we check whether the message was received within a timing window from which it was sent. The duration of the window can be set by the user (set as a parameter for xnf in the p5 messages). Note that the DL information must be received by the PNF within a timing window at least lesser than the duration of slot_ahead variable (timing_window <= slot_ahead * slot_duration). + +```mermaid + +graph TB + pselect[PNF socket pselect] -- timed out : end of slot --> slot_inc[Increment sfn/slot]; + pselect[PNF socket pselect] -- DL p7 xyz msg recvd --> msg_recvd[Read message pnf_nr_nfapi_p7_read_dispatch_message] --> header_unpack[Unpack Header] -- switch cases on header --> unpack_msg[Unpack Message pnf_handle_nr_xyz] --> fill_pnf_p7[Fill pnf_p7 global structure pnf_handle_nr_xyz] --Data from pnf_p7 struct copied to fapi structures using pnf_phy_***_req. Called every slot from handle_nr_slot_ind-->pselect; + + slot_inc -- next slot --> pselect +``` +Once the messages are received, they are filled into slot buffers, and are stored until the processing of the slot that they were meant for. + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/RUN_NR_NFAPI.md b/doc/RUN_NR_NFAPI.md new file mode 100644 index 0000000000..ea7c2590c7 --- /dev/null +++ b/doc/RUN_NR_NFAPI.md @@ -0,0 +1,54 @@ +# Procedure to run nFAPI in 5G NR + +## Contributed by 5G Testbed IISc + +### Developers: Gokul S, Mahesh A, Aniq U R + +## Procedure to Build gNB and UE + +The regular commands to build gNB and UE can be used +``` +sudo ./build_oai --gNB --nrUE + +``` +## Procedure to run NR nFAPI using RF-Simulator + +### Bring up another loopback interface + +If running for the first time on your computer, or you have restarted your computer, bring up another loopback interface with this command: + +sudo ifconfig lo: 127.0.0.2 netmask 255.0.0.0 up + +### VNF command +``` +sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/rcc.band78.tm1.106PRB.nfapi.conf --nfapi 2 --noS1 --phy-test + +``` +### PNF command +``` +sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/oaiL1.nfapi.usrpx300.conf --nfapi 1 --rfsim --phy-test --rfsimulator.serveraddr server + +``` +### UE command +``` +sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem --rfsim --phy-test --rrc_config_path . -d + +``` +## Procedure to run NR nFAPI using Hardware (tested with USRP x310) + +### VNF command +``` +sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/rcc.band78.tm1.106PRB.nfapi.conf --nfapi 2 --noS1 --phy-test + +``` +### PNF command +``` +sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/oaiL1.nfapi.usrpx300.conf --nfapi 1 --phy-test + +``` +### UE command +``` +sudo ./nr-uesoftmodem --usrp-args "addr=*USRP_ADDRESS*,clock_source=external,time_source=external" --phy-test --rrc_config_path ../../../ci-scripts/rrc-files + +``` + diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index e9319f9d3c..ce09531e78 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -85,6 +85,7 @@ #include "T.h" #include "nfapi/oai_integration/vendor_ext.h" +#include "executables/softmodem-common.h" #include #include #include @@ -146,15 +147,12 @@ void rx_func(void *param) { start_meas(&softmodem_stats_rxtx_sf); // ******************************************************************* - // NFAPI not yet supported for NR - this code has to be revised + if (NFAPI_MODE == NFAPI_MODE_PNF) { // I am a PNF and I need to let nFAPI know that we have a (sub)frame tick - //add_subframe(&frame, &subframe, 4); - //oai_subframe_ind(proc->frame_tx, proc->subframe_tx); - //LOG_D(PHY, "oai_subframe_ind(frame:%u, subframe:%d) - NOT CALLED ********\n", frame, subframe); + //LOG_D(PHY, "oai_nfapi_slot_ind(frame:%u, slot:%d) ********\n", frame_rx, slot_rx); start_meas(&nfapi_meas); - // oai_subframe_ind(frame_rx, slot_rx); - oai_slot_ind(frame_rx, slot_rx); + handle_nr_slot_ind(frame_rx, slot_rx); stop_meas(&nfapi_meas); /*if (gNB->UL_INFO.rx_ind.rx_indication_body.number_of_pdus|| @@ -451,6 +449,8 @@ void init_eNB_afterRU(void) { PHY_VARS_gNB *gNB; LOG_I(PHY,"%s() RC.nb_nr_inst:%d\n", __FUNCTION__, RC.nb_nr_inst); + if(NFAPI_MODE == NFAPI_MODE_PNF) + RC.nb_nr_inst = 1; for (inst=0; instUL_INFO.cqi_ind.cqi_raw_pdu_list = gNB->cqi_raw_pdu_list;*/ gNB->prach_energy_counter = 0; + gNB->prb_interpolation = get_softmodem_params()->prb_interpolation; } diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 9475cc8657..395801f488 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -252,6 +252,7 @@ void init_tpools(uint8_t nun_dlsch_threads) { } static void get_options(void) { + nrUE_params.ofdm_offset_divisor = 8; paramdef_t cmdline_params[] =CMDLINE_NRUEPARAMS_DESC ; int numparams = sizeof(cmdline_params)/sizeof(paramdef_t); config_process_cmdline( cmdline_params,numparams,NULL); @@ -318,8 +319,8 @@ void set_options(int CC_id, PHY_VARS_NR_UE *UE){ UE->rf_map.chain = CC_id + chain_offset; - LOG_I(PHY,"Set UE mode %d, UE_fo_compensation %d, UE_scan_carrier %d, UE_no_timing_correction %d \n", - UE->mode, UE->UE_fo_compensation, UE->UE_scan_carrier, UE->no_timing_correction); + LOG_I(PHY,"Set UE mode %d, UE_fo_compensation %d, UE_scan_carrier %d, UE_no_timing_correction %d \n, do_prb_interpolation %d\n", + UE->mode, UE->UE_fo_compensation, UE->UE_scan_carrier, UE->no_timing_correction, UE->prb_interpolation); // Set FP variables @@ -335,6 +336,8 @@ void set_options(int CC_id, PHY_VARS_NR_UE *UE){ LOG_I(PHY, "Set UE N_RB_DL %d\n", N_RB_DL); LOG_I(PHY, "Set UE nb_rx_antenna %d, nb_tx_antenna %d, threequarter_fs %d\n", fp->nb_antennas_rx, fp->nb_antennas_tx, fp->threequarter_fs); + fp->ofdm_offset_divisor = nrUE_params.ofdm_offset_divisor; + } void init_openair0(void) { @@ -496,6 +499,7 @@ int main( int argc, char **argv ) { mac->scc == NULL ? 78 : *mac->scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]); init_symbol_rotation(&UE[CC_id]->frame_parms); + init_timeshift_rotation(&UE[CC_id]->frame_parms); init_nr_ue_vars(UE[CC_id], 0, abstraction_flag); #ifdef FR2_TEST diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index be3ca2d1a2..3eba5731b0 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -8,7 +8,8 @@ -#define CONFIG_HLP_DLSCH_PARA "number of threads for dlsch processing 0 for no parallelization" +#define CONFIG_HLP_DLSCH_PARA "number of threads for dlsch processing 0 for no parallelization\n" +#define CONFIG_HLP_OFFSET_DIV "Divisor for computing OFDM symbol offset in Rx chain (num samples in CP/). Default value is 8. To set the sample offset to 0, set this value ~ 10e6\n" /***************************************************************************************************************************************/ /* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument when calling config_get or config_getlist functions */ @@ -29,6 +30,7 @@ {"usrp-args", CONFIG_HLP_USRP_ARGS, 0, strptr:(char **)&usrp_args, defstrval:"type=b200", TYPE_STRING, 0}, \ {"single-thread-disable", CONFIG_HLP_NOSNGLT, PARAMFLAG_BOOL, iptr:&single_thread_flag, defintval:1, TYPE_INT, 0}, \ {"dlsch-parallel", CONFIG_HLP_DLSCH_PARA, 0, iptr:(int32_t *)&nrUE_params.nr_dlsch_parallel, defintval:0, TYPE_UINT8, 0}, \ + {"offset-divisor", CONFIG_HLP_OFFSET_DIV, 0, uptr:(uint32_t *)&nrUE_params.ofdm_offset_divisor, defuintval:UINT_MAX, TYPE_UINT32, 0}, \ {"nr-dlsch-demod-shift", CONFIG_HLP_DLSHIFT, 0, iptr:(int32_t *)&nr_dlsch_demod_shift, defintval:0, TYPE_INT, 0}, \ {"V" , CONFIG_HLP_VCD, PARAMFLAG_BOOL, iptr:&vcdflag, defintval:0, TYPE_INT, 0}, \ {"rrc_config_path", CONFIG_HLP_RRC_CFG_PATH,0, strptr:(char **)&rrc_config_path, defstrval:"./", TYPE_STRING, 0} \ @@ -60,11 +62,13 @@ {"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, u8ptr:&(fp->threequarter_fs), defintval:0, TYPE_UINT8, 0}, \ {"T" , CONFIG_HLP_TDD, PARAMFLAG_BOOL, iptr:&tddflag, defintval:0, TYPE_INT, 0}, \ {"ue-timing-correction-disable", CONFIG_HLP_DISABLETIMECORR, PARAMFLAG_BOOL, iptr:&(UE->no_timing_correction), defintval:0, TYPE_INT, 0}, \ + {"do-prb-interpolation", CONFIG_HLP_PRBINTER, PARAMFLAG_BOOL, iptr:&(UE->prb_interpolation), defintval:0, TYPE_INT, 0}, \ } typedef struct { uint64_t optmask; //mask to store boolean config options + uint32_t ofdm_offset_divisor; // Divisor for sample offset computation for each OFDM symbol uint8_t nr_dlsch_parallel; // number of threads for dlsch decoding, 0 means no parallelization tpool_t Tpool; // thread pool } nrUE_params_t; diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index b6a2b18215..76904f2368 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -72,6 +72,7 @@ extern "C" #define CONFIG_HLP_DLMCS "Set the maximum downlink MCS\n" #define CONFIG_HLP_STMON "Enable processing timing measurement of lte softmodem on per subframe basis \n" #define CONFIG_HLP_256QAM "Use the 256 QAM mcs table for PDSCH\n" +#define CONFIG_HLP_PRBINTER "Do PRB based averaging of channel estimates. Frequency domain linear interpolation by default\n" #define CONFIG_HLP_NONSTOP "Go back to frame sync mode after 100 consecutive PBCH failures\n" //#define CONFIG_HLP_NUMUES "Set the number of UEs for the emulation" @@ -119,6 +120,7 @@ extern "C" #define SEND_DMRSSYNC softmodem_params.send_dmrs_sync #define USIM_TEST softmodem_params.usim_test #define USE_256QAM_TABLE softmodem_params.use_256qam_table +#define PRB_INTERPOLATION softmodem_params.prb_interpolation #define NFAPI softmodem_params.nfapi #define NON_STOP softmodem_params.non_stop @@ -153,9 +155,10 @@ extern int usrp_tx_thread; {"nokrnmod", CONFIG_HLP_NOKRNMOD, PARAMFLAG_BOOL, uptr:&nokrnmod, defintval:0, TYPE_INT, 0}, \ {"nbiot-disable", CONFIG_HLP_DISABLNBIOT, PARAMFLAG_BOOL, uptr:&nonbiot, defuintval:0, TYPE_INT, 0}, \ {"use-256qam-table", CONFIG_HLP_256QAM, PARAMFLAG_BOOL, iptr:&USE_256QAM_TABLE, defintval:0, TYPE_INT, 0}, \ - {"usrp-tx-thread-config", CONFIG_HLP_USRP_THREAD, 0, iptr:&usrp_tx_thread, defstrval:0, TYPE_INT, 0}, \ - {"nfapi", CONFIG_HLP_NFAPI, 0, u8ptr:&nfapi_mode, defintval:0, TYPE_UINT8, 0}, \ - {"non-stop", CONFIG_HLP_NONSTOP, PARAMFLAG_BOOL, iptr:&NON_STOP, defintval:0, TYPE_INT, 0}, \ + {"do-prb-interpolation", CONFIG_HLP_PRBINTER, PARAMFLAG_BOOL, iptr:&PRB_INTERPOLATION, defintval:0, TYPE_INT, 0}, \ + {"usrp-tx-thread-config", CONFIG_HLP_USRP_THREAD, 0, iptr:&usrp_tx_thread, defstrval:0, TYPE_INT, 0}, \ + {"nfapi", CONFIG_HLP_NFAPI, 0, u8ptr:&nfapi_mode, defintval:0, TYPE_UINT8, 0}, \ + {"non-stop", CONFIG_HLP_NONSTOP, PARAMFLAG_BOOL, iptr:&NON_STOP, defintval:0, TYPE_INT, 0}, \ } @@ -246,6 +249,7 @@ typedef struct { int hw_timing_advance; uint32_t send_dmrs_sync; int use_256qam_table; + int prb_interpolation; uint8_t nfapi; int non_stop; } softmodem_params_t; diff --git a/nfapi/nrNFAPI.md b/nfapi/nrNFAPI.md deleted file mode 100644 index 299a43fd93..0000000000 --- a/nfapi/nrNFAPI.md +++ /dev/null @@ -1,35 +0,0 @@ -# Procedure to run nFAPI in 5G NR - -## Conributed by 5G Testbed IISC -### Developers: Sudhakar B,Mahesh K,Gokul S,Aniq U.R - -## Procedure to Build gNB and UE - -The regular commands to build gNB and UE can be used -``` -sudo ./build_oai --gNB --UE - -``` -## Procedure to run NR nFAPI using RF-Simulator - -### VNF command -``` -sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/rcc.band78.tm1.106PRB.nfapi.conf --nfapi 2 --noS1 --phy-test - -``` -### PNF command -``` -sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/oaiL1.nfapi.usrpx300.conf --nfapi 1 --rfsim --phy-test --rfsimulator.serveraddr server - -``` -### UE command -``` -sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem --rfsim --phy-test --rrc_config_path . -d - -``` -## Procedure to run NR nFAPI using Hardware -Will be updated as we have not yet currently tested on hardware - -## Notes -* In order to acheive the synchronization between VNF and PNF and receive the P7 messages within the timing window the order in which we should run the modules on different terminals is UE->VNF->PNF -* Currently only downlink is functional and working as we are still working on uplink functionality diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index d9c5df199c..2b1900200a 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -37,8 +37,6 @@ #include "nfapi_pnf.h" #include "common/ran_context.h" #include "openair2/PHY_INTERFACE/phy_stub_UE.h" -//#include "openair1/PHY/vars.h" -extern RAN_CONTEXT_t RC; #include #include @@ -50,7 +48,7 @@ extern RAN_CONTEXT_t RC; #include #include "fapi_stub.h" -//#include "fapi_l1.h" + #include "common/utils/LOG/log.h" #include "PHY/INIT/phy_init.h" @@ -62,7 +60,7 @@ extern RAN_CONTEXT_t RC; #include #include #include - +#include "nfapi/open-nFAPI/pnf/inc/pnf_p7.h" #define NUM_P5_PHY 2 @@ -70,6 +68,7 @@ extern RAN_CONTEXT_t RC; extern void phy_init_RU(RU_t *); extern int config_sync_var; +extern RAN_CONTEXT_t RC; extern pthread_cond_t nfapi_sync_cond; extern pthread_mutex_t nfapi_sync_mutex; @@ -1110,18 +1109,14 @@ int pnf_phy_ul_dci_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, // LOG_D(PHY,"[PNF] HI_DCI0_REQUEST SFN/SF:%05d dci:%d hi:%d\n", NFAPI_SFNSF2DEC(req->sfn_sf), req->hi_dci0_request_body.number_of_dci, req->hi_dci0_request_body.number_of_hi); - //phy_info* phy = (phy_info*)(pnf_p7->user_data); struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; if (proc ==NULL) proc = &gNB->proc.L1_proc; for (int i=0; inumPdus; i++) { - //LOG_D(PHY,"[PNF] HI_DCI0_REQ sfn_sf:%d PDU[%d]\n", NFAPI_SFNSF2DEC(req->sfn_sf), i); if (req->ul_dci_pdu_list[i].PDUType == 0) { - //LOG_D(PHY,"[PNF] HI_DCI0_REQ sfn_sf:%d PDU[%d] - NFAPI_HI_DCI0_DCI_PDU_TYPE\n", NFAPI_SFNSF2DEC(req->sfn_sf), i); - nfapi_nr_ul_dci_request_pdus_t *ul_dci_req_pdu = &req->ul_dci_pdu_list[i]; - int SFN=req->SFN+2; - handle_nfapi_nr_ul_dci_pdu(gNB, SFN, req->Slot, ul_dci_req_pdu); + nfapi_nr_ul_dci_request_pdus_t *ul_dci_req_pdu = &req->ul_dci_pdu_list[i]; + handle_nfapi_nr_ul_dci_pdu(gNB, req->SFN, req->Slot, ul_dci_req_pdu); } else { LOG_E(PHY,"[PNF] UL_DCI_REQ sfn_slot:%d PDU[%d] - unknown pdu type:%d\n", NFAPI_SFNSLOT2DEC(req->SFN, req->Slot), i, req->ul_dci_pdu_list[i].PDUType); @@ -1181,45 +1176,37 @@ int pnf_phy_dl_tti_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, int sfn = req->SFN; int slot = req->Slot; - struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; if (proc==NULL) proc = &gNB->proc.L1_proc; nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu_list = req->dl_tti_request_body.dl_tti_pdu_list; - if (req->dl_tti_request_body.nPDUs) - NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() TX:%d/%d RX:%d/%d; sfn:%d, slot:%d, nGroup:%u, nPDUs: %u, nUE: %u, PduIdx: %u,\n", - __FUNCTION__, proc->frame_tx, proc->slot_tx, proc->frame_rx, proc->slot_rx, // TODO: change subframes to slot - req->SFN, - req->Slot, - req->dl_tti_request_body.nGroup, - req->dl_tti_request_body.nPDUs, - req->dl_tti_request_body.nUe, - req->dl_tti_request_body.PduIdx); + //if (req->dl_tti_request_body.nPDUs) + // NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() TX:%d/%d RX:%d/%d; sfn:%d, slot:%d, nGroup:%u, nPDUs: %u, nUE: %u, PduIdx: %u,\n", + // __FUNCTION__, proc->frame_tx, proc->slot_tx, proc->frame_rx, proc->slot_rx, // TODO: change subframes to slot + // req->SFN, + // req->Slot, + // req->dl_tti_request_body.nGroup, + // req->dl_tti_request_body.nPDUs, + // req->dl_tti_request_body.nUe, + // req->dl_tti_request_body.PduIdx); for (int i=0; idl_tti_request_body.nPDUs; i++) { - // TODO: enable after adding gNB PDCCH: // NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() sfn/sf:%d PDU[%d] size:%d pdcch_vars->num_dci:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(req->sfn_sf), i, dl_config_pdu_list[i].pdu_size,pdcch_vars->num_dci); if (dl_tti_pdu_list[i].PDUType == NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE) { nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu=&dl_tti_pdu_list[i]; - memcpy(dl_tti_pdu,&dl_tti_pdu_list[i],sizeof(nfapi_nr_dl_tti_request_pdu_t)); - int SFN=sfn+2; - handle_nfapi_nr_pdcch_pdu(gNB, SFN, slot, &dl_tti_pdu->pdcch_pdu); - //dl_tti_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15.numDlDci++; // ? - // NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() pdcch_vars->num_dci:%d\n", __FUNCTION__, pdcch_vars->num_dci); - } else if (dl_tti_pdu_list[i].PDUType == NFAPI_NR_DL_TTI_SSB_PDU_TYPE) { - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() PDU:%d BCH: pdu_index:%u pdu_length:%d sdu_length:%d BCH_SDU:%x,%x,%x\n", __FUNCTION__, i, pdu_index, bch_pdu->bch_pdu_rel8.length, tx_request_pdu[sfn][sf][pdu_index]->segments[0].segment_length, sdu[0], sdu[1], sdu[2]); - handle_nr_nfapi_ssb_pdu(gNB, sfn, slot, &dl_tti_pdu_list[i]); - gNB->pbch_configured=1; - //} else { - // NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() BCH NULL TX PDU SFN/SF:%d PDU_INDEX:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(req->sfn_sf), pdu_index); - //} - } else if (dl_tti_pdu_list[i].PDUType == NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE) { + handle_nfapi_nr_pdcch_pdu(gNB, sfn, slot, &dl_tti_pdu->pdcch_pdu); + } + else if (dl_tti_pdu_list[i].PDUType == NFAPI_NR_DL_TTI_SSB_PDU_TYPE) { + //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() PDU:%d BCH: pdu_index:%u pdu_length:%d sdu_length:%d BCH_SDU:%x,%x,%x\n", __FUNCTION__, i, pdu_index, bch_pdu->bch_pdu_rel8.length, tx_request_pdu[sfn][sf][pdu_index]->segments[0].segment_length, sdu[0], sdu[1], sdu[2]); + handle_nr_nfapi_ssb_pdu(gNB, sfn, slot, &dl_tti_pdu_list[i]); + gNB->pbch_configured=1; + } + else if (dl_tti_pdu_list[i].PDUType == NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE) { nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu = &dl_tti_pdu_list[i].pdsch_pdu; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15_pdu = &pdsch_pdu->pdsch_pdu_rel15; - //nfapi_nr_tx_data_request_t *tx_data = tx_data_request[sfn][slot][rel15_pdu->pduIndex]; - nfapi_nr_pdu_t *tx_data = tx_data_request[sfn][slot][0]; + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15_pdu = &pdsch_pdu->pdsch_pdu_rel15; + nfapi_nr_pdu_t *tx_data = tx_data_request[sfn][slot][rel15_pdu->pduIndex]; if (tx_data != NULL) { int UE_id = find_nr_dlsch(rel15_pdu->rnti,gNB,SEARCH_EXIST_OR_FREE); @@ -1232,14 +1219,21 @@ int pnf_phy_dl_tti_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, LOG_E(PHY,"pnf_phy_dl_config_req illegal harq_pid %d\n", harq_pid); return(-1); } - //uint8_t *dlsch_sdu = (uint8_t *)tx_data->TLVs[0].value.direct; + uint8_t *dlsch_sdu = nr_tx_pdus[UE_id][harq_pid]; - memcpy(dlsch_sdu, tx_data->TLVs[0].value.direct,tx_data->PDU_length);//TODO: Check if required + memcpy(dlsch_sdu, tx_data->TLVs[0].value.direct,tx_data->PDU_length); //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() DLSCH:pdu_index:%d handle_nfapi_dlsch_pdu(eNB, proc_rxtx, dlsch_pdu, transport_blocks:%d sdu:%p) eNB->pdcch_vars[proc->subframe_tx & 1].num_pdcch_symbols:%d\n", __FUNCTION__, rel8_pdu->pdu_index, rel8_pdu->transport_blocks, dlsch_sdu, eNB->pdcch_vars[proc->subframe_tx & 1].num_pdcch_symbols); handle_nr_nfapi_pdsch_pdu(gNB, sfn, slot,pdsch_pdu, dlsch_sdu); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() DLSCH NULL TX PDU SFN/SF:%d PDU_INDEX:%d\n", __FUNCTION__, NFAPI_SFNSLOT2DEC(sfn,slot), rel15_pdu->pduIndex); } - } else { + } + else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() DLSCH NULL TX PDU SFN/SF:%d PDU_INDEX:%d\n", __FUNCTION__, NFAPI_SFNSLOT2DEC(sfn,slot), rel15_pdu->pduIndex); + } + } + else if (dl_tti_pdu_list[i].PDUType == NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE) { + nfapi_nr_dl_tti_csi_rs_pdu *csi_rs_pdu = &dl_tti_pdu_list[i].csi_rs_pdu; + handle_nfapi_nr_csirs_pdu(gNB, sfn, slot, csi_rs_pdu); + } + else { NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() UNKNOWN:%d\n", __FUNCTION__, dl_tti_pdu_list[i].PDUType); } } @@ -1399,13 +1393,10 @@ int pnf_phy_tx_req(nfapi_pnf_p7_config_t *pnf_p7, nfapi_tx_request_t *req) { int pnf_phy_ul_tti_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfapi_nr_ul_tti_request_t *req) { - // if (0)LOG_D(PHY,"[PNF] UL_CONFIG_REQ %s() sfn_sf:%d pdu:%d rach_prach_frequency_resources:%d srs_present:%u\n", - // __FUNCTION__, - // NFAPI_SFNSF2DEC(req->sfn_sf), - // req->ul_config_request_body.number_of_pdus, - // req->ul_config_request_body.rach_prach_frequency_resources, - // req->ul_config_request_body.srs_present - // ); + LOG_D(PHY,"[PNF] UL_TTI_REQ recvd, writing into structs, SFN/slot:%d.%d pdu:%d \n", + req->SFN,req->Slot, + req->n_pdus + ); if (RC.ru == 0) { return -1; @@ -2162,7 +2153,7 @@ void configure_nr_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_add pnf.phys[0].udp.tx_port = vnf_p7_port; strcpy(pnf.phys[0].udp.tx_addr, vnf_ip_addr); strcpy(pnf.phys[0].local_addr, pnf_ip_addr); - printf("%s() VNF:%s:%d PNF_PHY[addr:%s UDP:tx_addr:%s:%u rx:%u]\n", + printf("%s() VNF:%s:%d PNF_PHY[addr:%s UDP:tx_addr:%s:%d rx:%d]\n", __FUNCTION__,config->vnf_ip_addr, config->vnf_p5_port, pnf.phys[0].local_addr, pnf.phys[0].udp.tx_addr, pnf.phys[0].udp.tx_port, @@ -2213,7 +2204,7 @@ void configure_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_addr, pnf.phys[0].udp.tx_port = vnf_p7_port; strcpy(pnf.phys[0].udp.tx_addr, vnf_ip_addr); strcpy(pnf.phys[0].local_addr, pnf_ip_addr); - printf("%s() VNF:%s:%d PNF_PHY[addr:%s UDP:tx_addr:%s:%u rx:%u]\n", + printf("%s() VNF:%s:%d PNF_PHY[addr:%s UDP:tx_addr:%s:%d rx:%d]\n", __FUNCTION__, config->vnf_ip_addr, config->vnf_p5_port, pnf.phys[0].local_addr, @@ -2272,50 +2263,24 @@ void oai_subframe_ind(uint16_t sfn, uint16_t sf) { } } +void handle_nr_slot_ind(uint16_t sfn, uint16_t slot) { -long shift_ns,prev_ts_nsec,shift_us; -void oai_slot_ind(uint16_t sfn, uint16_t slot) { - - //slow down PNF - - LOG_D(PHY,"%s(sfn:%d, slot:%d)\n", __FUNCTION__, sfn, slot); - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - - // if (!(sfn == 0 && slot == 0) && ts.tv_nsec > prev_ts_nsec){ - // shift_ns = ts.tv_nsec - prev_ts_nsec; - // shift_us = shift_ns/1000; - // printf("previous: %d, current: %d, shift: %d", prev_ts_nsec, ts.tv_nsec, shift_us); - // if(500-shift_us > 0) - // usleep(500-shift_us); - // // usleep(50); - // } - prev_ts_nsec = ts.tv_nsec; - - -//NFAPI_TRACE(NFAPI_TRACE_INFO, "[PNF] %s %d.%d (sfn:%u slot:%u) SFN/SLOT(TX):%u\n", __FUNCTION__, ts.tv_sec, ts.tv_nsec, sfn, slot, NFAPI_SFNSLOT2DEC(sfn, slot)); - //TODO FIXME - HACK - DJP - using a global to bodge it in - if (p7_config_g != NULL && sync_var==0) { - // DONE: changed for NR x x x x x x x x x x x x x x - - - - - - : x (Frame), - (Slot) (max_numer =2) - uint16_t sfn_slot_tx = sfn<<6 | slot; - // if ((sfn % 100 == 0) && slot==0) { // DOUBT: Why 100? - // struct timespec ts; - // clock_gettime(CLOCK_MONOTONIC, &ts); - // NFAPI_TRACE(NFAPI_TRACE_INFO, "[PNF] %s %d.%d (sfn:%u slot:%u) SFN/SLOT(TX):%u\n", __FUNCTION__, ts.tv_sec, ts.tv_nsec, sfn, slot, NFAPI_SFNSLOT2DEC(sfn, slot)); - // } - - //TODO: send p7_config instead of p7_config_g - int slot_ret = nfapi_pnf_p7_slot_ind(p7_config_g, p7_config_g->phy_id, sfn, slot); + //send VNF slot indication, which is aligned with TX thread, so that it can call the scheduler + nfapi_nr_slot_indication_scf_t *ind; + ind = (nfapi_nr_slot_indication_scf_t *) malloc(sizeof(nfapi_nr_slot_indication_scf_t)); + uint8_t slot_ahead = 6; + uint32_t sfn_slot_tx = sfnslot_add_slot(sfn, slot, slot_ahead); + uint16_t sfn_tx = NFAPI_SFNSLOT2SFN(sfn_slot_tx); + uint8_t slot_tx = NFAPI_SFNSLOT2SLOT(sfn_slot_tx); - // if (subframe_ret) { - if (slot_ret) { - NFAPI_TRACE(NFAPI_TRACE_INFO, "[PNF] %s(frame:%u slot:%u) SFN/SLOT(TX):%u - PROBLEM with pnf_p7_slot_ind()\n", __FUNCTION__, sfn, slot, sfn_slot_tx, NFAPI_SFNSLOT2DEC(sfn, slot)); - // printing anything causes error: probably because there isn't enough time to accomodate a print statement - } else { - //NFAPI_TRACE(NFAPI_TRACE_INFO, "***NFAPI subframe handler finished *** \n"); - } - } else { - } + ind->sfn = sfn_tx; + ind->slot = slot_tx; + oai_nfapi_nr_slot_indication(ind); + + //copy data from appropriate p7 slot buffers into channel structures for PHY processing + nfapi_pnf_p7_slot_ind(p7_config_g, p7_config_g->phy_id, sfn, slot); + + return; } int oai_nfapi_rach_ind(nfapi_rach_indication_t *rach_ind) { @@ -2366,3 +2331,42 @@ int oai_nfapi_sr_indication(nfapi_sr_indication_t *ind) { //free(ind.rx_indication_body.rx_pdu_list); return retval; } + +//NR UPLINK INDICATION + +int oai_nfapi_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind) { + ind->header.phy_id = 1; + ind->header.message_id = NFAPI_NR_PHY_MSG_TYPE_SLOT_INDICATION; + return nfapi_pnf_p7_nr_slot_ind(p7_config_g, ind); +} + +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { + ind->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! + ind->header.message_id = NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION; + return nfapi_pnf_p7_nr_rx_data_ind(p7_config_g, ind); +} + +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { + ind->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! + ind->header.message_id = NFAPI_NR_PHY_MSG_TYPE_CRC_INDICATION; + return nfapi_pnf_p7_nr_crc_ind(p7_config_g, ind); +} + +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { + ind->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! + ind->header.message_id = NFAPI_NR_PHY_MSG_TYPE_SRS_INDICATION; + return nfapi_pnf_p7_nr_srs_ind(p7_config_g, ind); +} + +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { + ind->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! + ind->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UCI_INDICATION; + return nfapi_pnf_p7_nr_uci_ind(p7_config_g, ind); +} + +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { + ind->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! + ind->header.message_id = NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION; + return nfapi_pnf_p7_nr_rach_ind(p7_config_g, ind); +} + diff --git a/nfapi/oai_integration/nfapi_pnf.h b/nfapi/oai_integration/nfapi_pnf.h index 61d4847eba..b7ebdb36ea 100644 --- a/nfapi/oai_integration/nfapi_pnf.h +++ b/nfapi/oai_integration/nfapi_pnf.h @@ -19,13 +19,20 @@ * contact@openairinterface.org */ -#if !defined(NFAPI_PNF_H__) -#define NFAPI_PNF_H__ + extern nfapi_ue_release_request_body_t release_rntis; int oai_nfapi_rach_ind(nfapi_rach_indication_t *rach_ind); void configure_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port); void configure_nr_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port); void oai_subframe_ind(uint16_t sfn, uint16_t sf); -void oai_slot_ind(uint16_t sfn, uint16_t slot); -#endif +void handle_nr_slot_ind(uint16_t sfn, uint16_t slot); +uint32_t sfnslot_add_slot(uint16_t sfn, uint16_t slot, int offset); + +int oai_nfapi_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind); +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind); +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind); +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind); +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind); +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind); + diff --git a/nfapi/oai_integration/nfapi_vnf.c b/nfapi/oai_integration/nfapi_vnf.c index f4448e71df..d96829e127 100644 --- a/nfapi/oai_integration/nfapi_vnf.c +++ b/nfapi/oai_integration/nfapi_vnf.c @@ -33,10 +33,10 @@ #include "nfapi_nr_interface_scf.h" #include "nfapi_vnf_interface.h" +#include "nfapi_vnf.h" #include "nfapi.h" #include "vendor_ext.h" -#include "nfapi_vnf.h" #include "PHY/defs_eNB.h" #include "PHY/LTE_TRANSPORT/transport_proto.h" #include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h" @@ -239,6 +239,18 @@ void oai_enb_init(void) { void oai_create_gnb(void) { int bodge_counter=0; + + if (RC.gNB == NULL) { + RC.gNB = (PHY_VARS_gNB **) calloc(1, sizeof(PHY_VARS_gNB *)); + LOG_I(PHY,"gNB L1 structure RC.gNB allocated @ %p\n",RC.gNB); + } + + + if (RC.gNB[0] == NULL) { + RC.gNB[0] = (PHY_VARS_gNB *) calloc(1, sizeof(PHY_VARS_gNB)); + LOG_I(PHY,"[nr-gnb.c] gNB structure RC.gNB[%d] allocated @ %p\n",0,RC.gNB[0]); + } + PHY_VARS_gNB *gNB = RC.gNB[0]; RC.nb_nr_CC = (int *)malloc(sizeof(int)); // TODO: find a better function to place this in @@ -584,7 +596,7 @@ extern pthread_mutex_t nfapi_sync_mutex; extern int nfapi_sync_var; int phy_sync_indication(struct nfapi_vnf_p7_config *config, uint8_t sync) { - printf("[VNF] SYNC %s\n", sync==1 ? "ACHIEVED" : "LOST"); + //printf("[VNF] SYNC %s\n", sync==1 ? "ACHIEVED" : "LOST"); if (sync==1 && nfapi_sync_var!=0) { @@ -1002,6 +1014,124 @@ int phy_cqi_indication(struct nfapi_vnf_p7_config *config, nfapi_cqi_indication_ return 1; } +//NR phy indication + +int phy_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind) { + + uint8_t vnf_slot_ahead = 2; + uint32_t vnf_sfn_slot = sfnslot_add_slot(ind->sfn, ind->slot, vnf_slot_ahead); + uint16_t vnf_sfn = NFAPI_SFNSLOT2SFN(vnf_sfn_slot); + uint8_t vnf_slot = NFAPI_SFNSLOT2SLOT(vnf_sfn_slot); //offsetting the vnf from pnf by vnf_slot_head slots + struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; + pthread_mutex_lock(&gNB->UL_INFO_mutex); + gNB->UL_INFO.frame = vnf_sfn; + gNB->UL_INFO.slot = vnf_slot; + pthread_mutex_unlock(&gNB->UL_INFO_mutex); + LOG_D(MAC, "VNF SFN/Slot %d.%d \n", gNB->UL_INFO.frame, gNB->UL_INFO.slot); + + return 1; +} + +int phy_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { + struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; + pthread_mutex_lock(&gNB->UL_INFO_mutex); + + gNB->UL_INFO.crc_ind = *ind; + + if (ind->number_crcs > 0) + gNB->UL_INFO.crc_ind.crc_list = malloc(sizeof(nfapi_nr_crc_t)*ind->number_crcs); + + for (int i=0; inumber_crcs; i++) + memcpy(&gNB->UL_INFO.crc_ind.crc_list[i], &ind->crc_list[i], sizeof(ind->crc_list[0])); + + pthread_mutex_unlock(&gNB->UL_INFO_mutex); + + return 1; +} + +int phy_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { + struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; + pthread_mutex_lock(&gNB->UL_INFO_mutex); + + gNB->UL_INFO.rx_ind = *ind; + + if (ind->number_of_pdus > 0) + gNB->UL_INFO.rx_ind.pdu_list = malloc(sizeof(nfapi_nr_rx_data_pdu_t)*ind->number_of_pdus); + + for (int i=0; inumber_of_pdus; i++) + memcpy(&gNB->UL_INFO.rx_ind.pdu_list[i], &ind->pdu_list[i], sizeof(ind->pdu_list[0])); + + + pthread_mutex_unlock(&gNB->UL_INFO_mutex); + + return 1; +} + +int phy_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { + struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; + pthread_mutex_lock(&gNB->UL_INFO_mutex); + + gNB->UL_INFO.uci_ind = *ind; + + if (ind->num_ucis > 0) + gNB->UL_INFO.uci_ind.uci_list = malloc(sizeof(nfapi_nr_uci_t)*ind->num_ucis); + + for (int i=0; inum_ucis; i++) + memcpy(&gNB->UL_INFO.uci_ind.uci_list[i], &ind->uci_list[i], sizeof(ind->uci_list[0])); + //printf("UCI ind written to UL_info: num_ucis: %d, PDU_type : %d. \n", ind->num_ucis, ind->uci_list[0].pdu_type); + pthread_mutex_unlock(&gNB->UL_INFO_mutex); + + return 1; +} + +int phy_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { + struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; + pthread_mutex_lock(&gNB->UL_INFO_mutex); + + gNB->UL_INFO.srs_ind = *ind; + + if (ind->number_of_pdus > 0) + gNB->UL_INFO.srs_ind.pdu_list = malloc(sizeof(nfapi_nr_srs_indication_pdu_t)*ind->number_of_pdus); + + for (int i=0; inumber_of_pdus; i++) { + memcpy(&gNB->UL_INFO.srs_ind.pdu_list[i], &ind->pdu_list[i], sizeof(ind->pdu_list[0])); + + LOG_D(MAC, "%s() NFAPI SFN/Slot:%d.%d SRS_IND:number_of_pdus:%d UL_INFO:pdus:%d\n", + __FUNCTION__, + ind->sfn,ind->slot, ind->number_of_pdus, gNB->UL_INFO.srs_ind.number_of_pdus + ); + } + + pthread_mutex_unlock(&gNB->UL_INFO_mutex); + + return 1; +} + +int phy_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { + struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; + pthread_mutex_lock(&gNB->UL_INFO_mutex); + + gNB->UL_INFO.rach_ind = *ind; + + if (ind->number_of_pdus > 0) + gNB->UL_INFO.rach_ind.pdu_list = malloc(sizeof(nfapi_nr_prach_indication_pdu_t)*ind->number_of_pdus); + + for (int i=0; inumber_of_pdus; i++) { + memcpy(&gNB->UL_INFO.rach_ind.pdu_list[i], &ind->pdu_list[i], sizeof(ind->pdu_list[0])); + + LOG_D(MAC, "%s() NFAPI SFN/Slot:%d.%d RACH_IND:number_of_pdus:%d UL_INFO:pdus:%d\n", + __FUNCTION__, + ind->sfn,ind->slot, ind->number_of_pdus, gNB->UL_INFO.rach_ind.number_of_pdus + ); + } + + pthread_mutex_unlock(&gNB->UL_INFO_mutex); + + return 1; +} + +//end NR phy indication + int phy_lbt_dl_indication(struct nfapi_vnf_p7_config *config, nfapi_lbt_dl_indication_t *ind) { // vnf_p7_info* p7_vnf = (vnf_p7_info*)(config->user_data); //mac_lbt_dl_ind(p7_vnf->mac, ind); @@ -1167,6 +1297,12 @@ void *vnf_nr_p7_thread_start(void *ptr) { p7_vnf->config->lbt_dl_indication = &phy_lbt_dl_indication; p7_vnf->config->nb_harq_indication = &phy_nb_harq_indication; p7_vnf->config->nrach_indication = &phy_nrach_indication; + p7_vnf->config->nr_crc_indication = &phy_nr_crc_indication; + p7_vnf->config->nr_slot_indication = &phy_nr_slot_indication; + p7_vnf->config->nr_rx_data_indication = &phy_nr_rx_data_indication; + p7_vnf->config->nr_uci_indication = &phy_nr_uci_indication; + p7_vnf->config->nr_rach_indication = &phy_nr_rach_indication; + p7_vnf->config->nr_srs_indication = &phy_nr_srs_indication; p7_vnf->config->malloc = &vnf_allocate; p7_vnf->config->free = &vnf_deallocate; p7_vnf->config->trace = &vnf_trace; @@ -1501,8 +1637,8 @@ void configure_nr_nfapi_vnf(char *vnf_addr, int vnf_p5_port) { nfapi_setmode(NFAPI_MODE_VNF); memset(&vnf, 0, sizeof(vnf)); memset(vnf.p7_vnfs, 0, sizeof(vnf.p7_vnfs)); - vnf.p7_vnfs[0].timing_window = 32; - vnf.p7_vnfs[0].periodic_timing_enabled = 1; + vnf.p7_vnfs[0].timing_window = 30; + vnf.p7_vnfs[0].periodic_timing_enabled = 0; vnf.p7_vnfs[0].aperiodic_timing_enabled = 0; vnf.p7_vnfs[0].periodic_timing_period = 10; vnf.p7_vnfs[0].config = nfapi_vnf_p7_config_create(); @@ -1620,7 +1756,7 @@ int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) //LOG_I(PHY, "sfn:%d,slot:%d\n",dl_config_req->SFN,dl_config_req->Slot); //printf("\nEntering oai_nfapi_nr_dl_config_req sfn:%d,slot:%d\n",dl_config_req->SFN,dl_config_req->Slot); nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; - dl_config_req->header.message_id= NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST; + dl_config_req->header.message_id= NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST; dl_config_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! int retval = nfapi_vnf_p7_nr_dl_config_req(p7_config, dl_config_req); diff --git a/nfapi/oai_integration/nfapi_vnf.h b/nfapi/oai_integration/nfapi_vnf.h index f4fd6a2bf9..478956f75c 100644 --- a/nfapi/oai_integration/nfapi_vnf.h +++ b/nfapi/oai_integration/nfapi_vnf.h @@ -19,9 +19,9 @@ * contact@openairinterface.org */ -#if !defined(NFAPI_VNF_H__) -#define NFAPI_VNF_H__ void configure_nfapi_vnf(char *vnf_addr, int vnf_p5_port); void configure_nr_nfapi_vnf(char *vnf_addr, int vnf_p5_port); -#endif + +uint32_t sfnslot_add_slot(uint16_t sfn, uint16_t slot, int offset); + diff --git a/nfapi/open-nFAPI/common/src/debug.c b/nfapi/open-nFAPI/common/src/debug.c index f53be43223..a45d41d7d7 100644 --- a/nfapi/open-nFAPI/common/src/debug.c +++ b/nfapi/open-nFAPI/common/src/debug.c @@ -1,76 +1,76 @@ -/* - * Copyright 2017 Cisco Systems, Inc. - * - * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MAX_MSG_LENGTH 2096 -#define TRACE_HEADER_LENGTH 44 - -void nfapi_trace_dbg(nfapi_trace_level_t level, const char *format, ...); - -// initialize the trace function to 0 -void (*nfapi_trace_g)(nfapi_trace_level_t level, const char* format, ...) = &nfapi_trace_dbg; - -nfapi_trace_level_t nfapi_trace_level_g = NFAPI_TRACE_INFO; -//nfapi_trace_level_t nfapi_trace_level_g = NFAPI_TRACE_WARN; - -void nfapi_set_trace_level(nfapi_trace_level_t new_level) -{ - nfapi_trace_level_g = new_level; -} - -void nfapi_trace_dbg(nfapi_trace_level_t level, const char *format, ...) -{ - char trace_buff[MAX_MSG_LENGTH + TRACE_HEADER_LENGTH]; - uint32_t num_chars; - va_list p_args; - struct timeval tv; - pthread_t tid = pthread_self(); - - (void)gettimeofday(&tv, NULL); - - num_chars = (uint32_t)snprintf(trace_buff, TRACE_HEADER_LENGTH, "%04u.%06u: 0x%02x: %10u: ", ((uint32_t)tv.tv_sec) & 0x1FFF, (uint32_t)tv.tv_usec, (uint32_t)level, (uint32_t)tid); - - if (num_chars > TRACE_HEADER_LENGTH) - { - printf("trace_dbg: Error, num_chars is too large: %u", num_chars); - return; - } - - va_start(p_args, format); - if ((num_chars = (uint32_t)vsnprintf(&trace_buff[num_chars], MAX_MSG_LENGTH, format, p_args))) - { - if (level <= NFAPI_TRACE_WARN) - { - printf("%s", trace_buff); - } - printf("%s", trace_buff); - } - va_end(p_args); -} +/* + * Copyright 2017 Cisco Systems, Inc. + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_MSG_LENGTH 2096 +#define TRACE_HEADER_LENGTH 44 + +void nfapi_trace_dbg(nfapi_trace_level_t level, const char *format, ...); + +// initialize the trace function to 0 +void (*nfapi_trace_g)(nfapi_trace_level_t level, const char* format, ...) = &nfapi_trace_dbg; + +nfapi_trace_level_t nfapi_trace_level_g = NFAPI_TRACE_INFO; +//nfapi_trace_level_t nfapi_trace_level_g = NFAPI_TRACE_WARN; + +void nfapi_set_trace_level(nfapi_trace_level_t new_level) +{ + nfapi_trace_level_g = new_level; +} + +void nfapi_trace_dbg(nfapi_trace_level_t level, const char *format, ...) +{ + char trace_buff[MAX_MSG_LENGTH + TRACE_HEADER_LENGTH]; + uint32_t num_chars; + va_list p_args; + struct timeval tv; + pthread_t tid = pthread_self(); + + (void)gettimeofday(&tv, NULL); + + num_chars = (uint32_t)snprintf(trace_buff, TRACE_HEADER_LENGTH, "%04u.%06u: 0x%02x: %10u: ", ((uint32_t)tv.tv_sec) & 0x1FFF, (uint32_t)tv.tv_usec, (uint32_t)level, (uint32_t)tid); + + if (num_chars > TRACE_HEADER_LENGTH) + { + printf("trace_dbg: Error, num_chars is too large: %d", num_chars); + return; + } + + va_start(p_args, format); + if ((num_chars = (uint32_t)vsnprintf(&trace_buff[num_chars], MAX_MSG_LENGTH, format, p_args))) + { + if (level <= NFAPI_TRACE_WARN) + { + printf("%s", trace_buff); + } + printf("%s", trace_buff); + } + va_end(p_args); +} diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf deleted file mode 100644 index 6ee4ec840431454c5429456810abaa7a2f29fb59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3986416 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 z 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°XK?cs+^}VF8F+= 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�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
=(V8T~( z_&Z`$4tWypttPm64&N`NxFHF5p$Tqqm@_@2xPi7weR#(Nw=`D`w?N{%-UPQK$C%q|ezAwPy)qLFi)bTi^;~_EyHIsII{J-0pi}OxXAqQH% zkmDZDE_}0T`@8hIUET`U|2^^rzwhMA`nTEP(Cb#Xp%~)Ac-(4Siz{h8yS%`q`k?Xe z|G_$0acrJp9-G!fO&`+Uz;Pb@zN;RX#rCam2G0FH(?{~|K{>$wvsyKl++td&ijzVP zv`C}^aUl-=agZv31IA8f5oDY>~QFHflJ%X!{rEZg0+9Y9G_drbwicT z#y7c6{O`rsUz@Vb{jHC7747CrvE6*F5Eu3Zy|VxCV+_~B{?x)L+J`7_H;8+_5UyVa zm~IXsUs@+7>BFlF@4S~@x651MrnOD_zq98Z|Aml4?D5p>q;@E&zfGpiXWqNJ!-}tu z^)S#~aJtXDW&S94s6u;~U;kCNIF6_veIV(l_B+_faQn~;IIj`qX(}pETtBxvO<0A7 zcb>pziTcB%7(dJ9xFCzSW^dXpo-CANzsO@f3@}*$M!U}s*4^D92Q z?3suCsYOz3w@?iGE9_JFd;ULtgg$ugch`@pd}EwfGlu)&HGeU_`lC{DJPvSu01bP) z*DKYw-$?r(J}-5*UL=Y?>Qtko* zQ532m1m!4r5Rs#ZU^tW_BL8`vSu%OM%kED4pU(%<$?Sad&3E2=^JaFE95=$j?RvPL z>eMd^kPc~E?%u`yjOu#0t_^fw2ok=F5C66No(DyFell$?^*4yWeS<678S%sIdVGU5 z!{HbDIxLps|$KpEJLx;z{UxoV_)%9>4rNVyk#2@cC6nk(#AKg6f=jNej z4{UF4#1FSCd~sa&vvGrba>I=F2XMdSD`NX+w&B!|Az$y8+=0xSltJ)iYdT=QS)5-? z=o{M)^G&%d#@Ahu@_L2{x}q_u%bzeK^$Nl2w= zzF39;I@)Y|2jd#m^>oPO^nEA~^|kG%;|}ZY?K4J>#{Ark#q!?}NgN=Tg!l8)-Mq_7 z0-O#HN1+3@hq$J6{ab>-`u!odf3 zlaE#VMn=ebxH;Uu+{t#q$woN1T@TmK@$KE>5QKM2MGrTX#Rpip6_`4qrV<~dO4i~T zwufY94-&yWSa;+?Og1MZ7T4oDQokrj0PigP`!2>cs_W?>ouqj+tg~JiHAZzFMikfg zCB+3_ZgOtwY(-!Bpf!E*>jyZ$DDd;PA91@;T~CMj)8X~Jz1oJg3SVbeYkaXi#5SYx zK_bLnZlkt`_-kVJAkU@ol`L3ng}(fUENAIElUi8gi}iBO506`^i%U=8cB8tUz7Cc@ z!2V9*?Kv3=U$;el@#}{;dnlBL_F!juAKc%3timf_DSUkv^Brst0XA-B2(ILtS{c8e z%FCMoH&X3PH4u2B?N-A-J^L)a*upf{9_|qGTzV2wpO{V%nK#$v5 zJkRENpPkwhtcQj@+wV<9Nr31G+4-o_Z$sIKtE z*ClhGLupX^tKV?DbE??=p&Z5+)`hp0m284>jp}-QZ4GGLg8MBy%558laqRWX?aSeD zv%Nt50`_5KU%qsV>$xg6ZeC8`)z#it?f<$>^2K~4#ux4vX8iljQN<#i?0I}Wu7kc3 zev;HP%TPOmJoVWV#Oo}sT=d2*jp+dUh?Rair&@1j3F_dGDc?s4?$RB@NSy^g! zaX;G}IzC_zP5RkkTh&t0;~TS@uCqYj{^g%k=^N8jOkW>o4^8usuf*}4yLsOiScEh| zzJA6R>|sQmc8f8tQC&~pc#iM1Co6r4aZ+oG*+Uw;FAeu0=k%Fh8si$(_4v9tzHQGO zR_U8#QeT{x1ev~MgTPkjDUS*_`){$lB#GmjH2qTx z)*-q$>0;|xSP$)9vEVrcSB^6N2Dj4u0oFs0ynSoOJ=~9@4L6vP70#tjE_O(>w~!8?D&!!g1wFU$o=KeGmP_ zh8+%KzA4Sc?AKO`<~yM8I}6vTaI*w)CHDCMI1et3J3!o-D&~yRFC@kn;;+=S!3$Ui zm*Dp%NhBh`x*PU)-dR1EWNX%no;~=;&Me@X&bDeDypf^LA#lb5GT*2U1*AN z9e;_%NjInOPOtj=&te6?=P8}(0CDGVw_TrNzD9LDePxdC#?a9k7$-@PZ{i;`u0#Gi za>=~&7}uz-$2a*KiVN}K#XZC3DI+X(i!T29I6sz~Ew6?7`5TzaAMkwaXLbhr1n0KH z=Q<)wQ1INgpV`C11g`tYLNg*!jca=P`Z*n%bUa!OBgF`QKbNaLjq5PaZ{5F3_42YH zu3M(@0sP{n^|$+CzD9Kw5!S&YhvQCxIWv7mIJjL8HJQ(;=Pd0QRupNU>%ZB%zhq zd@L~?VBr>gAeGnyW#DAbh$1sk)wb;1HGUW^TWd0%x$Xz~bMUSss zH@yGbwbpqRU$-D%uT1$u{2lRg;ea)^idCw6*$0OA5rabtcUP`-M>)UQo(b#l)ZO=0 zI%JvD0o$+4>=)*@b?LRL;&+spBWAx6$2a5f<7CwpS&{|6XHR1LJFq`F)7vva(bv{0 zy8OY(@m*i@4HaM4b7Fqslf!n_vv{3lkt!J1^!&oj@f~)jq>8Ukkgu&mcs=y}!+-6= z3`2sr=}ZTh=kKoBaUJ6t)%A4nv-kiL@rMcJB#e{P$Xwos`}w(fC_Vp$UW%PbQQDb{ zEQVk`l%M@2ey?zVty>^JOSxL@TOQNQ-+Fdtt4I9;_|9$Sa$_v7pq*tiz7Wt? z*8eci)&WkA?|T3CwwP~*NqfNah3%zqo;tN|$1gCh=eF2<;bZmyaea5U7dvCzfGl=j zT4H=5pUn4OiQbAIJK6L2b#PtJ9{imB-X79f?3qe|Gsp#>w zk<1_B4&dI(L)>h3{tWilQ~!BC&-nVe`-Knh+t}Wc2;^ct9g+&s&x=8P7`Jih0gRhG zMeH23lpUVuWBXPgigAtVdVHO$!}(-fpYVRQd!n^`2m7(_e0U$`v+J#LFu$xV<~~=4 zais^S4h0Bc!+y8#Vq8y0vG|a-AZ)*jUYL}QagFMVzL>9*@hwOI9*;lpAI9-572_LR zN_Bv93b*HZ>tbBj5wZC=^kx`0-+VQ0V1gkoZ1Ji}d=rWb`vi}U`TYvMAoVk`c$VIu zelDpV0W3ajaX|6RU`;xo;@81-W!&)du`Hf}9~b>|3Hf|sK8gHkbDGc3GOLYKy z7Ovd>;xG6`@k@F4ISKy+8XrK11&zl7ZHfFdT@-z>U&w5JghX;~ zp(`pK0)qSN4sP7KcP~6du?Jt2^RaDN*e_OZU!wB2Ot09u1!Rf~>z2f2Li>}Nb{1lG z2L86~qnj#!ljn%>bvK}K8|LHSJ5yCU#LXAPW#a}mTypN#*T{pVLhMXa2Rj=#XzUq# z%$ z)ruL4U)Wrx=Xp)O_K}n?+((ytZdctW%iJLr*F!SJg%>^gKpoXSIC=WMMKA71?EOL_ zxw-GNFJKVY9kFqfI2|Ty=n(U{IUTT_xjDW~N2>V-ZyEB%Z!xzXTPvKW?mpO)oQ8-j z{^eqRVXI8-3D#L%ZKYK<apA*THeL*~}ws?c!*9TL529b8My<<%J6&CU~mqcy3% zxQD{mXFfh)$lSVv`33CTZ}L=6#1|!!r*Z6u|hpQ$0B&oX^QNA!ACp0Mjr>&pHMfZ71kFUh$H^?7GzH=6q z-x0;*GiMLjFT9*zbkpz)yIssLY%OSB9#2@tB#o(|=%`Y%tKg%DUB)Fd@e+=#s zQ8NA$i<4Ol7XTL=Ja*$A-zfbe#BgE222Ow76+zZSMb93b%->*LIQE5us{AY%67x65 zxUj$7s+wOiK{4VIg)bgAA7^LXHS8>Tqv5!z<`{V4L~gw~vu?#dBgl}7yxN>!;C?|i z?yzo&zp#|BXYZ_|HTzZJvVA$oe;?hIFO}d*xIE_NdlxNk_%zc_*_oc+ugH^~Kkqz8+5BHX8ccPne4jSYMBm_LIoF zKfJh7?FbP_#rv9|4wb|Hma3scym7vxX=gFzC@$>F9j>n4my6$F&M)x$4Acqx+i#(( z|KN6`I*B`)#od+e_d)Er@J7?)_@bcKP+v_QlDYZffB$Bx;#qQ*m_1~3_V9b%sr4}5 z#0)Wgv*w3Chux>j3uL;diUrNbNlYcT?jA?S?O{@@*X+i-nit*YNxgB4BL)UzM%>v} zEa$P8?gOFOq=)O}?BTM8J!C95H*WYvy$2~@n2#$>@U2$#wf!7j9+k@S9XN+K;lsZ_ z#;h z@QF2l!#c>!9$+8bbMf2XFj@B@v3$qJaACh%ZWQ=Zv0vLYYkV;u2aA)izrOCk#~pFI zQC&~pAmdw#0K7VVUF&;%J6q$6^_5tEu(JV=by$P>r6r2R-%Mr?un_wBzqW@IzEa`n z?wdLpU(n&R!oNPJ;JPj9fZwN^t+Qa?{?W1BAgoy`y5m-z`Wx6oU~=^q3SVcG^H3T$ zZ?=iAbOMtNo)(J_agWe=2IKbV;5W%EO5d3?%5n3sb082WGt&$GiuoGV_4JKr{s!yV zqRscJ@;*`A9G3qAZuXR`!noMqWM-#eXE*Pr!!}PuiQgkOZr*X!&LHnA*{IBa5uhe2 zps%EHU3wP9h5K(`uBzm*kr~cPQN|7H;OF8_W|hUCsWAOwI{21^*B8s5+OS2jUn$L6 zoWy*b|Iq#rcgPRfe5m017e$8~w}7q#DiYWVh2Pz&;7W_5!%ZRwGL?Ki@o}=)V1De5 zGVTPq_FYQ>6aT7jLg3 z_8^fzZBC1Rf%TP`zNHD)^xxk<448aL5Y*Q}GBVKiA@Xna>2aNSOf!G$>FXOuabaCp zqUX>#X0Z5c^l)Wv-2C0YRgGKnALjBXtgo#l?MIH=*w(f_y^rStf2Z~o!;POn?Y<1b znElDGs&UInGlz@$cv#$l`;a5r7x`U@JH9C6j)(ah+$VeE=qv5;>l)Sd#x1K4y&vl! z0G`e@iCNGqgB7jisd(Jn%r9WRxYc$;I_BpW^b47bXDb)NuXk80dODU#E& zz;Gcxym{-fe~oZ(yB=FnxWM_WQ2tEAIb{!WYN0Am&AE>1_3tUR+L|9@9WvNF1pB|0-+s3x=9|?^Y~Bp9af5tk zQlGImFs@NuZ`^#x==+5@d9v84$rvYOJZ`v4Ze7l%;|7EJO2_|H`wyC(xF;39+*7%K z#&o0e1qqP0YqlG$aC_h>vHR%RZ2tlB-xin4ufw=Tbv?e>%pQPmO8ObH+heULxY*8o zEbl8r;5Jr0HyESF4luMcO@9lupyLMf(7@eORO`2d=3?i6{neXV1Nd=;&Pz#NLa+2$^ka-``X9GFzJ|4; z@Wp;%t4{3<^0NYewI*>LmBWU4pQc~f$VkC@6Xv0!{zGjLsx7CKp}*lSx%HSnln-FH z+t`&nuy?k`+B}589ZX-ChdMv+oPgVn>U#Q0$HVi*wRzV`)*4wnCk*HL$PTRLP$z(?{P3>wrny$5VTN{4D!QqjI?2sIKUXuk*4z74}o#+WgKS zMF+{^_se0wa5H^jp6?;OpMYQ2oh3GIsU(zx?LJBVP0CnN3FC%Ji}{70v)|a48ZXDV zMs-DBtb>!;FYNc#{CM;VjFT#8ztTcFFG>Wk^vYM?P{z&8UJt(xuIusjK276q9O>}W z$mXYUzidIi>BFi0!n$yFmzu;Ln7>(!J6K
~j43GSco4W5em#vL_`zqm_oUE=(0 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

^SLPfJk$!O%IyUB$-;YTl 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ⅆ++Qx_8ni+9a;VDR+s>2^V|u11{hO&-w2Eign~x|H63?N7Tt%Wqsn&x2nWSies|Y ztS{6zRDHGOST|{X$pW8J=gHtAonXDA^}nq9*_9#pb;MVvyeEv zj^KHRw_3z0j-HKWo2O;&ixHd8?e_FxT&aO)yQ37x`ZD`h^dxgUG~#QufiLix4NsnR zreu!$ws9WlUOUH(OXq8ufiLh`D6T|~zKZi?ef4dwBP9>!IPREIE!GfUs57A*j@FsT zS5cRXR4}fy=AZ4pp7@G;*(cBrjQfRNr&H*>HsR-dqH#vGxQqDeJbGfLe}XT&a3K!R z3C5u$^wpR7TDa+K_YK5XV1${kV&{)meBrpj1>GY~od+?lO7i=!5Uy^Ct1S7dL0=vH z7+2$hYOz+2qZ}XYj-$#~txjAK>k84Z*1K_4n#h-2&!=4;OGx$dtLU+H!&lYfCP@a@!nSAeT} zFyr!(-)|zmBI_Afsr=rmKYC9w*TtYO;A)k)=2gyfe~tKR9?5y3-F)?WpHqXrq9=16 zef^$laWC=Ja-1{zf?r$dD>#JXc>er(?pt(yjdF%BHP1mDJLI?>AHcZo**MRAE8%J# zZN`PXve5m$R{FB?d+5t^DC4?zf3;|$IEIgRCXT9qAtwzuxac1wO%ZzUvAdInFz99_R~v`A2XZ�X5zMXJs@_uRf`(pXBs+(B( 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 uint16_t slot;//0->319 @@ -1492,10 +1493,11 @@ typedef struct typedef struct { + nfapi_p7_message_header_t header; uint16_t sfn; uint16_t slot; uint16_t number_of_pdus; - nfapi_nr_rx_data_pdu_t* pdu_list; + nfapi_nr_rx_data_pdu_t *pdu_list; } nfapi_nr_rx_data_indication_t; @@ -1518,6 +1520,7 @@ typedef struct typedef struct { + nfapi_p7_message_header_t header; uint16_t sfn; uint16_t slot; uint16_t number_crcs; @@ -1653,6 +1656,7 @@ typedef struct typedef struct { + nfapi_p7_message_header_t header; uint16_t sfn; uint16_t slot; uint16_t num_ucis; @@ -1689,6 +1693,7 @@ typedef struct typedef struct { + nfapi_p7_message_header_t header; uint16_t sfn; uint16_t slot; uint8_t number_of_pdus; @@ -1721,6 +1726,7 @@ typedef struct{ typedef struct { + nfapi_p7_message_header_t header; uint16_t sfn; uint16_t slot; uint8_t number_of_pdus; diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c index 0c0a8750bc..23d19482ea 100644 --- a/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c +++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c @@ -1,2368 +1,2389 @@ -/* - * Copyright 2017 Cisco Systems, Inc. - * - * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "nfapi_nr_interface.h" -#include "nfapi_nr_interface_scf.h" -#include - - -// Pack routines -//TODO: Add pacl/unpack fns for uint32 and uint64 -static uint8_t pack_nr_pnf_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_param_request_t *request = (nfapi_nr_pnf_param_request_t *)msg; - return pack_vendor_extension_tlv(request->vendor_extension, ppWritePackedMsg, end, config); -} - -static uint8_t pack_pnf_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_param_request_t *request = (nfapi_pnf_param_request_t *)msg; - return pack_vendor_extension_tlv(request->vendor_extension, ppWritePackedMsg, end, config); -} - -static uint8_t pack_pnf_param_general_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_param_general_t *value = (nfapi_pnf_param_general_t *)tlv; - return ( push8(value->nfapi_sync_mode, ppWritePackedMsg, end) && - push8(value->location_mode, ppWritePackedMsg, end) && - push16(value->location_coordinates_length, ppWritePackedMsg, end) && - pusharray8(value->location_coordinates, NFAPI_PNF_PARAM_GENERAL_LOCATION_LENGTH, value->location_coordinates_length, ppWritePackedMsg, end) && - push32(value->dl_config_timing, ppWritePackedMsg, end) && - push32(value->tx_timing, ppWritePackedMsg, end) && - push32(value->ul_config_timing, ppWritePackedMsg, end) && - push32(value->hi_dci0_timing, ppWritePackedMsg, end) && - push16(value->maximum_number_phys, ppWritePackedMsg, end) && - push16(value->maximum_total_bandwidth, ppWritePackedMsg, end) && - push8(value->maximum_total_number_dl_layers, ppWritePackedMsg, end) && - push8(value->maximum_total_number_ul_layers, ppWritePackedMsg, end) && - push8(value->shared_bands, ppWritePackedMsg, end) && - push8(value->shared_pa, ppWritePackedMsg, end) && - pushs16(value->maximum_total_power, ppWritePackedMsg, end) && - pusharray8(value->oui, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, ppWritePackedMsg, end)); -} - -static uint8_t pack_rf_config_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_rf_config_info_t *rf = (nfapi_rf_config_info_t *)elem; - return (push16(rf->rf_config_index, ppWritePackedMsg, end)); -} - - -static uint8_t pack_pnf_phy_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_info_t *phy = (nfapi_pnf_phy_info_t *)elem; - return ( push16(phy->phy_config_index, ppWritePackedMsg, end) && - push16(phy->number_of_rfs, ppWritePackedMsg, end) && - packarray(phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, ppWritePackedMsg, end, &pack_rf_config_info) && - push16(phy->number_of_rf_exclusions, ppWritePackedMsg, end) && - packarray(phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, ppWritePackedMsg, end, &pack_rf_config_info) && - push16(phy->downlink_channel_bandwidth_supported, ppWritePackedMsg, end) && - push16(phy->uplink_channel_bandwidth_supported, ppWritePackedMsg, end) && - push8(phy->number_of_dl_layers_supported, ppWritePackedMsg, end) && - push8(phy->number_of_ul_layers_supported, ppWritePackedMsg, end) && - push16(phy->maximum_3gpp_release_supported, ppWritePackedMsg, end) && - push8(phy->nmm_modes_supported, ppWritePackedMsg, end)); -} - -static uint8_t pack_pnf_phy_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_t *value = (nfapi_pnf_phy_t *)tlv; - return ( push16(value->number_of_phys, ppWritePackedMsg, end) && - packarray(value->phy, sizeof(nfapi_pnf_phy_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_info)); -} - -static uint8_t pack_pnf_rf_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_rf_t *value = (nfapi_pnf_rf_t *)tlv; - uint16_t rf_index = 0; - - if(push16(value->number_of_rfs, ppWritePackedMsg, end) == 0) - return 0; - - for(; rf_index < value->number_of_rfs; ++rf_index) { - if( !(push16(value->rf[rf_index].rf_config_index, ppWritePackedMsg, end) && - push16(value->rf[rf_index].band, ppWritePackedMsg, end) && - pushs16(value->rf[rf_index].maximum_transmit_power, ppWritePackedMsg, end) && - pushs16(value->rf[rf_index].minimum_transmit_power, ppWritePackedMsg, end) && - push8(value->rf[rf_index].number_of_antennas_suppported, ppWritePackedMsg, end) && - push32(value->rf[rf_index].minimum_downlink_frequency, ppWritePackedMsg, end) && - push32(value->rf[rf_index].maximum_downlink_frequency, ppWritePackedMsg, end) && - push32(value->rf[rf_index].minimum_uplink_frequency, ppWritePackedMsg, end) && - push32(value->rf[rf_index].maximum_uplink_frequency, ppWritePackedMsg, end))) - return 0; - } - - return 1; -} -static uint8_t pack_pnf_phy_rel10_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel10_info_t *phy = (nfapi_pnf_phy_rel10_info_t *)elem; - return(push16(phy->phy_config_index, ppWritePackedMsg, end) && - push16(phy->transmission_mode_7_supported, ppWritePackedMsg, end) && - push16(phy->transmission_mode_8_supported, ppWritePackedMsg, end) && - push16(phy->two_antenna_ports_for_pucch, ppWritePackedMsg, end) && - push16(phy->transmission_mode_9_supported, ppWritePackedMsg, end) && - push16(phy->simultaneous_pucch_pusch, ppWritePackedMsg, end) && - push16(phy->four_layer_tx_with_tm3_and_tm4, ppWritePackedMsg, end)); -} - -static uint8_t pack_pnf_phy_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel10_t *value = (nfapi_pnf_phy_rel10_t *)tlv; - return (push16(value->number_of_phys, ppWritePackedMsg, end) && - packarray(value->phy, sizeof(nfapi_pnf_phy_rel10_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel10_info)); -} - -static uint8_t pack_pnf_phy_rel11_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel11_info_t *phy = (nfapi_pnf_phy_rel11_info_t *)elem; - return (push16(phy->phy_config_index, ppWritePackedMsg, end) && - push16(phy->edpcch_supported, ppWritePackedMsg, end) && - push16(phy->multi_ack_csi_reporting, ppWritePackedMsg, end) && - push16(phy->pucch_tx_diversity, ppWritePackedMsg, end) && - push16(phy->ul_comp_supported, ppWritePackedMsg, end) && - push16(phy->transmission_mode_5_supported, ppWritePackedMsg, end )); -} -static uint8_t pack_pnf_phy_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel11_t *value = (nfapi_pnf_phy_rel11_t *)tlv; - return (push16(value->number_of_phys, ppWritePackedMsg, end) && - packarray(value->phy, sizeof(nfapi_pnf_phy_rel11_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel11_info)); -} -static uint8_t pack_pnf_phy_rel12_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel12_info_t *phy = (nfapi_pnf_phy_rel12_info_t *)elem; - return( push16(phy->phy_config_index, ppWritePackedMsg, end) && - push16(phy->csi_subframe_set, ppWritePackedMsg, end) && - push16(phy->enhanced_4tx_codebook, ppWritePackedMsg, end) && - push16(phy->drs_supported, ppWritePackedMsg, end) && - push16(phy->ul_64qam_supported, ppWritePackedMsg, end) && - push16(phy->transmission_mode_10_supported, ppWritePackedMsg, end) && - push16(phy->alternative_bts_indices, ppWritePackedMsg, end)); -} -static uint8_t pack_pnf_phy_rel12_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel12_t *value = (nfapi_pnf_phy_rel12_t *)tlv; - return (push16(value->number_of_phys, ppWritePackedMsg, end) && - packarray(value->phy, sizeof(nfapi_pnf_phy_rel12_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel12_info)); -} - -static uint8_t pack_pnf_phy_rel13_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_info_t *phy = (nfapi_pnf_phy_rel13_info_t *)elem; - return( push16(phy->phy_config_index, ppWritePackedMsg, end) && - push16(phy->pucch_format4_supported, ppWritePackedMsg, end) && - push16(phy->pucch_format5_supported, ppWritePackedMsg, end) && - push16(phy->more_than_5_ca_support, ppWritePackedMsg, end) && - push16(phy->laa_supported, ppWritePackedMsg, end) && - push16(phy->laa_ending_in_dwpts_supported, ppWritePackedMsg, end) && - push16(phy->laa_starting_in_second_slot_supported, ppWritePackedMsg, end) && - push16(phy->beamforming_supported, ppWritePackedMsg, end) && - push16(phy->csi_rs_enhancement_supported, ppWritePackedMsg, end) && - push16(phy->drms_enhancement_supported, ppWritePackedMsg, end) && - push16(phy->srs_enhancement_supported, ppWritePackedMsg, end) ); -} - -static uint8_t pack_pnf_phy_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_t *value = (nfapi_pnf_phy_rel13_t *)tlv; - return (push16(value->number_of_phys, ppWritePackedMsg, end) && - packarray(value->phy, sizeof(nfapi_pnf_phy_rel13_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel13_info)); -} - -static uint8_t pack_pnf_phy_rel13_nb_iot_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_nb_iot_info_t *phy = (nfapi_pnf_phy_rel13_nb_iot_info_t *)elem; - return( push16(phy->phy_config_index, ppWritePackedMsg, end) && - push16(phy->number_of_rfs, ppWritePackedMsg, end) && - packarray(phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, ppWritePackedMsg, end, &pack_rf_config_info) && - push16(phy->number_of_rf_exclusions, ppWritePackedMsg, end) && - packarray(phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, ppWritePackedMsg, end, &pack_rf_config_info) && - push8(phy->number_of_dl_layers_supported, ppWritePackedMsg, end) && - push8(phy->number_of_ul_layers_supported, ppWritePackedMsg, end) && - push16(phy->maximum_3gpp_release_supported, ppWritePackedMsg, end) && - push8(phy->nmm_modes_supported, ppWritePackedMsg, end)); -} - -static uint8_t pack_pnf_phy_rel13_nb_iot_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_nb_iot_t *value = (nfapi_pnf_phy_rel13_nb_iot_t *)tlv; - return (push16(value->number_of_phys, ppWritePackedMsg, end) && - packarray(value->phy, sizeof(nfapi_pnf_phy_rel13_nb_iot_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel13_nb_iot_info)); -} -/* -static uint8_t pack_nr_pnf_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t* config) -{ - nfapi_nr_pnf_param_response_t *pNfapiMsg = (nfapi_nr_pnf_param_response_t*)msg; - - return (push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_tlv(NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, ppWritePackedMsg, end, &pack_pnf_param_general_value) && - pack_tlv(NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, ppWritePackedMsg, end, &pack_pnf_phy_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} -*/ -static uint8_t pack_nr_pnf_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_param_response_t *pNfapiMsg = (nfapi_nr_pnf_param_response_t *)msg; - return (push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_tlv(NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, ppWritePackedMsg, end, &pack_pnf_param_general_value) && - pack_tlv(NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, ppWritePackedMsg, end, &pack_pnf_phy_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_pnf_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_param_response_t *pNfapiMsg = (nfapi_pnf_param_response_t *)msg; - return (push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_tlv(NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, ppWritePackedMsg, end, &pack_pnf_param_general_value) && - pack_tlv(NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, ppWritePackedMsg, end, &pack_pnf_phy_value) && - pack_tlv(NFAPI_PNF_RF_TAG, &pNfapiMsg->pnf_rf, ppWritePackedMsg, end, &pack_pnf_rf_value) && - pack_tlv(NFAPI_PNF_PHY_REL10_TAG, &pNfapiMsg->pnf_phy_rel10, ppWritePackedMsg, end, &pack_pnf_phy_rel10_value) && - pack_tlv(NFAPI_PNF_PHY_REL11_TAG, &pNfapiMsg->pnf_phy_rel11, ppWritePackedMsg, end, &pack_pnf_phy_rel11_value) && - pack_tlv(NFAPI_PNF_PHY_REL12_TAG, &pNfapiMsg->pnf_phy_rel12, ppWritePackedMsg, end, &pack_pnf_phy_rel12_value) && - pack_tlv(NFAPI_PNF_PHY_REL13_TAG, &pNfapiMsg->pnf_phy_rel13, ppWritePackedMsg, end, &pack_pnf_phy_rel13_value) && - pack_tlv(NFAPI_PNF_PHY_REL13_NB_IOT_TAG, &pNfapiMsg->pnf_phy_rel13_nb_iot, ppWritePackedMsg, end, &pack_pnf_phy_rel13_nb_iot_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_phy_rf_config_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_phy_rf_config_info_t *rf = (nfapi_phy_rf_config_info_t *)elem; - return (push16(rf->phy_id, ppWritePackedMsg, end) && - push16(rf->phy_config_index, ppWritePackedMsg, end) && - push16(rf->rf_config_index, ppWritePackedMsg, end)); -} - - -static uint8_t pack_pnf_phy_rf_config_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_pnf_phy_rf_config_t *value = (nfapi_pnf_phy_rf_config_t *)tlv; - return(push16(value->number_phy_rf_config_info, ppWritePackedMsg, end) && - packarray(value->phy_rf_config, sizeof(nfapi_phy_rf_config_info_t), NFAPI_MAX_PHY_RF_INSTANCES, value->number_phy_rf_config_info, ppWritePackedMsg, end, &pack_phy_rf_config_info)); -} - -static uint8_t pack_nr_pnf_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_config_request_t *pNfapiMsg = (nfapi_nr_pnf_config_request_t *)msg; - return (pack_tlv(NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, ppWritePackedMsg, end, &pack_pnf_phy_rf_config_value) && - //push8(pNfapiMsg->num_tlvs,ppWritePackedMsg,end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_pnf_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_config_request_t *pNfapiMsg = (nfapi_pnf_config_request_t *)msg; - return (pack_tlv(NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, ppWritePackedMsg, end, &pack_pnf_phy_rf_config_value) && - push8(pNfapiMsg->num_tlvs,ppWritePackedMsg,end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_nr_pnf_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_config_response_t *pNfapiMsg = (nfapi_nr_pnf_config_response_t *)msg; - return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_pnf_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_config_response_t *pNfapiMsg = (nfapi_pnf_config_response_t *)msg; - return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nr_pnf_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_start_request_t *pNfapiMsg = (nfapi_nr_pnf_start_request_t *)msg; - return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_pnf_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_start_request_t *pNfapiMsg = (nfapi_pnf_start_request_t *)msg; - return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_nr_pnf_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_start_response_t *pNfapiMsg = (nfapi_nr_pnf_start_response_t *)msg; - return( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_pnf_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_start_response_t *pNfapiMsg = (nfapi_pnf_start_response_t *)msg; - return( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_nr_pnf_stop_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_stop_request_t *pNfapiMsg = (nfapi_nr_pnf_stop_request_t *)msg; - return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - - - -static uint8_t pack_pnf_stop_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_stop_request_t *pNfapiMsg = (nfapi_pnf_stop_request_t *)msg; - return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - - -static uint8_t pack_nr_pnf_stop_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_stop_response_t *pNfapiMsg = (nfapi_nr_pnf_stop_response_t *)msg; - return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - -static uint8_t pack_pnf_stop_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_stop_response_t *pNfapiMsg = (nfapi_pnf_stop_response_t *)msg; - return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nr_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_param_request_scf_t *pNfapiMsg = (nfapi_nr_param_request_scf_t *)msg; - return (pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_param_request_t *pNfapiMsg = (nfapi_param_request_t *)msg; - return (pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_uint32_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_uint32_tlv_t *value = (nfapi_uint32_tlv_t *)tlv; - return push32(value->value, ppWritePackedMsg, end); -} - -static uint8_t unpack_uint32_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_uint32_tlv_t *value = (nfapi_uint32_tlv_t *)tlv; - return pull32(ppReadPackedMsg, &value->value, end); -} - - -static uint8_t pack_uint16_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_uint16_tlv_t *value = (nfapi_uint16_tlv_t *)tlv; - return push16(value->value, ppWritePackedMsg, end); -} - -static uint8_t unpack_uint16_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_uint16_tlv_t *value = (nfapi_uint16_tlv_t *)tlv; - return pull16(ppReadPackedMsg, &value->value, end); -} - -static uint8_t pack_int16_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_int16_tlv_t *value = (nfapi_int16_tlv_t *)tlv; - return pushs16(value->value, ppWritePackedMsg, end); -} - -static uint8_t unpack_int16_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_int16_tlv_t *value = (nfapi_int16_tlv_t *)tlv; - return pulls16(ppReadPackedMsg, &value->value, end); -} - -static uint8_t pack_uint8_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_uint8_tlv_t *value = (nfapi_uint8_tlv_t *)tlv; - return push8(value->value, ppWritePackedMsg, end); -} -static uint8_t unpack_uint8_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_uint8_tlv_t *value = (nfapi_uint8_tlv_t *)tlv; - return pull8(ppReadPackedMsg, &value->value, end); -} - -static uint8_t pack_ipv4_address_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ipv4_address_t *value = (nfapi_ipv4_address_t *)tlv; - return pusharray8(value->address, NFAPI_IPV4_ADDRESS_LENGTH, NFAPI_IPV4_ADDRESS_LENGTH, ppWritePackedMsg, end); -} -static uint8_t unpack_ipv4_address_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ipv4_address_t *value = (nfapi_ipv4_address_t *)tlv; - return pullarray8(ppReadPackedMsg, value->address, NFAPI_IPV4_ADDRESS_LENGTH, NFAPI_IPV4_ADDRESS_LENGTH, end); -} -static uint8_t pack_ipv6_address_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ipv6_address_t *value = (nfapi_ipv6_address_t *)tlv; - return pusharray8(value->address, NFAPI_IPV6_ADDRESS_LENGTH, NFAPI_IPV6_ADDRESS_LENGTH, ppWritePackedMsg, end); -} -static uint8_t unpack_ipv6_address_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ipv4_address_t *value = (nfapi_ipv4_address_t *)tlv; - return pullarray8(ppReadPackedMsg, value->address, NFAPI_IPV6_ADDRESS_LENGTH, NFAPI_IPV6_ADDRESS_LENGTH, end); -} - -static uint8_t pack_rf_bands_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_rf_bands_t *value = (nfapi_rf_bands_t *)tlv; - return ( push16(value->number_rf_bands, ppWritePackedMsg, end) && - pusharray16(value->rf_band, NFAPI_MAX_NUM_RF_BANDS, value->number_rf_bands, ppWritePackedMsg, end)); -} -static uint8_t unpack_rf_bands_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_rf_bands_t *value = (nfapi_rf_bands_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_rf_bands, end) && - pullarray16(ppReadPackedMsg, value->rf_band, NFAPI_MAX_NUM_RF_BANDS, value->number_rf_bands, end)); -} - -static uint8_t pack_nmm_frequency_bands_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nmm_frequency_bands_t *value = (nfapi_nmm_frequency_bands_t *)tlv; - return( push16(value->number_of_rf_bands, ppWritePackedMsg, end) && - pusharray16(value->bands, NFAPI_MAX_NMM_FREQUENCY_BANDS, value->number_of_rf_bands, ppWritePackedMsg, end)); -} -static uint8_t unpack_nmm_frequency_bands_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nmm_frequency_bands_t *value = (nfapi_nmm_frequency_bands_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_rf_bands, end) && - pullarray16(ppReadPackedMsg, value->bands, NFAPI_MAX_NMM_FREQUENCY_BANDS, value->number_of_rf_bands, end)); -} -static uint8_t pack_embms_mbsfn_config_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_embms_mbsfn_config_t *value = (nfapi_embms_mbsfn_config_t *)tlv; - return ( push16(value->num_mbsfn_config, ppWritePackedMsg, end) && - pusharray16(value->radioframe_allocation_period, 8,value->num_mbsfn_config,ppWritePackedMsg, end) && - pusharray16(value->radioframe_allocation_offset, 8,value->num_mbsfn_config,ppWritePackedMsg, end) && - pusharray8(value->fourframes_flag, 8,value->num_mbsfn_config,ppWritePackedMsg, end) && - pusharrays32(value->mbsfn_subframeconfig, 8, value->num_mbsfn_config, ppWritePackedMsg, end)); -} -// static uint8_t unpack_embms_mbsfn_config_value(void* tlv, uint8_t **ppReadPackedMsg, uint8_t* end) -// { -// nfapi_embms_mbsfn_config_t* value = (nfapi_embms_mbsfn_config_t*)tlv; - -// return ( pull16(ppReadPackedMsg, &value->num_mbsfn_config, end) && -// pull16(ppReadPackedMsg, &value->radioframe_allocation_period, end) && -// pull16(ppReadPackedMsg, &value->radioframe_allocation_offset, end) && -// pull8(ppReadPackedMsg, &value->fourframes_flag, end) && -// pullarrays32(ppReadPackedMsg, value->mbsfn_subframeconfig, 8, value->num_mbsfn_config, end)); -// } - -static uint8_t pack_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_param_response_t *pNfapiMsg = (nfapi_param_response_t *)msg; - return( push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && - push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && - pack_tlv(NFAPI_L1_STATUS_PHY_STATE_TAG, &pNfapiMsg->l1_status.phy_state, ppWritePackedMsg, end, &pack_uint16_tlv_value) && - // Do we check the phy state and then just fill those sepecified, however - // we do not know the duplex mode, so just attempt to pack all and assumme - // that the callee has set the right tlvs - pack_tlv(NFAPI_PHY_CAPABILITIES_DL_BANDWIDTH_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.dl_bandwidth_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHY_CAPABILITIES_UL_BANDWIDTH_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.ul_bandwidth_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHY_CAPABILITIES_DL_MODULATION_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.dl_modulation_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHY_CAPABILITIES_UL_MODULATION_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.ul_modulation_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHY_CAPABILITIES_PHY_ANTENNA_CAPABILITY_TAG, &(pNfapiMsg->phy_capabilities.phy_antenna_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHY_CAPABILITIES_RELEASE_CAPABILITY_TAG, &(pNfapiMsg->phy_capabilities.release_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHY_CAPABILITIES_MBSFN_CAPABILITY_TAG, &(pNfapiMsg->phy_capabilities.mbsfn_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - // laa capability - pack_tlv(NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &(pNfapiMsg->subframe_config.duplex_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &(pNfapiMsg->subframe_config.pcfich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value)&& - pack_tlv(NFAPI_SUBFRAME_CONFIG_PB_TAG, &(pNfapiMsg->subframe_config.pb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.dl_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.ul_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.dl_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.ul_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &(pNfapiMsg->rf_config.reference_signal_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.tx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.rx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &(pNfapiMsg->phich_config.phich_resource), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &(pNfapiMsg->phich_config.phich_duration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &(pNfapiMsg->phich_config.phich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &(pNfapiMsg->sch_config.physical_cell_id), ppWritePackedMsg, end,&pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->prach_config.configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &(pNfapiMsg->prach_config.zero_correlation_zone_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &(pNfapiMsg->prach_config.high_speed_flag), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->prach_config.frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &(pNfapiMsg->pusch_config.hopping_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &(pNfapiMsg->pusch_config.hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &(pNfapiMsg->pusch_config.number_of_subbands), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &(pNfapiMsg->pucch_config.delta_pucch_shift), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &(pNfapiMsg->pucch_config.n_cqi_rb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &(pNfapiMsg->pucch_config.n_an_cs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &(pNfapiMsg->pucch_config.n1_pucch_an), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMBMS_MBSFN_CONFIG_AREA_IDX_TAG, &(pNfapiMsg->embms_sib13_config.mbsfn_area_idx), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_EMBMS_MBSFN_CONFIG_AREA_IDR9_TAG, &(pNfapiMsg->embms_sib13_config.mbsfn_area_id_r9), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMBMS_MBSFN_CONFIG_TAG, &(pNfapiMsg->embms_mbsfn_config), ppWritePackedMsg, end, &pack_embms_mbsfn_config_value) && - pack_tlv(NFAPI_FEMBMS_CONFIG_RADIOFRAME_ALLOCATION_PERIOD_TAG, &(pNfapiMsg->fembms_config.radioframe_allocation_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_FEMBMS_CONFIG_RADIOFRAME_ALLOCATION_OFFSET_TAG, &(pNfapiMsg->fembms_config.radioframe_allocation_offset), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_FEMBMS_CONFIG_NON_MBSFN_FLAG_TAG, &(pNfapiMsg->fembms_config.non_mbsfn_config_flag), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_FEMBMS_CONFIG_NON_MBSFN_SUBFRAMECONFIG_TAG, &(pNfapiMsg->fembms_config.non_mbsfn_subframeconfig), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.bandwidth_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &(pNfapiMsg->srs_config.max_up_pts), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.srs_subframe_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &(pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &(pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &(pNfapiMsg->uplink_reference_signal_config.group_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &(pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &(pNfapiMsg->tdd_frame_structure_config.subframe_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &(pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &(pNfapiMsg->l23_config.data_report_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_L23_CONFIG_SFNSF_TAG, &(pNfapiMsg->l23_config.sfnsf), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.dl_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.ul_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_RF_BANDS_TAG, &(pNfapiMsg->nfapi_config.rf_bands), ppWritePackedMsg, end, &pack_rf_bands_value) && - pack_tlv(NFAPI_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &(pNfapiMsg->nfapi_config.max_transmit_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_EARFCN_TAG, &(pNfapiMsg->nfapi_config.earfcn), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && - pack_tlv(NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_umts_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && - pack_tlv(NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_lte_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && - pack_tlv(NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &(pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - -static uint8_t pack_nr_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - printf("\nRUNNING pack_param_response\n"); - nfapi_nr_param_response_scf_t *pNfapiMsg = (nfapi_nr_param_response_scf_t *)msg; - return (push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && - push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && - pack_tlv(NFAPI_NR_PARAM_TLV_RELEASE_CAPABILITY_TAG, &(pNfapiMsg->cell_param.release_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PHY_STATE_TAG, &(pNfapiMsg->cell_param.phy_state), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_SKIP_BLANK_DL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_dl_config), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_SKIP_BLANK_UL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_ul_config), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_NUM_CONFIG_TLVS_TO_REPORT_TAG, &(pNfapiMsg->cell_param.num_config_tlvs_to_report ), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_CYCLIC_PREFIX_TAG, &(pNfapiMsg->carrier_param.cyclic_prefix), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_DL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_dl), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_DL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_dl), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_UL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_ul), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_UL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_ul), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_CCE_MAPPING_TYPE_TAG, &(pNfapiMsg->pdcch_param.cce_mapping_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_CORESET_OUTSIDE_FIRST_3_OFDM_SYMS_OF_SLOT_TAG, &(pNfapiMsg->pdcch_param.coreset_outside_first_3_of_ofdm_syms_of_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PRECODER_GRANULARITY_CORESET_TAG, &(pNfapiMsg->pdcch_param.coreset_precoder_granularity_coreset), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDCCH_MU_MIMO_TAG, &(pNfapiMsg->pdcch_param.pdcch_mu_mimo), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDCCH_PRECODER_CYCLING_TAG, &(pNfapiMsg->pdcch_param.pdcch_precoder_cycling), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PDCCHS_PER_SLOT_TAG, &(pNfapiMsg->pdcch_param.max_pdcch_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUCCH_FORMATS_TAG, &(pNfapiMsg->pucch_param.pucch_formats), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PUCCHS_PER_SLOT_TAG, &(pNfapiMsg->pucch_param.max_pucchs_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pdsch_param.pdsch_mapping_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_allocation_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pdsch_param.pdsch_vrb_to_prb_mapping), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_CBG_TAG, &(pNfapiMsg->pdsch_param.pdsch_cbg), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_config_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DMRS_MAX_LENGTH_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_max_length), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_additional_pos), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PDSCH_S_YBS_PER_SLOT_TAG, &(pNfapiMsg->pdsch_param.max_pdsch_tbs_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_PDSCH_TAG, &(pNfapiMsg->pdsch_param.max_number_mimo_layers_pdsch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_DL_TAG, &(pNfapiMsg->pdsch_param.max_mu_mimo_users_dl), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DATA_IN_DMRS_SYMBOLS_TAG, &(pNfapiMsg->pdsch_param.pdsch_data_in_dmrs_symbols), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PREMPTION_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.premption_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_NON_SLOT_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.pdsch_non_slot_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_UCI_MUX_ULSCH_IN_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_mux_ulsch_in_pusch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_UCI_ONLY_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_only_pusch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_FREQUENCY_HOPPING_TAG, &(pNfapiMsg->pusch_param.pusch_frequency_hopping), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_config_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_DMRS_MAX_LEN_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_max_len), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_additional_pos), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_CBG_TAG, &(pNfapiMsg->pusch_param.pusch_cbg), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pusch_param.pusch_mapping_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_allocation_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pusch_param.pusch_vrb_to_prb_mapping), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_MAX_PTRS_PORTS_TAG, &(pNfapiMsg->pusch_param.pusch_max_ptrs_ports), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PDUSCHS_TBS_PER_SLOT_TAG, &(pNfapiMsg->pusch_param.max_pduschs_tbs_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_NON_CB_PUSCH_TAG, &(pNfapiMsg->pusch_param.max_number_mimo_layers_non_cb_pusch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_MODULATION_ORDER_UL_TAG, &(pNfapiMsg->pusch_param.supported_modulation_order_ul), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_UL_TAG, &(pNfapiMsg->pusch_param.max_mu_mimo_users_ul), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_DFTS_OFDM_SUPPORT_TAG, &(pNfapiMsg->pusch_param.dfts_ofdm_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_AGGREGATION_FACTOR_TAG, &(pNfapiMsg->pusch_param.pusch_aggregation_factor), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PRACH_LONG_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_long_formats), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PRACH_SHORT_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_short_formats), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_PRACH_RESTRICTED_SETS_TAG, &(pNfapiMsg->prach_param.prach_restricted_sets), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PRACH_FD_OCCASIONS_IN_A_SLOT_TAG, &(pNfapiMsg->prach_param.max_prach_fd_occasions_in_a_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_PARAM_TLV_RSSI_MEASUREMENT_SUPPORT_TAG, &(pNfapiMsg->measurement_param.rssi_measurement_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - // config: - pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_config_request_t *pNfapiMsg = (nfapi_config_request_t *)msg; - return ( push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && - // Do we check the phy state and then just fill those sepecified, however - // we do not know the duplex mode, so just attempt to pack all and assumme - // that the callee has set the right tlvs - pack_tlv(NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &(pNfapiMsg->subframe_config.duplex_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &(pNfapiMsg->subframe_config.pcfich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SUBFRAME_CONFIG_PB_TAG, &(pNfapiMsg->subframe_config.pb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.dl_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.ul_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.dl_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.ul_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &(pNfapiMsg->rf_config.reference_signal_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.tx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.rx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &(pNfapiMsg->phich_config.phich_resource), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &(pNfapiMsg->phich_config.phich_duration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &(pNfapiMsg->phich_config.phich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &(pNfapiMsg->sch_config.physical_cell_id), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->prach_config.configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &(pNfapiMsg->prach_config.zero_correlation_zone_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &(pNfapiMsg->prach_config.high_speed_flag), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->prach_config.frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &(pNfapiMsg->pusch_config.hopping_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &(pNfapiMsg->pusch_config.hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &(pNfapiMsg->pusch_config.number_of_subbands), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &(pNfapiMsg->pucch_config.delta_pucch_shift), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &(pNfapiMsg->pucch_config.n_cqi_rb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &(pNfapiMsg->pucch_config.n_an_cs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &(pNfapiMsg->pucch_config.n1_pucch_an), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.bandwidth_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &(pNfapiMsg->srs_config.max_up_pts), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.srs_subframe_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &(pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &(pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &(pNfapiMsg->uplink_reference_signal_config.group_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &(pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_PDSCH_TAG, &(pNfapiMsg->laa_config.ed_threshold_lbt_pdsch), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_DRS_TAG, &(pNfapiMsg->laa_config.ed_threshold_lbt_drs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_PD_THRESHOLD_TAG, &(pNfapiMsg->laa_config.pd_threshold), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_MULTI_CARRIER_TYPE_TAG, &(pNfapiMsg->laa_config.multi_carrier_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_MULTI_CARRIER_TX_TAG, &(pNfapiMsg->laa_config.multi_carrier_tx), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_MULTI_CARRIER_FREEZE_TAG, &(pNfapiMsg->laa_config.multi_carrier_freeze), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_TX_ANTENNA_PORTS_FOR_DRS_TAG, &(pNfapiMsg->laa_config.tx_antenna_ports_drs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_LAA_CONFIG_TRANSMISSION_POWER_FOR_DRS_TAG, &(pNfapiMsg->laa_config.tx_power_drs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PBCH_REPETITIONS_ENABLE_R13_TAG, &(pNfapiMsg->emtc_config.pbch_repetitions_enable_r13), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CATM_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_catm_root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CATM_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &(pNfapiMsg->emtc_config.prach_catm_zero_correlation_zone_configuration), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CATM_HIGH_SPEED_FLAG, &(pNfapiMsg->emtc_config.prach_catm_high_speed_flag), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_number_of_repetitions_per_attempt), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_starting_subframe_periodicity), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_number_of_repetitions_per_attempt), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_starting_subframe_periodicity), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_number_of_repetitions_per_attempt), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_starting_subframe_periodicity), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_number_of_repetitions_per_attempt), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_starting_subframe_periodicity), ppWritePackedMsg, end, - &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEA_TAG, &(pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodea), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEB_TAG, &(pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodeb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &(pNfapiMsg->tdd_frame_structure_config.subframe_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &(pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &(pNfapiMsg->l23_config.data_report_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_L23_CONFIG_SFNSF_TAG, &(pNfapiMsg->l23_config.sfnsf), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.dl_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.ul_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_PHY_RF_BANDS_TAG, &(pNfapiMsg->nfapi_config.rf_bands), ppWritePackedMsg, end, &pack_rf_bands_value) && - pack_tlv(NFAPI_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &(pNfapiMsg->nfapi_config.max_transmit_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_EARFCN_TAG, &(pNfapiMsg->nfapi_config.earfcn), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && - pack_tlv(NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_umts_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && - pack_tlv(NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_lte_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && - pack_tlv(NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &(pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - - -static uint8_t pack_nr_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - printf("\n\nEntering pack_config_request\n"); - nfapi_nr_config_request_scf_t *pNfapiMsg = (nfapi_nr_config_request_scf_t *)msg; - return (push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && - pack_tlv(NFAPI_NR_CONFIG_DL_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.dl_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_DL_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.dl_frequency), ppWritePackedMsg, end, &pack_uint32_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_DL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.dl_grid_size[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_DL_K0_TAG, &(pNfapiMsg->carrier_config.dl_k0[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_NUM_RX_ANT_TAG, &(pNfapiMsg->carrier_config.num_rx_ant), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_NUM_TX_ANT_TAG, &(pNfapiMsg->carrier_config.num_tx_ant), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_UL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.ul_grid_size[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_UL_K0_TAG, &(pNfapiMsg->carrier_config.ul_k0[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_UPLINK_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.uplink_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_UPLINK_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.uplink_frequency), ppWritePackedMsg, end, &pack_uint32_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_FRAME_DUPLEX_TYPE_TAG, &(pNfapiMsg->cell_config.frame_duplex_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_PHY_CELL_ID_TAG, &(pNfapiMsg->cell_config.phy_cell_id), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_PRACH_SEQUENCE_LENGTH_TAG, &(pNfapiMsg->prach_config.prach_sequence_length), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG, &(pNfapiMsg->prach_config.restricted_set_config), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SSB_PER_RACH_TAG, &(pNfapiMsg->prach_config.ssb_per_rach), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_PRACH_SUB_C_SPACING_TAG, &(pNfapiMsg->prach_config.prach_sub_c_spacing), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_PRACH_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_K1_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].k1), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_PRACH_ZERO_CORR_CONF_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_zero_corr_conf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_NUM_ROOT_SEQUENCES_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].num_root_sequences), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SCS_COMMON_TAG, &(pNfapiMsg->ssb_config.scs_common), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SS_PBCH_POWER_TAG, &(pNfapiMsg->ssb_config.ss_pbch_power), ppWritePackedMsg, end, &pack_uint32_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_BETA_PSS_TAG, &(pNfapiMsg->ssb_table.beta_pss), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_MIB_TAG, &(pNfapiMsg->ssb_table.MIB), ppWritePackedMsg, end, &pack_uint32_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[0].ssb_mask), ppWritePackedMsg, end, &pack_uint32_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[1].ssb_mask), ppWritePackedMsg, end, &pack_uint32_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SSB_OFFSET_POINT_A_TAG, &(pNfapiMsg->ssb_table.ssb_offset_point_a), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SSB_PERIOD_TAG, &(pNfapiMsg->ssb_table.ssb_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SSB_SUBCARRIER_OFFSET_TAG, &(pNfapiMsg->ssb_table.ssb_subcarrier_offset), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_TDD_PERIOD_TAG, &(pNfapiMsg->tdd_table.tdd_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_CONFIG_SLOT_CONFIG_TAG, &(pNfapiMsg->tdd_table.max_tdd_periodicity_list[0].max_num_of_symbol_per_slot_list[0].slot_config), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - //pack_tlv(NFAPI_NR_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.dl_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - //pack_tlv(NFAPI_NR_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &(pNfapiMsg->nfapi_config.max_transmit_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - //pack_tlv(NFAPI_NR_NFAPI_NRARFCN_TAG, &(pNfapiMsg->nfapi_config.nrarfcn), ppWritePackedMsg, end, &pack_uint32_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && - pack_tlv(NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - //pack_tlv(NFAPI_NR_NFAPI_RF_BANDS_TAG, &(pNfapiMsg->nfapi_config.rf_bands), ppWritePackedMsg, end, &pack_rf_bands_value) && - pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_tlv(NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - //pack_tlv(NFAPI_NR_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.ul_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - -static uint8_t pack_nr_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_config_response_scf_t *pNfapiMsg = (nfapi_nr_config_response_scf_t *)msg; - return ( push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - -static uint8_t pack_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_config_response_t *pNfapiMsg = (nfapi_config_response_t *)msg; - return ( push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - -static uint8_t pack_nr_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_start_request_scf_t *pNfapiMsg = (nfapi_nr_start_request_scf_t *)msg; - return pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); -} - -static uint8_t pack_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_start_request_t *pNfapiMsg = (nfapi_start_request_t *)msg; - return pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); -} - -static uint8_t pack_nr_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_start_response_scf_t *pNfapiMsg = (nfapi_nr_start_response_scf_t *)msg; - return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end ) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - -static uint8_t pack_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_start_response_t *pNfapiMsg = (nfapi_start_response_t *)msg; - return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end ) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - - -static uint8_t pack_stop_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_stop_request_t *pNfapiMsg = (nfapi_stop_request_t *)msg; - return pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); -} - -static uint8_t pack_stop_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_stop_response_t *pNfapiMsg = (nfapi_stop_response_t *)msg; - return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); -} - -static uint8_t pack_measurement_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_measurement_request_t *pNfapiMsg = (nfapi_measurement_request_t *)msg; - return( pack_tlv(NFAPI_MEASUREMENT_REQUEST_DL_RS_XTX_POWER_TAG, &(pNfapiMsg->dl_rs_tx_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_MEASUREMENT_REQUEST_RECEIVED_INTERFERENCE_POWER_TAG, &(pNfapiMsg->received_interference_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_tlv(NFAPI_MEASUREMENT_REQUEST_THERMAL_NOISE_POWER_TAG, &(pNfapiMsg->thermal_noise_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_recevied_interference_power_measurement_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_received_interference_power_measurement_t *value = (nfapi_received_interference_power_measurement_t *)tlv; - return ( push16(value->number_of_resource_blocks, ppWritePackedMsg, end) && - pusharrays16(value->received_interference_power, NFAPI_MAX_RECEIVED_INTERFERENCE_POWER_RESULTS, value->number_of_resource_blocks, ppWritePackedMsg, end)); -} - -static uint8_t pack_measurement_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - nfapi_measurement_response_t *pNfapiMsg = (nfapi_measurement_response_t *)msg; - return( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && - pack_tlv(NFAPI_MEASUREMENT_RESPONSE_DL_RS_POWER_MEASUREMENT_TAG, &(pNfapiMsg->dl_rs_tx_power_measurement), ppWritePackedMsg, end, &pack_int16_tlv_value) && - pack_tlv(NFAPI_MEASUREMENT_RESPONSE_RECEIVED_INTERFERENCE_POWER_MEASUREMENT_TAG, &(pNfapiMsg->received_interference_power_measurement), ppWritePackedMsg, end, - &pack_recevied_interference_power_measurement_value) && - pack_tlv(NFAPI_MEASUREMENT_RESPONSE_THERMAL_NOISE_MEASUREMENT_TAG, &(pNfapiMsg->thermal_noise_power_measurement), ppWritePackedMsg, end, &pack_uint16_tlv_value) && - pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nr_p5_message_body(nfapi_p4_p5_message_header_t *header, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - uint8_t result = 0; - - // look for the specific message - switch (header->message_id) { - case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_REQUEST: - result = pack_nr_pnf_param_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_PARAM_RESPONSE: - result = pack_nr_pnf_param_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_REQUEST: - result = pack_nr_pnf_config_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_RESPONSE: - result = pack_nr_pnf_config_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_START_REQUEST: - result = pack_nr_pnf_start_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_START_RESPONSE: - result = pack_nr_pnf_start_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_REQUEST: - result = pack_nr_pnf_stop_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_STOP_RESPONSE: - result = pack_nr_pnf_stop_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PARAM_REQUEST: - result = pack_nr_param_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE: - result = pack_nr_param_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_CONFIG_REQUEST: - result = pack_nr_config_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE: - result = pack_nr_config_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_START_REQUEST: - result = pack_nr_start_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE: - result = pack_nr_start_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST: - result = pack_stop_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: - result = pack_stop_response(header, ppWritePackedMsg, end, config); - break; - - default: { - if(header->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - header->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->pack_p4_p5_vendor_extension) { - result = (config->pack_p4_p5_vendor_extension)(header, ppWritePackedMsg, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, header->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, header->message_id); - } - } - break; - } - - return result; -} - - -static uint8_t pack_p5_message_body(nfapi_p4_p5_message_header_t *header, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { - uint8_t result = 0; - - // look for the specific message - switch (header->message_id) { - case NFAPI_PNF_PARAM_REQUEST: - result = pack_pnf_param_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_PARAM_RESPONSE: - result = pack_pnf_param_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_CONFIG_REQUEST: - result = pack_pnf_config_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_CONFIG_RESPONSE: - result = pack_pnf_config_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_START_REQUEST: - result = pack_pnf_start_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_START_RESPONSE: - result = pack_pnf_start_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_STOP_REQUEST: - result = pack_pnf_stop_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PNF_STOP_RESPONSE: - result = pack_pnf_stop_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PARAM_REQUEST: - result = pack_param_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_PARAM_RESPONSE: - result = pack_param_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_CONFIG_REQUEST: - result = pack_config_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_CONFIG_RESPONSE: - result = pack_config_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_START_REQUEST: - result = pack_start_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_START_RESPONSE: - result = pack_start_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_STOP_REQUEST: - result = pack_stop_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_STOP_RESPONSE: - result = pack_stop_response(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_MEASUREMENT_REQUEST: - result = pack_measurement_request(header, ppWritePackedMsg, end, config); - break; - - case NFAPI_MEASUREMENT_RESPONSE: - result = pack_measurement_response(header, ppWritePackedMsg, end, config); - break; - - default: { - if(header->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - header->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->pack_p4_p5_vendor_extension) { - result = (config->pack_p4_p5_vendor_extension)(header, ppWritePackedMsg, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, header->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, header->message_id); - } - } - break; - } - - return result; -} - - -// helper function for message length calculation - -// takes the pointers to the start of message to end of message - -static uint32_t get_packed_msg_len(uintptr_t msgHead, uintptr_t msgEnd) { - if (msgEnd < msgHead) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "get_packed_msg_len: Error in pointers supplied %d, %d\n", msgHead, msgEnd); - return 0; - } - - return (msgEnd - msgHead); -} - -// Main pack function - public -int nfapi_nr_p5_message_pack(void *pMessageBuf, uint32_t messageBufLen, void *pPackedBuf, uint32_t packedBufLen, nfapi_p4_p5_codec_config_t *config) { - nfapi_p4_p5_message_header_t *pMessageHeader = pMessageBuf; - uint8_t *pWritePackedMessage = pPackedBuf; - uint8_t *pPackMessageEnd = pPackedBuf + packedBufLen; - uint8_t *pPackedLengthField = &pWritePackedMessage[4]; - uint32_t packedMsgLen; - uint16_t packedMsgLen16; - - if (pMessageBuf == NULL || pPackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Pack supplied pointers are null\n"); - return -1; - } - - // pack the message - if(push16(pMessageHeader->phy_id, &pWritePackedMessage, pPackMessageEnd) && - push16(pMessageHeader->message_id, &pWritePackedMessage, pPackMessageEnd) && - push16(0, &pWritePackedMessage, pPackMessageEnd) && - push16(pMessageHeader->spare, &pWritePackedMessage, pPackMessageEnd) && - pack_nr_p5_message_body(pMessageHeader, &pWritePackedMessage, pPackMessageEnd, config)) { - // check for a valid message length - packedMsgLen = get_packed_msg_len((uintptr_t)pPackedBuf, (uintptr_t)pWritePackedMessage); - - if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); - return -1; - } else { - packedMsgLen16 = (uint16_t)packedMsgLen; - } - - // Update the message length in the header - if(!push16(packedMsgLen16, &pPackedLengthField, pPackMessageEnd)) - return -1; - - // return the packed length - return (packedMsgLen); - } else { - // Failed to pack the meassage - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Failed to pack message\n"); - return -1; - } -} - -int nfapi_p5_message_pack(void *pMessageBuf, uint32_t messageBufLen, void *pPackedBuf, uint32_t packedBufLen, nfapi_p4_p5_codec_config_t *config) { - nfapi_p4_p5_message_header_t *pMessageHeader = pMessageBuf; - uint8_t *pWritePackedMessage = pPackedBuf; - uint8_t *pPackMessageEnd = pPackedBuf + packedBufLen; - uint8_t *pPackedLengthField = &pWritePackedMessage[4]; - uint32_t packedMsgLen; - uint16_t packedMsgLen16; - - if (pMessageBuf == NULL || pPackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Pack supplied pointers are null\n"); - return -1; - } - - // pack the message - if(push16(pMessageHeader->phy_id, &pWritePackedMessage, pPackMessageEnd) && - push16(pMessageHeader->message_id, &pWritePackedMessage, pPackMessageEnd) && - push16(0/*pMessageHeader->message_length*/, &pWritePackedMessage, pPackMessageEnd) && - push16(pMessageHeader->spare, &pWritePackedMessage, pPackMessageEnd) && - pack_p5_message_body(pMessageHeader, &pWritePackedMessage, pPackMessageEnd, config)) { - // check for a valid message length - packedMsgLen = get_packed_msg_len((uintptr_t)pPackedBuf, (uintptr_t)pWritePackedMessage); - - if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); - return -1; - } else { - packedMsgLen16 = (uint16_t)packedMsgLen; - } - - // Update the message length in the header - if(!push16(packedMsgLen16, &pPackedLengthField, pPackMessageEnd)) - return -1; - - // return the packed length - return (packedMsgLen); - } else { - // Failed to pack the meassage - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Failed to pack message\n"); - return -1; - } -} - - - -// Unpack routines - - -static uint8_t unpack_nr_pnf_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_param_request_t *pNfapiMsg = (nfapi_nr_pnf_param_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_pnf_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_param_request_t *pNfapiMsg = (nfapi_pnf_param_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_pnf_param_general_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_param_general_t *value = (nfapi_pnf_param_general_t *)tlv; - return( pull8(ppReadPackedMsg, &value->nfapi_sync_mode, end) && - pull8(ppReadPackedMsg, &value->location_mode, end) && - pull16(ppReadPackedMsg, &value->location_coordinates_length, end) && - pullarray8(ppReadPackedMsg, value->location_coordinates, NFAPI_PNF_PARAM_GENERAL_LOCATION_LENGTH, value->location_coordinates_length, end) && - pull32(ppReadPackedMsg, &value->dl_config_timing, end) && - pull32(ppReadPackedMsg, &value->tx_timing, end) && - pull32(ppReadPackedMsg, &value->ul_config_timing, end) && - pull32(ppReadPackedMsg, &value->hi_dci0_timing, end) && - pull16(ppReadPackedMsg, &value->maximum_number_phys, end) && - pull16(ppReadPackedMsg, &value->maximum_total_bandwidth, end) && - pull8(ppReadPackedMsg, &value->maximum_total_number_dl_layers, end) && - pull8(ppReadPackedMsg, &value->maximum_total_number_ul_layers, end) && - pull8(ppReadPackedMsg, &value->shared_bands, end) && - pull8(ppReadPackedMsg, &value->shared_pa, end) && - pulls16(ppReadPackedMsg, &value->maximum_total_power, end) && - pullarray8(ppReadPackedMsg, value->oui, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, end)); -} - -static uint8_t unpack_rf_config_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_rf_config_info_t *info = (nfapi_rf_config_info_t *)elem; - return pull16(ppReadPackedMsg, &info->rf_config_index, end); -} - -static uint8_t unpack_pnf_phy_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_info_t *phy = (nfapi_pnf_phy_info_t *)elem; - return ( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && - pull16(ppReadPackedMsg, &phy->number_of_rfs, end) && - unpackarray(ppReadPackedMsg, phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, end, &unpack_rf_config_info) && - pull16(ppReadPackedMsg, &phy->number_of_rf_exclusions, end) && - unpackarray(ppReadPackedMsg, phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, end, &unpack_rf_config_info) && - pull16(ppReadPackedMsg, &phy->downlink_channel_bandwidth_supported, end) && - pull16(ppReadPackedMsg, &phy->uplink_channel_bandwidth_supported, end) && - pull8(ppReadPackedMsg, &phy->number_of_dl_layers_supported, end) && - pull8(ppReadPackedMsg, &phy->number_of_ul_layers_supported, end) && - pull16(ppReadPackedMsg, &phy->maximum_3gpp_release_supported, end) && - pull8(ppReadPackedMsg, &phy->nmm_modes_supported, end)); -} - - -static uint8_t unpack_pnf_phy_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_t *value = (nfapi_pnf_phy_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && - unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_info)); -} - -static uint8_t unpack_pnf_rf_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_rf_info_t *rf = (nfapi_pnf_rf_info_t *)elem; - return( pull16(ppReadPackedMsg, &rf->rf_config_index, end) && - pull16(ppReadPackedMsg, &rf->band, end) && - pulls16(ppReadPackedMsg, &rf->maximum_transmit_power, end) && - pulls16(ppReadPackedMsg, &rf->minimum_transmit_power, end) && - pull8(ppReadPackedMsg, &rf->number_of_antennas_suppported, end) && - pull32(ppReadPackedMsg, &rf->minimum_downlink_frequency, end) && - pull32(ppReadPackedMsg, &rf->maximum_downlink_frequency, end) && - pull32(ppReadPackedMsg, &rf->minimum_uplink_frequency, end) && - pull32(ppReadPackedMsg, &rf->maximum_uplink_frequency, end)); -} -static uint8_t unpack_pnf_rf_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_rf_t *value = (nfapi_pnf_rf_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_rfs, end) && - unpackarray(ppReadPackedMsg, value->rf, sizeof(nfapi_pnf_rf_info_t), NFAPI_MAX_PNF_RF, value->number_of_rfs, end, &unpack_pnf_rf_info)); -} - -static uint8_t unpack_pnf_phy_rel10_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel10_info_t *phy = (nfapi_pnf_phy_rel10_info_t *)elem; - return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && - pull16(ppReadPackedMsg, &phy->transmission_mode_7_supported, end) && - pull16(ppReadPackedMsg, &phy->transmission_mode_8_supported, end) && - pull16(ppReadPackedMsg, &phy->two_antenna_ports_for_pucch, end) && - pull16(ppReadPackedMsg, &phy->transmission_mode_9_supported, end) && - pull16(ppReadPackedMsg, &phy->simultaneous_pucch_pusch, end) && - pull16(ppReadPackedMsg, &phy->four_layer_tx_with_tm3_and_tm4, end)); -} -static uint8_t unpack_pnf_phy_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel10_t *value = (nfapi_pnf_phy_rel10_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && - unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel10_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel10_info)); -} - - -static uint8_t unpack_pnf_phy_rel11_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel11_info_t *phy = (nfapi_pnf_phy_rel11_info_t *)elem; - return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && - pull16(ppReadPackedMsg, &phy->edpcch_supported, end) && - pull16(ppReadPackedMsg, &phy->multi_ack_csi_reporting, end ) && - pull16(ppReadPackedMsg, &phy->pucch_tx_diversity, end) && - pull16(ppReadPackedMsg, &phy->ul_comp_supported, end) && - pull16(ppReadPackedMsg, &phy->transmission_mode_5_supported, end)); -} - -static uint8_t unpack_pnf_phy_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel11_t *value = (nfapi_pnf_phy_rel11_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && - unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel11_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel11_info)); -} - -static uint8_t unpack_phy_phy_rel12_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel12_info_t *phy = (nfapi_pnf_phy_rel12_info_t *)elem; - return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && - pull16(ppReadPackedMsg, &phy->csi_subframe_set, end) && - pull16(ppReadPackedMsg, &phy->enhanced_4tx_codebook, end) && - pull16(ppReadPackedMsg, &phy->drs_supported, end) && - pull16(ppReadPackedMsg, &phy->ul_64qam_supported, end) && - pull16(ppReadPackedMsg, &phy->transmission_mode_10_supported, end) && - pull16(ppReadPackedMsg, &phy->alternative_bts_indices, end)); -} - -static uint8_t unpack_pnf_phy_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel12_t *value = (nfapi_pnf_phy_rel12_t *)tlv; - return (pull16(ppReadPackedMsg, &value->number_of_phys, end) && - unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel12_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_phy_phy_rel12_info)); -} - -static uint8_t unpack_pnf_phy_rel13_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_info_t *phy = (nfapi_pnf_phy_rel13_info_t *)elem; - return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && - pull16(ppReadPackedMsg, &phy->pucch_format4_supported, end) && - pull16(ppReadPackedMsg, &phy->pucch_format5_supported, end) && - pull16(ppReadPackedMsg, &phy->more_than_5_ca_support, end) && - pull16(ppReadPackedMsg, &phy->laa_supported, end) && - pull16(ppReadPackedMsg, &phy->laa_ending_in_dwpts_supported, end) && - pull16(ppReadPackedMsg, &phy->laa_starting_in_second_slot_supported, end) && - pull16(ppReadPackedMsg, &phy->beamforming_supported, end) && - pull16(ppReadPackedMsg, &phy->csi_rs_enhancement_supported, end) && - pull16(ppReadPackedMsg, &phy->drms_enhancement_supported, end) && - pull16(ppReadPackedMsg, &phy->srs_enhancement_supported, end)); -} - -static uint8_t unpack_pnf_phy_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_t *value = (nfapi_pnf_phy_rel13_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && - unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel13_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel13_info)); -} - -static uint8_t unpack_pnf_phy_rel13_nb_info_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_nb_iot_info_t *phy = (nfapi_pnf_phy_rel13_nb_iot_info_t *)elem; - return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && - pull16(ppReadPackedMsg, &phy->number_of_rfs, end) && - unpackarray(ppReadPackedMsg, phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, end, &unpack_rf_config_info) && - pull16(ppReadPackedMsg, &phy->number_of_rf_exclusions, end) && - unpackarray(ppReadPackedMsg, phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, end, &unpack_rf_config_info) && - pull8(ppReadPackedMsg, &phy->number_of_dl_layers_supported, end) && - pull8(ppReadPackedMsg, &phy->number_of_ul_layers_supported, end) && - pull16(ppReadPackedMsg, &phy->maximum_3gpp_release_supported, end) && - pull8(ppReadPackedMsg, &phy->nmm_modes_supported, end)); -} - -static uint8_t unpack_pnf_phy_rel13_nb_iot_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rel13_nb_iot_t *value = (nfapi_pnf_phy_rel13_nb_iot_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && - unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel13_nb_iot_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel13_nb_info_info)); -} - -static uint8_t unpack_nr_pnf_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_param_response_t *pNfapiMsg = (nfapi_nr_pnf_param_response_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, &unpack_pnf_param_general_value}, - { NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, &unpack_pnf_phy_value}, - }; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - - - -static uint8_t unpack_pnf_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_param_response_t *pNfapiMsg = (nfapi_pnf_param_response_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, &unpack_pnf_param_general_value}, - { NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, &unpack_pnf_phy_value}, - { NFAPI_PNF_RF_TAG, &pNfapiMsg->pnf_rf, &unpack_pnf_rf_value}, - { NFAPI_PNF_PHY_REL10_TAG, &pNfapiMsg->pnf_phy_rel10, &unpack_pnf_phy_rel10_value}, - { NFAPI_PNF_PHY_REL11_TAG, &pNfapiMsg->pnf_phy_rel11, &unpack_pnf_phy_rel11_value}, - { NFAPI_PNF_PHY_REL12_TAG, &pNfapiMsg->pnf_phy_rel12, &unpack_pnf_phy_rel12_value}, - { NFAPI_PNF_PHY_REL13_TAG, &pNfapiMsg->pnf_phy_rel13, &unpack_pnf_phy_rel13_value}, - { NFAPI_PNF_PHY_REL13_NB_IOT_TAG, &pNfapiMsg->pnf_phy_rel13_nb_iot, &unpack_pnf_phy_rel13_nb_iot_value}, - - }; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_phy_rf_config_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_phy_rf_config_info_t *rf = (nfapi_phy_rf_config_info_t *)elem; - return( pull16(ppReadPackedMsg, &rf->phy_id, end) && - pull16(ppReadPackedMsg, &rf->phy_config_index, end) && - pull16(ppReadPackedMsg, &rf->rf_config_index, end)); -} - -static uint8_t unpack_pnf_phy_rf_config_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_pnf_phy_rf_config_t *value = (nfapi_pnf_phy_rf_config_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_phy_rf_config_info, end) && - unpackarray(ppReadPackedMsg, value->phy_rf_config, sizeof(nfapi_phy_rf_config_info_t), NFAPI_MAX_PHY_RF_INSTANCES, value->number_phy_rf_config_info, end, &unpack_phy_rf_config_info)); -} - -static uint8_t unpack_nr_pnf_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_config_request_t *pNfapiMsg = (nfapi_nr_pnf_config_request_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, &unpack_pnf_phy_rf_config_value}, - }; - return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension); -} - - - -static uint8_t unpack_pnf_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_config_request_t *pNfapiMsg = (nfapi_pnf_config_request_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, &unpack_pnf_phy_rf_config_value}, - }; - return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension); -} - - -static uint8_t unpack_nr_pnf_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_config_response_t *pNfapiMsg = (nfapi_nr_pnf_config_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_pnf_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_config_response_t *pNfapiMsg = (nfapi_pnf_config_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_nr_pnf_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_start_request_t *pNfapiMsg = (nfapi_nr_pnf_start_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_pnf_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_start_request_t *pNfapiMsg = (nfapi_pnf_start_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_pnf_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_start_response_t *pNfapiMsg = (nfapi_pnf_start_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end ) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_nr_pnf_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_pnf_start_response_t *pNfapiMsg = (nfapi_nr_pnf_start_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end ) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - - -static uint8_t unpack_pnf_stop_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_stop_request_t *pNfapiMsg = (nfapi_pnf_stop_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_pnf_stop_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_pnf_stop_response_t *pNfapiMsg = (nfapi_pnf_stop_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_param_request_t *pNfapiMsg = (nfapi_param_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_nr_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_param_request_scf_t *pNfapiMsg = (nfapi_nr_param_request_scf_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_param_response_t *pNfapiMsg = (nfapi_param_response_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_L1_STATUS_PHY_STATE_TAG, &pNfapiMsg->l1_status.phy_state, &unpack_uint16_tlv_value}, - - { NFAPI_PHY_CAPABILITIES_DL_BANDWIDTH_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.dl_bandwidth_support, &unpack_uint16_tlv_value}, - { NFAPI_PHY_CAPABILITIES_UL_BANDWIDTH_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.ul_bandwidth_support, &unpack_uint16_tlv_value}, - { NFAPI_PHY_CAPABILITIES_DL_MODULATION_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.dl_modulation_support, &unpack_uint16_tlv_value}, - { NFAPI_PHY_CAPABILITIES_UL_MODULATION_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.ul_modulation_support, &unpack_uint16_tlv_value}, - { NFAPI_PHY_CAPABILITIES_PHY_ANTENNA_CAPABILITY_TAG, &pNfapiMsg->phy_capabilities.phy_antenna_capability, &unpack_uint16_tlv_value}, - { NFAPI_PHY_CAPABILITIES_RELEASE_CAPABILITY_TAG, &pNfapiMsg->phy_capabilities.release_capability, &unpack_uint16_tlv_value}, - { NFAPI_PHY_CAPABILITIES_MBSFN_CAPABILITY_TAG, &pNfapiMsg->phy_capabilities.mbsfn_capability, &unpack_uint16_tlv_value}, - - { NFAPI_LAA_CAPABILITY_LAA_SUPPORT_TAG, &pNfapiMsg->laa_capability.laa_support, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CAPABILITY_PD_SENSING_LBT_SUPPORT_TAG, &pNfapiMsg->laa_capability.pd_sensing_lbt_support, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CAPABILITY_MULTI_CARRIER_LBT_SUPPORT_TAG, &pNfapiMsg->laa_capability.multi_carrier_lbt_support, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CAPABILITY_PARTIAL_SF_SUPPORT_TAG, &pNfapiMsg->laa_capability.partial_sf_support, &unpack_uint16_tlv_value}, - - { NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &pNfapiMsg->subframe_config.duplex_mode, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &pNfapiMsg->subframe_config.pcfich_power_offset, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_PB_TAG, &pNfapiMsg->subframe_config.pb, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.dl_cyclic_prefix_type, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.ul_cyclic_prefix_type, &unpack_uint16_tlv_value}, - - { NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.dl_channel_bandwidth, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.ul_channel_bandwidth, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &pNfapiMsg->rf_config.reference_signal_power, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.tx_antenna_ports, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.rx_antenna_ports, &unpack_uint16_tlv_value}, - - { NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &pNfapiMsg->phich_config.phich_resource, &unpack_uint16_tlv_value}, - { NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &pNfapiMsg->phich_config.phich_duration, &unpack_uint16_tlv_value}, - { NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &pNfapiMsg->phich_config.phich_power_offset, &unpack_uint16_tlv_value}, - - { NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, - { NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, - { NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &pNfapiMsg->sch_config.physical_cell_id, &unpack_uint16_tlv_value}, - - { NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &pNfapiMsg->prach_config.configuration_index, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &pNfapiMsg->prach_config.root_sequence_index, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &pNfapiMsg->prach_config.zero_correlation_zone_configuration, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &pNfapiMsg->prach_config.high_speed_flag, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &pNfapiMsg->prach_config.frequency_offset, &unpack_uint16_tlv_value}, - - { NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &pNfapiMsg->pusch_config.hopping_mode, &unpack_uint16_tlv_value}, - { NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &pNfapiMsg->pusch_config.hopping_offset, &unpack_uint16_tlv_value}, - { NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &pNfapiMsg->pusch_config.number_of_subbands, &unpack_uint16_tlv_value}, - - { NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &pNfapiMsg->pucch_config.delta_pucch_shift, &unpack_uint16_tlv_value}, - { NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &pNfapiMsg->pucch_config.n_cqi_rb, &unpack_uint16_tlv_value}, - { NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &pNfapiMsg->pucch_config.n_an_cs, &unpack_uint16_tlv_value}, - { NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &pNfapiMsg->pucch_config.n1_pucch_an, &unpack_uint16_tlv_value}, - - { NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &pNfapiMsg->srs_config.bandwidth_configuration, &unpack_uint16_tlv_value}, - { NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &pNfapiMsg->srs_config.max_up_pts, &unpack_uint16_tlv_value}, - { NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &pNfapiMsg->srs_config.srs_subframe_configuration, &unpack_uint16_tlv_value}, - { NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission, &unpack_uint16_tlv_value}, - - { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping, &unpack_uint16_tlv_value}, - { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &pNfapiMsg->uplink_reference_signal_config.group_assignment, &unpack_uint16_tlv_value}, - { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms, &unpack_uint16_tlv_value}, - - { NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &pNfapiMsg->tdd_frame_structure_config.subframe_assignment, &unpack_uint16_tlv_value}, - { NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns, &unpack_uint16_tlv_value}, - - { NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &pNfapiMsg->l23_config.data_report_mode, &unpack_uint16_tlv_value}, - { NFAPI_L23_CONFIG_SFNSF_TAG, &pNfapiMsg->l23_config.sfnsf, &unpack_uint16_tlv_value}, - - { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv4, &unpack_ipv4_address_value}, - { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv6, &unpack_ipv6_address_value}, - { NFAPI_NFAPI_P7_VNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_vnf_port, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv4, &unpack_ipv4_address_value}, - { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv6, &unpack_ipv6_address_value}, - { NFAPI_NFAPI_P7_PNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_pnf_port, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.dl_ue_per_sf, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.ul_ue_per_sf, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_RF_BANDS_TAG, &pNfapiMsg->nfapi_config.rf_bands, &unpack_rf_bands_value}, - { NFAPI_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &pNfapiMsg->nfapi_config.timing_info_mode, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &pNfapiMsg->nfapi_config.timing_info_period, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &pNfapiMsg->nfapi_config.max_transmit_power, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_EARFCN_TAG, &pNfapiMsg->nfapi_config.earfcn, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands, &unpack_nmm_frequency_bands_value}, - { NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_umts_frequency_bands, &unpack_nmm_frequency_bands_value}, - { NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_lte_frequency_bands, &unpack_nmm_frequency_bands_value}, - { NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported, &unpack_uint8_tlv_value}, - - }; - return ( pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_nr_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_param_response_scf_t *pNfapiMsg = (nfapi_nr_param_response_scf_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_NR_PARAM_TLV_RELEASE_CAPABILITY_TAG, &(pNfapiMsg->cell_param.release_capability), &unpack_uint16_tlv_value}, - { NFAPI_NR_PARAM_TLV_PHY_STATE_TAG, &(pNfapiMsg->cell_param.phy_state),&unpack_uint16_tlv_value}, - { NFAPI_NR_PARAM_TLV_SKIP_BLANK_DL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_dl_config), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_SKIP_BLANK_UL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_ul_config), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_NUM_CONFIG_TLVS_TO_REPORT_TAG, &(pNfapiMsg->cell_param.num_config_tlvs_to_report ), &unpack_uint16_tlv_value}, - - { NFAPI_NR_PARAM_TLV_CYCLIC_PREFIX_TAG, &(pNfapiMsg->carrier_param.cyclic_prefix), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_DL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_dl), &unpack_uint16_tlv_value}, - { NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_DL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_dl), &unpack_uint16_tlv_value}, - { NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_UL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_ul), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_UL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_ul), &unpack_uint16_tlv_value}, - - - { NFAPI_NR_PARAM_TLV_CCE_MAPPING_TYPE_TAG, &(pNfapiMsg->pdcch_param.cce_mapping_type), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_CORESET_OUTSIDE_FIRST_3_OFDM_SYMS_OF_SLOT_TAG, &(pNfapiMsg->pdcch_param.coreset_outside_first_3_of_ofdm_syms_of_slot), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PRECODER_GRANULARITY_CORESET_TAG, &(pNfapiMsg->pdcch_param.coreset_precoder_granularity_coreset), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDCCH_MU_MIMO_TAG, &(pNfapiMsg->pdcch_param.pdcch_mu_mimo), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDCCH_PRECODER_CYCLING_TAG, &(pNfapiMsg->pdcch_param.pdcch_precoder_cycling), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_PDCCHS_PER_SLOT_TAG, &(pNfapiMsg->pdcch_param.max_pdcch_per_slot), &unpack_uint8_tlv_value}, - - { NFAPI_NR_PARAM_TLV_PUCCH_FORMATS_TAG, &(pNfapiMsg->pucch_param.pucch_formats), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_PUCCHS_PER_SLOT_TAG, &(pNfapiMsg->pucch_param.max_pucchs_per_slot), &unpack_uint8_tlv_value}, - - { NFAPI_NR_PARAM_TLV_PDSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pdsch_param.pdsch_mapping_type), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_allocation_types), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pdsch_param.pdsch_vrb_to_prb_mapping), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_CBG_TAG, &(pNfapiMsg->pdsch_param.pdsch_cbg), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_config_types), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_DMRS_MAX_LENGTH_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_max_length), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_additional_pos), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_PDSCH_S_YBS_PER_SLOT_TAG, &(pNfapiMsg->pdsch_param.max_pdsch_tbs_per_slot), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_PDSCH_TAG, &(pNfapiMsg->pdsch_param.max_number_mimo_layers_pdsch), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_DL_TAG, &(pNfapiMsg->pdsch_param.max_mu_mimo_users_dl), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_DATA_IN_DMRS_SYMBOLS_TAG, &(pNfapiMsg->pdsch_param.pdsch_data_in_dmrs_symbols), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PREMPTION_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.premption_support), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PDSCH_NON_SLOT_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.pdsch_non_slot_support), &unpack_uint8_tlv_value}, - - { NFAPI_NR_PARAM_TLV_UCI_MUX_ULSCH_IN_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_mux_ulsch_in_pusch), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_UCI_ONLY_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_only_pusch), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_FREQUENCY_HOPPING_TAG, &(pNfapiMsg->pusch_param.pusch_frequency_hopping), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_config_types), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_DMRS_MAX_LEN_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_max_len), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_additional_pos), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_CBG_TAG, &(pNfapiMsg->pusch_param.pusch_cbg), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pusch_param.pusch_mapping_type), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_allocation_types), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pusch_param.pusch_vrb_to_prb_mapping), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_MAX_PTRS_PORTS_TAG, &(pNfapiMsg->pusch_param.pusch_max_ptrs_ports), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_PDUSCHS_TBS_PER_SLOT_TAG, &(pNfapiMsg->pusch_param.max_pduschs_tbs_per_slot), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_NON_CB_PUSCH_TAG, &(pNfapiMsg->pusch_param.max_number_mimo_layers_non_cb_pusch), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_SUPPORTED_MODULATION_ORDER_UL_TAG, &(pNfapiMsg->pusch_param.supported_modulation_order_ul), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_UL_TAG, &(pNfapiMsg->pusch_param.max_mu_mimo_users_ul), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_DFTS_OFDM_SUPPORT_TAG, &(pNfapiMsg->pusch_param.dfts_ofdm_support), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PUSCH_AGGREGATION_FACTOR_TAG, &(pNfapiMsg->pusch_param.pusch_aggregation_factor), &unpack_uint8_tlv_value}, - - { NFAPI_NR_PARAM_TLV_PRACH_LONG_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_long_formats), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PRACH_SHORT_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_short_formats), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_PRACH_RESTRICTED_SETS_TAG, &(pNfapiMsg->prach_param.prach_restricted_sets), &unpack_uint8_tlv_value}, - { NFAPI_NR_PARAM_TLV_MAX_PRACH_FD_OCCASIONS_IN_A_SLOT_TAG, &(pNfapiMsg->prach_param.max_prach_fd_occasions_in_a_slot), &unpack_uint8_tlv_value}, - - { NFAPI_NR_PARAM_TLV_RSSI_MEASUREMENT_SUPPORT_TAG, &(pNfapiMsg->measurement_param.rssi_measurement_support), &unpack_uint8_tlv_value}, - //config - { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv4, &unpack_ipv4_address_value}, - { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv6, &unpack_ipv6_address_value}, - { NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_vnf_port, &unpack_uint16_tlv_value}, - { NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv4, &unpack_ipv4_address_value}, - { NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv6, &unpack_ipv6_address_value}, - { NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_pnf_port, &unpack_uint16_tlv_value}, - { NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint8_tlv_value}, - { NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &pNfapiMsg->nfapi_config.timing_info_mode, &unpack_uint8_tlv_value}, - { NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &pNfapiMsg->nfapi_config.timing_info_period, &unpack_uint8_tlv_value}, - }; - // print ppReadPackedMsg - uint8_t *ptr = *ppReadPackedMsg; - printf("\n Read message unpack_param_response: "); - - while(ptr < end) { - printf(" %d ", *ptr); - ptr++; - } - - printf("\n"); - return ( pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_config_request_t *pNfapiMsg = (nfapi_config_request_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &pNfapiMsg->subframe_config.duplex_mode, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &pNfapiMsg->subframe_config.pcfich_power_offset, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_PB_TAG, &pNfapiMsg->subframe_config.pb, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.dl_cyclic_prefix_type, &unpack_uint16_tlv_value}, - { NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.ul_cyclic_prefix_type, &unpack_uint16_tlv_value}, - - { NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.dl_channel_bandwidth, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.ul_channel_bandwidth, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &pNfapiMsg->rf_config.reference_signal_power, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.tx_antenna_ports, &unpack_uint16_tlv_value}, - { NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.rx_antenna_ports, &unpack_uint16_tlv_value}, - - { NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &pNfapiMsg->phich_config.phich_resource, &unpack_uint16_tlv_value}, - { NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &pNfapiMsg->phich_config.phich_duration, &unpack_uint16_tlv_value}, - { NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &pNfapiMsg->phich_config.phich_power_offset, &unpack_uint16_tlv_value}, - - { NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, - { NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, - { NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &pNfapiMsg->sch_config.physical_cell_id, &unpack_uint16_tlv_value}, - - { NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &pNfapiMsg->prach_config.configuration_index, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &pNfapiMsg->prach_config.root_sequence_index, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &pNfapiMsg->prach_config.zero_correlation_zone_configuration, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &pNfapiMsg->prach_config.high_speed_flag, &unpack_uint16_tlv_value}, - { NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &pNfapiMsg->prach_config.frequency_offset, &unpack_uint16_tlv_value}, - - { NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &pNfapiMsg->pusch_config.hopping_mode, &unpack_uint16_tlv_value}, - { NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &pNfapiMsg->pusch_config.hopping_offset, &unpack_uint16_tlv_value}, - { NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &pNfapiMsg->pusch_config.number_of_subbands, &unpack_uint16_tlv_value}, - - { NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &pNfapiMsg->pucch_config.delta_pucch_shift, &unpack_uint16_tlv_value}, - { NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &pNfapiMsg->pucch_config.n_cqi_rb, &unpack_uint16_tlv_value}, - { NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &pNfapiMsg->pucch_config.n_an_cs, &unpack_uint16_tlv_value}, - { NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &pNfapiMsg->pucch_config.n1_pucch_an, &unpack_uint16_tlv_value}, - - { NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &pNfapiMsg->srs_config.bandwidth_configuration, &unpack_uint16_tlv_value}, - { NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &pNfapiMsg->srs_config.max_up_pts, &unpack_uint16_tlv_value}, - { NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &pNfapiMsg->srs_config.srs_subframe_configuration, &unpack_uint16_tlv_value}, - { NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission, &unpack_uint16_tlv_value}, - - { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping, &unpack_uint16_tlv_value}, - { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &pNfapiMsg->uplink_reference_signal_config.group_assignment, &unpack_uint16_tlv_value}, - { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms, &unpack_uint16_tlv_value}, - - - { NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_PDSCH_TAG, &pNfapiMsg->laa_config.ed_threshold_lbt_pdsch, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_DRS_TAG, &pNfapiMsg->laa_config.ed_threshold_lbt_drs, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CONFIG_PD_THRESHOLD_TAG, &pNfapiMsg->laa_config.pd_threshold, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CONFIG_MULTI_CARRIER_TYPE_TAG, &pNfapiMsg->laa_config.multi_carrier_type, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CONFIG_MULTI_CARRIER_TX_TAG, &pNfapiMsg->laa_config.multi_carrier_tx, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CONFIG_MULTI_CARRIER_FREEZE_TAG, &pNfapiMsg->laa_config.multi_carrier_freeze, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CONFIG_TX_ANTENNA_PORTS_FOR_DRS_TAG, &pNfapiMsg->laa_config.tx_antenna_ports_drs, &unpack_uint16_tlv_value}, - { NFAPI_LAA_CONFIG_TRANSMISSION_POWER_FOR_DRS_TAG, &pNfapiMsg->laa_config.tx_power_drs, &unpack_uint16_tlv_value}, - - { NFAPI_EMTC_CONFIG_PBCH_REPETITIONS_ENABLE_R13_TAG, &pNfapiMsg->emtc_config.pbch_repetitions_enable_r13, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CATM_ROOT_SEQUENCE_INDEX_TAG, &pNfapiMsg->emtc_config.prach_catm_root_sequence_index, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CATM_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &pNfapiMsg->emtc_config.prach_catm_zero_correlation_zone_configuration, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CATM_HIGH_SPEED_FLAG, &pNfapiMsg->emtc_config.prach_catm_high_speed_flag, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_configuration_index, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_frequency_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_starting_subframe_periodicity, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_hopping_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_hopping_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_configuration_index, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_frequency_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_starting_subframe_periodicity, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_hopping_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_hopping_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_configuration_index, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_frequency_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_starting_subframe_periodicity, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_hopping_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_hopping_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_configuration_index, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_frequency_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_starting_subframe_periodicity, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_hopping_enable, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_hopping_offset, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEA_TAG, &pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodea, &unpack_uint16_tlv_value}, - { NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEB_TAG, &pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodeb, &unpack_uint16_tlv_value}, - - { NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &pNfapiMsg->tdd_frame_structure_config.subframe_assignment, &unpack_uint16_tlv_value}, - { NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns, &unpack_uint16_tlv_value}, - - { NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &pNfapiMsg->l23_config.data_report_mode, &unpack_uint16_tlv_value}, - { NFAPI_L23_CONFIG_SFNSF_TAG, &pNfapiMsg->l23_config.sfnsf, &unpack_uint16_tlv_value}, - - { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv4, &unpack_ipv4_address_value}, - { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv6, &unpack_ipv6_address_value}, - { NFAPI_NFAPI_P7_VNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_vnf_port, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv4, &unpack_ipv4_address_value}, - { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv6, &unpack_ipv6_address_value}, - { NFAPI_NFAPI_P7_PNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_pnf_port, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.dl_ue_per_sf, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.ul_ue_per_sf, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_RF_BANDS_TAG, &pNfapiMsg->nfapi_config.rf_bands, &unpack_rf_bands_value}, - { NFAPI_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &pNfapiMsg->nfapi_config.timing_info_mode, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &pNfapiMsg->nfapi_config.timing_info_period, &unpack_uint8_tlv_value}, - { NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &pNfapiMsg->nfapi_config.max_transmit_power, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_EARFCN_TAG, &pNfapiMsg->nfapi_config.earfcn, &unpack_uint16_tlv_value}, - { NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands, &unpack_nmm_frequency_bands_value}, - { NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_umts_frequency_bands, &unpack_nmm_frequency_bands_value}, - { NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_lte_frequency_bands, &unpack_nmm_frequency_bands_value}, - { NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported, &unpack_uint8_tlv_value}, - - }; - return ( pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} -static uint8_t unpack_nr_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_config_request_scf_t *pNfapiMsg = (nfapi_nr_config_request_scf_t *)msg; - pNfapiMsg->tdd_table.max_tdd_periodicity_list = (nfapi_nr_max_tdd_periodicity_t *) malloc(20*sizeof(nfapi_nr_max_tdd_periodicity_t)); - - for(int i=0; i<40; i++) - pNfapiMsg->tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list = (nfapi_nr_max_num_of_symbol_per_slot_t *) malloc(14*sizeof(nfapi_nr_max_num_of_symbol_per_slot_t)); - - pNfapiMsg->prach_config.num_prach_fd_occasions_list=(nfapi_nr_num_prach_fd_occasions_t *) malloc(sizeof(nfapi_nr_num_prach_fd_occasions_t)); - unpack_tlv_t unpack_fns[] = { - { NFAPI_NR_CONFIG_DL_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.dl_bandwidth), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_DL_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.dl_frequency), &unpack_uint32_tlv_value}, - { NFAPI_NR_CONFIG_DL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.dl_grid_size[1]), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_DL_K0_TAG, &(pNfapiMsg->carrier_config.dl_k0[1]), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_NUM_RX_ANT_TAG, &(pNfapiMsg->carrier_config.num_rx_ant), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_NUM_TX_ANT_TAG, &(pNfapiMsg->carrier_config.num_tx_ant), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_UL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.ul_grid_size[1]), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_UL_K0_TAG, &(pNfapiMsg->carrier_config.ul_k0[1]), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_UPLINK_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.uplink_bandwidth), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_UPLINK_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.uplink_frequency), &unpack_uint32_tlv_value}, - { NFAPI_NR_CONFIG_FRAME_DUPLEX_TYPE_TAG, &(pNfapiMsg->cell_config.frame_duplex_type), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_PHY_CELL_ID_TAG, &(pNfapiMsg->cell_config.phy_cell_id), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_PRACH_SEQUENCE_LENGTH_TAG, &(pNfapiMsg->prach_config.prach_sequence_length), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG, &(pNfapiMsg->prach_config.restricted_set_config), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_SSB_PER_RACH_TAG, &(pNfapiMsg->prach_config.ssb_per_rach), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_PRACH_SUB_C_SPACING_TAG, &(pNfapiMsg->prach_config.prach_sub_c_spacing), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_PRACH_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_root_sequence_index), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_K1_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].k1), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_PRACH_ZERO_CORR_CONF_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_zero_corr_conf), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_NUM_ROOT_SEQUENCES_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].num_root_sequences), &unpack_uint8_tlv_value}, - - { NFAPI_NR_CONFIG_SCS_COMMON_TAG, &(pNfapiMsg->ssb_config.scs_common), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_SS_PBCH_POWER_TAG, &(pNfapiMsg->ssb_config.ss_pbch_power), &unpack_uint32_tlv_value}, - { NFAPI_NR_CONFIG_BETA_PSS_TAG, &(pNfapiMsg->ssb_table.beta_pss), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_MIB_TAG, &(pNfapiMsg->ssb_table.MIB), &unpack_uint32_tlv_value}, - { NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[0].ssb_mask), &unpack_uint32_tlv_value}, - { NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[1].ssb_mask), &unpack_uint32_tlv_value}, - - { NFAPI_NR_CONFIG_SSB_OFFSET_POINT_A_TAG, &(pNfapiMsg->ssb_table.ssb_offset_point_a), &unpack_uint16_tlv_value}, - { NFAPI_NR_CONFIG_SSB_PERIOD_TAG, &(pNfapiMsg->ssb_table.ssb_period), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_SSB_SUBCARRIER_OFFSET_TAG, &(pNfapiMsg->ssb_table.ssb_subcarrier_offset), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_TDD_PERIOD_TAG, &(pNfapiMsg->tdd_table.tdd_period), &unpack_uint8_tlv_value}, - { NFAPI_NR_CONFIG_SLOT_CONFIG_TAG, &(pNfapiMsg->tdd_table.max_tdd_periodicity_list[0].max_num_of_symbol_per_slot_list[0].slot_config), &unpack_uint8_tlv_value}, - { NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), &unpack_ipv4_address_value}, - { NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), &unpack_ipv6_address_value}, - { NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), &unpack_uint16_tlv_value}, - { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), &unpack_ipv4_address_value}, - { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), &unpack_ipv6_address_value}, - { NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), &unpack_uint16_tlv_value}, - { NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), &unpack_uint8_tlv_value}, - { NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), &unpack_uint8_tlv_value}, - { NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), &unpack_uint8_tlv_value}, - }; - return ( pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_config_response_t *pNfapiMsg = (nfapi_config_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_nr_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_config_response_scf_t *pNfapiMsg = (nfapi_nr_config_response_scf_t *)msg; - return ( pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_nr_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_start_request_scf_t *pNfapiMsg = ( nfapi_nr_start_request_scf_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_start_request_t *pNfapiMsg = ( nfapi_start_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_start_response_t *pNfapiMsg = (nfapi_start_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_nr_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_nr_start_response_scf_t *pNfapiMsg = (nfapi_nr_start_response_scf_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} - -static uint8_t unpack_stop_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_stop_request_t *pNfapiMsg = (nfapi_stop_request_t *)msg; - return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_stop_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_stop_response_t *pNfapiMsg = (nfapi_stop_response_t *)msg; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); -} -static uint8_t unpack_measurement_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_measurement_request_t *pNfapiMsg = (nfapi_measurement_request_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_MEASUREMENT_REQUEST_DL_RS_XTX_POWER_TAG, &pNfapiMsg->dl_rs_tx_power, &unpack_uint16_tlv_value}, - { NFAPI_MEASUREMENT_REQUEST_RECEIVED_INTERFERENCE_POWER_TAG, &pNfapiMsg->received_interference_power, &unpack_uint16_tlv_value}, - { NFAPI_MEASUREMENT_REQUEST_THERMAL_NOISE_POWER_TAG, &pNfapiMsg->thermal_noise_power, &unpack_uint16_tlv_value}, - }; - return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_received_interference_power_measurement_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_received_interference_power_measurement_t *value = (nfapi_received_interference_power_measurement_t *)tlv; - return ( pull16(ppReadPackedMsg, &value->number_of_resource_blocks, end) && - pullarrays16(ppReadPackedMsg, value->received_interference_power, NFAPI_MAX_RECEIVED_INTERFERENCE_POWER_RESULTS, value->number_of_resource_blocks, end)); -} - - -static uint8_t unpack_measurement_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { - nfapi_measurement_response_t *pNfapiMsg = (nfapi_measurement_response_t *)msg; - unpack_tlv_t unpack_fns[] = { - { NFAPI_MEASUREMENT_RESPONSE_DL_RS_POWER_MEASUREMENT_TAG, &pNfapiMsg->dl_rs_tx_power_measurement, &unpack_int16_tlv_value}, - { NFAPI_MEASUREMENT_RESPONSE_RECEIVED_INTERFERENCE_POWER_MEASUREMENT_TAG, &pNfapiMsg->received_interference_power_measurement, &unpack_received_interference_power_measurement_value}, - { NFAPI_MEASUREMENT_RESPONSE_THERMAL_NOISE_MEASUREMENT_TAG, &pNfapiMsg->thermal_noise_power_measurement, &unpack_int16_tlv_value}, - }; - return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -// unpack length check - -static int check_nr_unpack_length(nfapi_nr_phy_msg_type_e msgId, uint32_t unpackedBufLen) { - int retLen = 0; - - switch (msgId) { - case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_pnf_param_request_t)) - retLen = sizeof(nfapi_pnf_param_request_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_nr_pnf_param_response_t)) - retLen = sizeof(nfapi_nr_pnf_param_response_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_pnf_config_request_t)) - retLen = sizeof(nfapi_nr_pnf_config_request_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_nr_pnf_config_response_t)) - retLen = sizeof(nfapi_nr_pnf_config_response_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_START_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_pnf_start_request_t)) - retLen = sizeof(nfapi_nr_pnf_start_request_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_START_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_nr_pnf_start_response_t)) - retLen = sizeof(nfapi_nr_pnf_start_response_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_pnf_stop_request_t)) - retLen = sizeof(nfapi_nr_pnf_stop_request_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_nr_pnf_stop_response_t)) - retLen = sizeof(nfapi_nr_pnf_stop_response_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PARAM_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_param_request_scf_t)) - retLen = sizeof(nfapi_nr_param_request_scf_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_nr_param_response_scf_t)) - retLen = sizeof(nfapi_nr_param_response_scf_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_config_request_scf_t)) - retLen = sizeof(nfapi_nr_config_request_scf_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_nr_config_response_scf_t)) - retLen = sizeof(nfapi_nr_config_response_scf_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_START_REQUEST: - if (unpackedBufLen >= sizeof( nfapi_nr_start_request_scf_t)) - retLen = sizeof( nfapi_nr_start_request_scf_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_nr_start_response_scf_t)) - retLen = sizeof(nfapi_nr_start_response_scf_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_stop_request_t)) - retLen = sizeof(nfapi_stop_request_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_stop_response_t)) - retLen = sizeof(nfapi_stop_response_t); - - break; - - default: - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s Unknown message ID %d\n", __FUNCTION__, msgId); - break; - } - - return retLen; -} - - -static int check_unpack_length(nfapi_message_id_e msgId, uint32_t unpackedBufLen) { - int retLen = 0; - - switch (msgId) { - case NFAPI_PNF_PARAM_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_pnf_param_request_t)) - retLen = sizeof(nfapi_pnf_param_request_t); - - break; - - case NFAPI_PNF_PARAM_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_pnf_param_response_t)) - retLen = sizeof(nfapi_pnf_param_response_t); - - break; - - case NFAPI_PNF_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_pnf_config_request_t)) - retLen = sizeof(nfapi_pnf_config_request_t); - - break; - - case NFAPI_PNF_CONFIG_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_pnf_config_response_t)) - retLen = sizeof(nfapi_pnf_config_response_t); - - break; - - case NFAPI_PNF_START_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_pnf_start_request_t)) - retLen = sizeof(nfapi_pnf_start_request_t); - - break; - - case NFAPI_PNF_START_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_pnf_start_response_t)) - retLen = sizeof(nfapi_pnf_start_response_t); - - break; - - case NFAPI_PNF_STOP_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_pnf_stop_request_t)) - retLen = sizeof(nfapi_pnf_stop_request_t); - - break; - - case NFAPI_PNF_STOP_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_pnf_stop_response_t)) - retLen = sizeof(nfapi_pnf_stop_response_t); - - break; - - case NFAPI_PARAM_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_param_request_t)) - retLen = sizeof(nfapi_param_request_t); - - break; - - case NFAPI_PARAM_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_param_response_t)) - retLen = sizeof(nfapi_param_response_t); - - break; - - case NFAPI_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_config_request_t)) - retLen = sizeof(nfapi_config_request_t); - - break; - - case NFAPI_CONFIG_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_config_response_t)) - retLen = sizeof(nfapi_config_response_t); - - break; - - case NFAPI_START_REQUEST: - if (unpackedBufLen >= sizeof( nfapi_start_request_t)) - retLen = sizeof( nfapi_start_request_t); - - break; - - case NFAPI_START_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_start_response_t)) - retLen = sizeof(nfapi_start_response_t); - - break; - - case NFAPI_STOP_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_stop_request_t)) - retLen = sizeof(nfapi_stop_request_t); - - break; - - case NFAPI_STOP_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_stop_response_t)) - retLen = sizeof(nfapi_stop_response_t); - - break; - - case NFAPI_MEASUREMENT_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_measurement_request_t)) - retLen = sizeof(nfapi_measurement_request_t); - - break; - - case NFAPI_MEASUREMENT_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_measurement_response_t)) - retLen = sizeof(nfapi_measurement_response_t); - - break; - - default: - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s Unknown message ID %d\n", __FUNCTION__, msgId); - break; - } - - return retLen; -} - - -// Main unpack functions - public - -int nfapi_p5_message_header_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p4_p5_codec_config_t *config) { - nfapi_p4_p5_message_header_t *pMessageHeader = pUnpackedBuf; - uint8_t *pReadPackedMessage = pMessageBuf; - uint8_t *end = pMessageBuf + messageBufLen; - - if (pMessageBuf == NULL || pUnpackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 header unpack supplied pointers are null\n"); - return -1; - } - - if (messageBufLen < NFAPI_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p4_p5_message_header_t)) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 header unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); - return -1; - } - - // process the header - return ( pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && - pull16(&pReadPackedMessage, &pMessageHeader->spare, end) ); -} - -int nfapi_nr_p5_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p4_p5_codec_config_t *config) { - nfapi_p4_p5_message_header_t *pMessageHeader = pUnpackedBuf; - uint8_t *pReadPackedMessage = pMessageBuf; - uint8_t *end = pMessageBuf + messageBufLen; - - if (pMessageBuf == NULL || pUnpackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied pointers are null\n"); - return -1; - } - - if (messageBufLen < NFAPI_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p4_p5_message_header_t)) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); - return -1; - } - - uint8_t *ptr = pReadPackedMessage; - printf("\n Read message unpack: "); - - while(ptr < end) { - printf(" %d ", *ptr); - ptr++; - } - - printf("\n"); - // clean the supplied buffer for - tag value blanking - (void)memset(pUnpackedBuf, 0, unpackedBufLen); - - // process the header - if( !(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end ) && - pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && - pull16(&pReadPackedMessage, &pMessageHeader->spare, end))) { - // failed to read the header - return -1; - } - - int result = -1; - - if(check_nr_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) { - // the unpack buffer is not big enough for the struct - return -1; - } - - // look for the specific message - switch (pMessageHeader->message_id) { - case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_REQUEST: - result = unpack_nr_pnf_param_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_RESPONSE: - result = unpack_nr_pnf_param_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_REQUEST: - result = unpack_nr_pnf_config_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_RESPONSE: - result = unpack_nr_pnf_config_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_START_REQUEST: - result = unpack_nr_pnf_start_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_START_RESPONSE: - result = unpack_nr_pnf_start_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_REQUEST: - result = unpack_pnf_stop_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_RESPONSE: - result = unpack_pnf_stop_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PARAM_REQUEST: - result = unpack_nr_param_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE: - result = unpack_nr_param_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_CONFIG_REQUEST: - result = unpack_nr_config_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE: - result = unpack_nr_config_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_START_REQUEST: - result = unpack_nr_start_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE: - result = unpack_nr_start_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST: - result = unpack_stop_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: - result = unpack_stop_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_MEASUREMENT_REQUEST: - result = unpack_measurement_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_MEASUREMENT_RESPONSE: - result = unpack_measurement_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - default: - if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->unpack_p4_p5_vendor_extension) { - result = (config->unpack_p4_p5_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown P5 message ID %d\n", __FUNCTION__, pMessageHeader->message_id); - } - - break; - } - - return result; -} - -int nfapi_p5_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p4_p5_codec_config_t *config) { - nfapi_p4_p5_message_header_t *pMessageHeader = pUnpackedBuf; - uint8_t *pReadPackedMessage = pMessageBuf; - uint8_t *end = pMessageBuf + messageBufLen; - - if (pMessageBuf == NULL || pUnpackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied pointers are null\n"); - return -1; - } - - if (messageBufLen < NFAPI_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p4_p5_message_header_t)) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); - return -1; - } - - uint8_t *ptr = pReadPackedMessage; - printf("\n Read message unpack: "); - - while(ptr < end) { - printf(" %d ", *ptr); - ptr++; - } - - printf("\n"); - // clean the supplied buffer for - tag value blanking - (void)memset(pUnpackedBuf, 0, unpackedBufLen); - - // process the header - if( !(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end ) && - pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && - pull16(&pReadPackedMessage, &pMessageHeader->spare, end))) { - // failed to read the header - return -1; - } - - int result = -1; - - if(check_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) { - // the unpack buffer is not big enough for the struct - return -1; - } - - // look for the specific message - switch (pMessageHeader->message_id) { - case NFAPI_PNF_PARAM_REQUEST: - result = unpack_pnf_param_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PNF_PARAM_RESPONSE: - result = unpack_pnf_param_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PNF_CONFIG_REQUEST: - result = unpack_pnf_config_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PNF_CONFIG_RESPONSE: - result = unpack_pnf_config_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PNF_START_REQUEST: - result = unpack_pnf_start_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PNF_START_RESPONSE: - result = unpack_pnf_start_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PNF_STOP_REQUEST: - result = unpack_pnf_stop_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PNF_STOP_RESPONSE: - result = unpack_pnf_stop_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PARAM_REQUEST: - result = unpack_param_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_PARAM_RESPONSE: - result = unpack_param_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_CONFIG_REQUEST: - result = unpack_config_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_CONFIG_RESPONSE: - result = unpack_config_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_START_REQUEST: - result = unpack_start_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_START_RESPONSE: - result = unpack_start_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_STOP_REQUEST: - result = unpack_stop_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_STOP_RESPONSE: - result = unpack_stop_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_MEASUREMENT_REQUEST: - result = unpack_measurement_request(&pReadPackedMessage, end, pMessageHeader, config); - break; - - case NFAPI_MEASUREMENT_RESPONSE: - result = unpack_measurement_response(&pReadPackedMessage, end, pMessageHeader, config); - break; - - default: - if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->unpack_p4_p5_vendor_extension) { - result = (config->unpack_p4_p5_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown P5 message ID %d\n", __FUNCTION__, pMessageHeader->message_id); - } - - break; - } - - return result; -} - +/* + * Copyright 2017 Cisco Systems, Inc. + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "nfapi_nr_interface.h" +#include "nfapi_nr_interface_scf.h" +#include + + +// Pack routines +//TODO: Add pacl/unpack fns for uint32 and uint64 +static uint8_t pack_nr_pnf_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_param_request_t *request = (nfapi_nr_pnf_param_request_t *)msg; + return pack_vendor_extension_tlv(request->vendor_extension, ppWritePackedMsg, end, config); +} + +static uint8_t pack_pnf_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_param_request_t *request = (nfapi_pnf_param_request_t *)msg; + return pack_vendor_extension_tlv(request->vendor_extension, ppWritePackedMsg, end, config); +} + +static uint8_t pack_pnf_param_general_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_param_general_t *value = (nfapi_pnf_param_general_t *)tlv; + return ( push8(value->nfapi_sync_mode, ppWritePackedMsg, end) && + push8(value->location_mode, ppWritePackedMsg, end) && + push16(value->location_coordinates_length, ppWritePackedMsg, end) && + pusharray8(value->location_coordinates, NFAPI_PNF_PARAM_GENERAL_LOCATION_LENGTH, value->location_coordinates_length, ppWritePackedMsg, end) && + push32(value->dl_config_timing, ppWritePackedMsg, end) && + push32(value->tx_timing, ppWritePackedMsg, end) && + push32(value->ul_config_timing, ppWritePackedMsg, end) && + push32(value->hi_dci0_timing, ppWritePackedMsg, end) && + push16(value->maximum_number_phys, ppWritePackedMsg, end) && + push16(value->maximum_total_bandwidth, ppWritePackedMsg, end) && + push8(value->maximum_total_number_dl_layers, ppWritePackedMsg, end) && + push8(value->maximum_total_number_ul_layers, ppWritePackedMsg, end) && + push8(value->shared_bands, ppWritePackedMsg, end) && + push8(value->shared_pa, ppWritePackedMsg, end) && + pushs16(value->maximum_total_power, ppWritePackedMsg, end) && + pusharray8(value->oui, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, ppWritePackedMsg, end)); +} + +static uint8_t pack_rf_config_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_rf_config_info_t *rf = (nfapi_rf_config_info_t *)elem; + return (push16(rf->rf_config_index, ppWritePackedMsg, end)); +} + + +static uint8_t pack_pnf_phy_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_info_t *phy = (nfapi_pnf_phy_info_t *)elem; + return ( push16(phy->phy_config_index, ppWritePackedMsg, end) && + push16(phy->number_of_rfs, ppWritePackedMsg, end) && + packarray(phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, ppWritePackedMsg, end, &pack_rf_config_info) && + push16(phy->number_of_rf_exclusions, ppWritePackedMsg, end) && + packarray(phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, ppWritePackedMsg, end, &pack_rf_config_info) && + push16(phy->downlink_channel_bandwidth_supported, ppWritePackedMsg, end) && + push16(phy->uplink_channel_bandwidth_supported, ppWritePackedMsg, end) && + push8(phy->number_of_dl_layers_supported, ppWritePackedMsg, end) && + push8(phy->number_of_ul_layers_supported, ppWritePackedMsg, end) && + push16(phy->maximum_3gpp_release_supported, ppWritePackedMsg, end) && + push8(phy->nmm_modes_supported, ppWritePackedMsg, end)); +} + +static uint8_t pack_pnf_phy_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_t *value = (nfapi_pnf_phy_t *)tlv; + return ( push16(value->number_of_phys, ppWritePackedMsg, end) && + packarray(value->phy, sizeof(nfapi_pnf_phy_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_info)); +} + +static uint8_t pack_pnf_rf_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_rf_t *value = (nfapi_pnf_rf_t *)tlv; + uint16_t rf_index = 0; + + if(push16(value->number_of_rfs, ppWritePackedMsg, end) == 0) + return 0; + + for(; rf_index < value->number_of_rfs; ++rf_index) { + if( !(push16(value->rf[rf_index].rf_config_index, ppWritePackedMsg, end) && + push16(value->rf[rf_index].band, ppWritePackedMsg, end) && + pushs16(value->rf[rf_index].maximum_transmit_power, ppWritePackedMsg, end) && + pushs16(value->rf[rf_index].minimum_transmit_power, ppWritePackedMsg, end) && + push8(value->rf[rf_index].number_of_antennas_suppported, ppWritePackedMsg, end) && + push32(value->rf[rf_index].minimum_downlink_frequency, ppWritePackedMsg, end) && + push32(value->rf[rf_index].maximum_downlink_frequency, ppWritePackedMsg, end) && + push32(value->rf[rf_index].minimum_uplink_frequency, ppWritePackedMsg, end) && + push32(value->rf[rf_index].maximum_uplink_frequency, ppWritePackedMsg, end))) + return 0; + } + + return 1; +} +static uint8_t pack_pnf_phy_rel10_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel10_info_t *phy = (nfapi_pnf_phy_rel10_info_t *)elem; + return(push16(phy->phy_config_index, ppWritePackedMsg, end) && + push16(phy->transmission_mode_7_supported, ppWritePackedMsg, end) && + push16(phy->transmission_mode_8_supported, ppWritePackedMsg, end) && + push16(phy->two_antenna_ports_for_pucch, ppWritePackedMsg, end) && + push16(phy->transmission_mode_9_supported, ppWritePackedMsg, end) && + push16(phy->simultaneous_pucch_pusch, ppWritePackedMsg, end) && + push16(phy->four_layer_tx_with_tm3_and_tm4, ppWritePackedMsg, end)); +} + +static uint8_t pack_pnf_phy_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel10_t *value = (nfapi_pnf_phy_rel10_t *)tlv; + return (push16(value->number_of_phys, ppWritePackedMsg, end) && + packarray(value->phy, sizeof(nfapi_pnf_phy_rel10_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel10_info)); +} + +static uint8_t pack_pnf_phy_rel11_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel11_info_t *phy = (nfapi_pnf_phy_rel11_info_t *)elem; + return (push16(phy->phy_config_index, ppWritePackedMsg, end) && + push16(phy->edpcch_supported, ppWritePackedMsg, end) && + push16(phy->multi_ack_csi_reporting, ppWritePackedMsg, end) && + push16(phy->pucch_tx_diversity, ppWritePackedMsg, end) && + push16(phy->ul_comp_supported, ppWritePackedMsg, end) && + push16(phy->transmission_mode_5_supported, ppWritePackedMsg, end )); +} +static uint8_t pack_pnf_phy_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel11_t *value = (nfapi_pnf_phy_rel11_t *)tlv; + return (push16(value->number_of_phys, ppWritePackedMsg, end) && + packarray(value->phy, sizeof(nfapi_pnf_phy_rel11_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel11_info)); +} +static uint8_t pack_pnf_phy_rel12_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel12_info_t *phy = (nfapi_pnf_phy_rel12_info_t *)elem; + return( push16(phy->phy_config_index, ppWritePackedMsg, end) && + push16(phy->csi_subframe_set, ppWritePackedMsg, end) && + push16(phy->enhanced_4tx_codebook, ppWritePackedMsg, end) && + push16(phy->drs_supported, ppWritePackedMsg, end) && + push16(phy->ul_64qam_supported, ppWritePackedMsg, end) && + push16(phy->transmission_mode_10_supported, ppWritePackedMsg, end) && + push16(phy->alternative_bts_indices, ppWritePackedMsg, end)); +} +static uint8_t pack_pnf_phy_rel12_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel12_t *value = (nfapi_pnf_phy_rel12_t *)tlv; + return (push16(value->number_of_phys, ppWritePackedMsg, end) && + packarray(value->phy, sizeof(nfapi_pnf_phy_rel12_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel12_info)); +} + +static uint8_t pack_pnf_phy_rel13_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_info_t *phy = (nfapi_pnf_phy_rel13_info_t *)elem; + return( push16(phy->phy_config_index, ppWritePackedMsg, end) && + push16(phy->pucch_format4_supported, ppWritePackedMsg, end) && + push16(phy->pucch_format5_supported, ppWritePackedMsg, end) && + push16(phy->more_than_5_ca_support, ppWritePackedMsg, end) && + push16(phy->laa_supported, ppWritePackedMsg, end) && + push16(phy->laa_ending_in_dwpts_supported, ppWritePackedMsg, end) && + push16(phy->laa_starting_in_second_slot_supported, ppWritePackedMsg, end) && + push16(phy->beamforming_supported, ppWritePackedMsg, end) && + push16(phy->csi_rs_enhancement_supported, ppWritePackedMsg, end) && + push16(phy->drms_enhancement_supported, ppWritePackedMsg, end) && + push16(phy->srs_enhancement_supported, ppWritePackedMsg, end) ); +} + +static uint8_t pack_pnf_phy_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_t *value = (nfapi_pnf_phy_rel13_t *)tlv; + return (push16(value->number_of_phys, ppWritePackedMsg, end) && + packarray(value->phy, sizeof(nfapi_pnf_phy_rel13_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel13_info)); +} + +static uint8_t pack_pnf_phy_rel13_nb_iot_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_nb_iot_info_t *phy = (nfapi_pnf_phy_rel13_nb_iot_info_t *)elem; + return( push16(phy->phy_config_index, ppWritePackedMsg, end) && + push16(phy->number_of_rfs, ppWritePackedMsg, end) && + packarray(phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, ppWritePackedMsg, end, &pack_rf_config_info) && + push16(phy->number_of_rf_exclusions, ppWritePackedMsg, end) && + packarray(phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, ppWritePackedMsg, end, &pack_rf_config_info) && + push8(phy->number_of_dl_layers_supported, ppWritePackedMsg, end) && + push8(phy->number_of_ul_layers_supported, ppWritePackedMsg, end) && + push16(phy->maximum_3gpp_release_supported, ppWritePackedMsg, end) && + push8(phy->nmm_modes_supported, ppWritePackedMsg, end)); +} + +static uint8_t pack_pnf_phy_rel13_nb_iot_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_nb_iot_t *value = (nfapi_pnf_phy_rel13_nb_iot_t *)tlv; + return (push16(value->number_of_phys, ppWritePackedMsg, end) && + packarray(value->phy, sizeof(nfapi_pnf_phy_rel13_nb_iot_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, ppWritePackedMsg, end, &pack_pnf_phy_rel13_nb_iot_info)); +} +/* +static uint8_t pack_nr_pnf_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t* config) +{ + nfapi_nr_pnf_param_response_t *pNfapiMsg = (nfapi_nr_pnf_param_response_t*)msg; + + return (push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_tlv(NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, ppWritePackedMsg, end, &pack_pnf_param_general_value) && + pack_tlv(NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, ppWritePackedMsg, end, &pack_pnf_phy_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} +*/ +static uint8_t pack_nr_pnf_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_param_response_t *pNfapiMsg = (nfapi_nr_pnf_param_response_t *)msg; + return (push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_tlv(NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, ppWritePackedMsg, end, &pack_pnf_param_general_value) && + pack_tlv(NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, ppWritePackedMsg, end, &pack_pnf_phy_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_pnf_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_param_response_t *pNfapiMsg = (nfapi_pnf_param_response_t *)msg; + return (push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_tlv(NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, ppWritePackedMsg, end, &pack_pnf_param_general_value) && + pack_tlv(NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, ppWritePackedMsg, end, &pack_pnf_phy_value) && + pack_tlv(NFAPI_PNF_RF_TAG, &pNfapiMsg->pnf_rf, ppWritePackedMsg, end, &pack_pnf_rf_value) && + pack_tlv(NFAPI_PNF_PHY_REL10_TAG, &pNfapiMsg->pnf_phy_rel10, ppWritePackedMsg, end, &pack_pnf_phy_rel10_value) && + pack_tlv(NFAPI_PNF_PHY_REL11_TAG, &pNfapiMsg->pnf_phy_rel11, ppWritePackedMsg, end, &pack_pnf_phy_rel11_value) && + pack_tlv(NFAPI_PNF_PHY_REL12_TAG, &pNfapiMsg->pnf_phy_rel12, ppWritePackedMsg, end, &pack_pnf_phy_rel12_value) && + pack_tlv(NFAPI_PNF_PHY_REL13_TAG, &pNfapiMsg->pnf_phy_rel13, ppWritePackedMsg, end, &pack_pnf_phy_rel13_value) && + pack_tlv(NFAPI_PNF_PHY_REL13_NB_IOT_TAG, &pNfapiMsg->pnf_phy_rel13_nb_iot, ppWritePackedMsg, end, &pack_pnf_phy_rel13_nb_iot_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_phy_rf_config_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_phy_rf_config_info_t *rf = (nfapi_phy_rf_config_info_t *)elem; + return (push16(rf->phy_id, ppWritePackedMsg, end) && + push16(rf->phy_config_index, ppWritePackedMsg, end) && + push16(rf->rf_config_index, ppWritePackedMsg, end)); +} + + +static uint8_t pack_pnf_phy_rf_config_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_pnf_phy_rf_config_t *value = (nfapi_pnf_phy_rf_config_t *)tlv; + return(push16(value->number_phy_rf_config_info, ppWritePackedMsg, end) && + packarray(value->phy_rf_config, sizeof(nfapi_phy_rf_config_info_t), NFAPI_MAX_PHY_RF_INSTANCES, value->number_phy_rf_config_info, ppWritePackedMsg, end, &pack_phy_rf_config_info)); +} + +static uint8_t pack_nr_pnf_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_config_request_t *pNfapiMsg = (nfapi_nr_pnf_config_request_t *)msg; + return (pack_tlv(NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, ppWritePackedMsg, end, &pack_pnf_phy_rf_config_value) && + //push8(pNfapiMsg->num_tlvs,ppWritePackedMsg,end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_pnf_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_config_request_t *pNfapiMsg = (nfapi_pnf_config_request_t *)msg; + return (pack_tlv(NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, ppWritePackedMsg, end, &pack_pnf_phy_rf_config_value) && + push8(pNfapiMsg->num_tlvs,ppWritePackedMsg,end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_nr_pnf_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_config_response_t *pNfapiMsg = (nfapi_nr_pnf_config_response_t *)msg; + return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_pnf_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_config_response_t *pNfapiMsg = (nfapi_pnf_config_response_t *)msg; + return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nr_pnf_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_start_request_t *pNfapiMsg = (nfapi_nr_pnf_start_request_t *)msg; + return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_pnf_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_start_request_t *pNfapiMsg = (nfapi_pnf_start_request_t *)msg; + return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_nr_pnf_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_start_response_t *pNfapiMsg = (nfapi_nr_pnf_start_response_t *)msg; + return( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_pnf_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_start_response_t *pNfapiMsg = (nfapi_pnf_start_response_t *)msg; + return( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_nr_pnf_stop_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_stop_request_t *pNfapiMsg = (nfapi_nr_pnf_stop_request_t *)msg; + return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + + + +static uint8_t pack_pnf_stop_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_stop_request_t *pNfapiMsg = (nfapi_pnf_stop_request_t *)msg; + return ( pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + + +static uint8_t pack_nr_pnf_stop_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_stop_response_t *pNfapiMsg = (nfapi_nr_pnf_stop_response_t *)msg; + return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + + +static uint8_t pack_pnf_stop_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_stop_response_t *pNfapiMsg = (nfapi_pnf_stop_response_t *)msg; + return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nr_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_param_request_scf_t *pNfapiMsg = (nfapi_nr_param_request_scf_t *)msg; + return (pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_param_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_param_request_t *pNfapiMsg = (nfapi_param_request_t *)msg; + return (pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_uint32_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_uint32_tlv_t *value = (nfapi_uint32_tlv_t *)tlv; + return push32(value->value, ppWritePackedMsg, end); +} + +static uint8_t unpack_uint32_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_uint32_tlv_t *value = (nfapi_uint32_tlv_t *)tlv; + return pull32(ppReadPackedMsg, &value->value, end); +} + + +static uint8_t pack_uint16_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_uint16_tlv_t *value = (nfapi_uint16_tlv_t *)tlv; + return push16(value->value, ppWritePackedMsg, end); +} + +static uint8_t unpack_uint16_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_uint16_tlv_t *value = (nfapi_uint16_tlv_t *)tlv; + return pull16(ppReadPackedMsg, &value->value, end); +} + +static uint8_t pack_int16_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_int16_tlv_t *value = (nfapi_int16_tlv_t *)tlv; + return pushs16(value->value, ppWritePackedMsg, end); +} + +static uint8_t unpack_int16_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_int16_tlv_t *value = (nfapi_int16_tlv_t *)tlv; + return pulls16(ppReadPackedMsg, &value->value, end); +} + +static uint8_t pack_uint8_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_uint8_tlv_t *value = (nfapi_uint8_tlv_t *)tlv; + return push8(value->value, ppWritePackedMsg, end); +} +static uint8_t unpack_uint8_tlv_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_uint8_tlv_t *value = (nfapi_uint8_tlv_t *)tlv; + return pull8(ppReadPackedMsg, &value->value, end); +} + +static uint8_t pack_ipv4_address_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ipv4_address_t *value = (nfapi_ipv4_address_t *)tlv; + return pusharray8(value->address, NFAPI_IPV4_ADDRESS_LENGTH, NFAPI_IPV4_ADDRESS_LENGTH, ppWritePackedMsg, end); +} +static uint8_t unpack_ipv4_address_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ipv4_address_t *value = (nfapi_ipv4_address_t *)tlv; + return pullarray8(ppReadPackedMsg, value->address, NFAPI_IPV4_ADDRESS_LENGTH, NFAPI_IPV4_ADDRESS_LENGTH, end); +} +static uint8_t pack_ipv6_address_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ipv6_address_t *value = (nfapi_ipv6_address_t *)tlv; + return pusharray8(value->address, NFAPI_IPV6_ADDRESS_LENGTH, NFAPI_IPV6_ADDRESS_LENGTH, ppWritePackedMsg, end); +} +static uint8_t unpack_ipv6_address_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ipv4_address_t *value = (nfapi_ipv4_address_t *)tlv; + return pullarray8(ppReadPackedMsg, value->address, NFAPI_IPV6_ADDRESS_LENGTH, NFAPI_IPV6_ADDRESS_LENGTH, end); +} + +static uint8_t pack_rf_bands_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_rf_bands_t *value = (nfapi_rf_bands_t *)tlv; + return ( push16(value->number_rf_bands, ppWritePackedMsg, end) && + pusharray16(value->rf_band, NFAPI_MAX_NUM_RF_BANDS, value->number_rf_bands, ppWritePackedMsg, end)); +} +static uint8_t unpack_rf_bands_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_rf_bands_t *value = (nfapi_rf_bands_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_rf_bands, end) && + pullarray16(ppReadPackedMsg, value->rf_band, NFAPI_MAX_NUM_RF_BANDS, value->number_rf_bands, end)); +} + +static uint8_t pack_nmm_frequency_bands_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nmm_frequency_bands_t *value = (nfapi_nmm_frequency_bands_t *)tlv; + return( push16(value->number_of_rf_bands, ppWritePackedMsg, end) && + pusharray16(value->bands, NFAPI_MAX_NMM_FREQUENCY_BANDS, value->number_of_rf_bands, ppWritePackedMsg, end)); +} +static uint8_t unpack_nmm_frequency_bands_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nmm_frequency_bands_t *value = (nfapi_nmm_frequency_bands_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_rf_bands, end) && + pullarray16(ppReadPackedMsg, value->bands, NFAPI_MAX_NMM_FREQUENCY_BANDS, value->number_of_rf_bands, end)); +} +static uint8_t pack_embms_mbsfn_config_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_embms_mbsfn_config_t *value = (nfapi_embms_mbsfn_config_t *)tlv; + return ( push16(value->num_mbsfn_config, ppWritePackedMsg, end) && + pusharray16(value->radioframe_allocation_period, 8,value->num_mbsfn_config,ppWritePackedMsg, end) && + pusharray16(value->radioframe_allocation_offset, 8,value->num_mbsfn_config,ppWritePackedMsg, end) && + pusharray8(value->fourframes_flag, 8,value->num_mbsfn_config,ppWritePackedMsg, end) && + pusharrays32(value->mbsfn_subframeconfig, 8, value->num_mbsfn_config, ppWritePackedMsg, end)); +} +// static uint8_t unpack_embms_mbsfn_config_value(void* tlv, uint8_t **ppReadPackedMsg, uint8_t* end) +// { +// nfapi_embms_mbsfn_config_t* value = (nfapi_embms_mbsfn_config_t*)tlv; + +// return ( pull16(ppReadPackedMsg, &value->num_mbsfn_config, end) && +// pull16(ppReadPackedMsg, &value->radioframe_allocation_period, end) && +// pull16(ppReadPackedMsg, &value->radioframe_allocation_offset, end) && +// pull8(ppReadPackedMsg, &value->fourframes_flag, end) && +// pullarrays32(ppReadPackedMsg, value->mbsfn_subframeconfig, 8, value->num_mbsfn_config, end)); +// } + +static uint8_t pack_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_param_response_t *pNfapiMsg = (nfapi_param_response_t *)msg; + return( push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && + push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && + pack_tlv(NFAPI_L1_STATUS_PHY_STATE_TAG, &pNfapiMsg->l1_status.phy_state, ppWritePackedMsg, end, &pack_uint16_tlv_value) && + // Do we check the phy state and then just fill those sepecified, however + // we do not know the duplex mode, so just attempt to pack all and assumme + // that the callee has set the right tlvs + pack_tlv(NFAPI_PHY_CAPABILITIES_DL_BANDWIDTH_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.dl_bandwidth_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHY_CAPABILITIES_UL_BANDWIDTH_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.ul_bandwidth_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHY_CAPABILITIES_DL_MODULATION_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.dl_modulation_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHY_CAPABILITIES_UL_MODULATION_SUPPORT_TAG, &(pNfapiMsg->phy_capabilities.ul_modulation_support), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHY_CAPABILITIES_PHY_ANTENNA_CAPABILITY_TAG, &(pNfapiMsg->phy_capabilities.phy_antenna_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHY_CAPABILITIES_RELEASE_CAPABILITY_TAG, &(pNfapiMsg->phy_capabilities.release_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHY_CAPABILITIES_MBSFN_CAPABILITY_TAG, &(pNfapiMsg->phy_capabilities.mbsfn_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + // laa capability + pack_tlv(NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &(pNfapiMsg->subframe_config.duplex_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &(pNfapiMsg->subframe_config.pcfich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value)&& + pack_tlv(NFAPI_SUBFRAME_CONFIG_PB_TAG, &(pNfapiMsg->subframe_config.pb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.dl_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.ul_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.dl_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.ul_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &(pNfapiMsg->rf_config.reference_signal_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.tx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.rx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &(pNfapiMsg->phich_config.phich_resource), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &(pNfapiMsg->phich_config.phich_duration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &(pNfapiMsg->phich_config.phich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &(pNfapiMsg->sch_config.physical_cell_id), ppWritePackedMsg, end,&pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->prach_config.configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &(pNfapiMsg->prach_config.zero_correlation_zone_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &(pNfapiMsg->prach_config.high_speed_flag), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->prach_config.frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &(pNfapiMsg->pusch_config.hopping_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &(pNfapiMsg->pusch_config.hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &(pNfapiMsg->pusch_config.number_of_subbands), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &(pNfapiMsg->pucch_config.delta_pucch_shift), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &(pNfapiMsg->pucch_config.n_cqi_rb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &(pNfapiMsg->pucch_config.n_an_cs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &(pNfapiMsg->pucch_config.n1_pucch_an), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMBMS_MBSFN_CONFIG_AREA_IDX_TAG, &(pNfapiMsg->embms_sib13_config.mbsfn_area_idx), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_EMBMS_MBSFN_CONFIG_AREA_IDR9_TAG, &(pNfapiMsg->embms_sib13_config.mbsfn_area_id_r9), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMBMS_MBSFN_CONFIG_TAG, &(pNfapiMsg->embms_mbsfn_config), ppWritePackedMsg, end, &pack_embms_mbsfn_config_value) && + pack_tlv(NFAPI_FEMBMS_CONFIG_RADIOFRAME_ALLOCATION_PERIOD_TAG, &(pNfapiMsg->fembms_config.radioframe_allocation_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_FEMBMS_CONFIG_RADIOFRAME_ALLOCATION_OFFSET_TAG, &(pNfapiMsg->fembms_config.radioframe_allocation_offset), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_FEMBMS_CONFIG_NON_MBSFN_FLAG_TAG, &(pNfapiMsg->fembms_config.non_mbsfn_config_flag), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_FEMBMS_CONFIG_NON_MBSFN_SUBFRAMECONFIG_TAG, &(pNfapiMsg->fembms_config.non_mbsfn_subframeconfig), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.bandwidth_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &(pNfapiMsg->srs_config.max_up_pts), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.srs_subframe_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &(pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &(pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &(pNfapiMsg->uplink_reference_signal_config.group_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &(pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &(pNfapiMsg->tdd_frame_structure_config.subframe_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &(pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &(pNfapiMsg->l23_config.data_report_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_L23_CONFIG_SFNSF_TAG, &(pNfapiMsg->l23_config.sfnsf), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.dl_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.ul_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_RF_BANDS_TAG, &(pNfapiMsg->nfapi_config.rf_bands), ppWritePackedMsg, end, &pack_rf_bands_value) && + pack_tlv(NFAPI_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &(pNfapiMsg->nfapi_config.max_transmit_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_EARFCN_TAG, &(pNfapiMsg->nfapi_config.earfcn), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && + pack_tlv(NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_umts_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && + pack_tlv(NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_lte_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && + pack_tlv(NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &(pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + +static uint8_t pack_nr_param_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + printf("\nRUNNING pack_param_response\n"); + nfapi_nr_param_response_scf_t *pNfapiMsg = (nfapi_nr_param_response_scf_t *)msg; + return (push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && + push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && + pack_tlv(NFAPI_NR_PARAM_TLV_RELEASE_CAPABILITY_TAG, &(pNfapiMsg->cell_param.release_capability), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PHY_STATE_TAG, &(pNfapiMsg->cell_param.phy_state), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_SKIP_BLANK_DL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_dl_config), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_SKIP_BLANK_UL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_ul_config), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_NUM_CONFIG_TLVS_TO_REPORT_TAG, &(pNfapiMsg->cell_param.num_config_tlvs_to_report ), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_CYCLIC_PREFIX_TAG, &(pNfapiMsg->carrier_param.cyclic_prefix), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_DL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_dl), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_DL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_dl), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_UL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_ul), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_UL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_ul), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_CCE_MAPPING_TYPE_TAG, &(pNfapiMsg->pdcch_param.cce_mapping_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_CORESET_OUTSIDE_FIRST_3_OFDM_SYMS_OF_SLOT_TAG, &(pNfapiMsg->pdcch_param.coreset_outside_first_3_of_ofdm_syms_of_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PRECODER_GRANULARITY_CORESET_TAG, &(pNfapiMsg->pdcch_param.coreset_precoder_granularity_coreset), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDCCH_MU_MIMO_TAG, &(pNfapiMsg->pdcch_param.pdcch_mu_mimo), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDCCH_PRECODER_CYCLING_TAG, &(pNfapiMsg->pdcch_param.pdcch_precoder_cycling), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PDCCHS_PER_SLOT_TAG, &(pNfapiMsg->pdcch_param.max_pdcch_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUCCH_FORMATS_TAG, &(pNfapiMsg->pucch_param.pucch_formats), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PUCCHS_PER_SLOT_TAG, &(pNfapiMsg->pucch_param.max_pucchs_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pdsch_param.pdsch_mapping_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_allocation_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pdsch_param.pdsch_vrb_to_prb_mapping), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_CBG_TAG, &(pNfapiMsg->pdsch_param.pdsch_cbg), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_config_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DMRS_MAX_LENGTH_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_max_length), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_additional_pos), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PDSCH_S_YBS_PER_SLOT_TAG, &(pNfapiMsg->pdsch_param.max_pdsch_tbs_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_PDSCH_TAG, &(pNfapiMsg->pdsch_param.max_number_mimo_layers_pdsch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_DL_TAG, &(pNfapiMsg->pdsch_param.max_mu_mimo_users_dl), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_DATA_IN_DMRS_SYMBOLS_TAG, &(pNfapiMsg->pdsch_param.pdsch_data_in_dmrs_symbols), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PREMPTION_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.premption_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PDSCH_NON_SLOT_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.pdsch_non_slot_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_UCI_MUX_ULSCH_IN_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_mux_ulsch_in_pusch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_UCI_ONLY_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_only_pusch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_FREQUENCY_HOPPING_TAG, &(pNfapiMsg->pusch_param.pusch_frequency_hopping), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_config_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_DMRS_MAX_LEN_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_max_len), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_additional_pos), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_CBG_TAG, &(pNfapiMsg->pusch_param.pusch_cbg), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pusch_param.pusch_mapping_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_allocation_types), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pusch_param.pusch_vrb_to_prb_mapping), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_MAX_PTRS_PORTS_TAG, &(pNfapiMsg->pusch_param.pusch_max_ptrs_ports), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PDUSCHS_TBS_PER_SLOT_TAG, &(pNfapiMsg->pusch_param.max_pduschs_tbs_per_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_NON_CB_PUSCH_TAG, &(pNfapiMsg->pusch_param.max_number_mimo_layers_non_cb_pusch), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_SUPPORTED_MODULATION_ORDER_UL_TAG, &(pNfapiMsg->pusch_param.supported_modulation_order_ul), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_UL_TAG, &(pNfapiMsg->pusch_param.max_mu_mimo_users_ul), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_DFTS_OFDM_SUPPORT_TAG, &(pNfapiMsg->pusch_param.dfts_ofdm_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PUSCH_AGGREGATION_FACTOR_TAG, &(pNfapiMsg->pusch_param.pusch_aggregation_factor), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PRACH_LONG_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_long_formats), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PRACH_SHORT_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_short_formats), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_PRACH_RESTRICTED_SETS_TAG, &(pNfapiMsg->prach_param.prach_restricted_sets), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_MAX_PRACH_FD_OCCASIONS_IN_A_SLOT_TAG, &(pNfapiMsg->prach_param.max_prach_fd_occasions_in_a_slot), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_PARAM_TLV_RSSI_MEASUREMENT_SUPPORT_TAG, &(pNfapiMsg->measurement_param.rssi_measurement_support), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + // config: + pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_config_request_t *pNfapiMsg = (nfapi_config_request_t *)msg; + return ( push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && + // Do we check the phy state and then just fill those sepecified, however + // we do not know the duplex mode, so just attempt to pack all and assumme + // that the callee has set the right tlvs + pack_tlv(NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &(pNfapiMsg->subframe_config.duplex_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &(pNfapiMsg->subframe_config.pcfich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SUBFRAME_CONFIG_PB_TAG, &(pNfapiMsg->subframe_config.pb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.dl_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &(pNfapiMsg->subframe_config.ul_cyclic_prefix_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.dl_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &(pNfapiMsg->rf_config.ul_channel_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &(pNfapiMsg->rf_config.reference_signal_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.tx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &(pNfapiMsg->rf_config.rx_antenna_ports), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &(pNfapiMsg->phich_config.phich_resource), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &(pNfapiMsg->phich_config.phich_duration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &(pNfapiMsg->phich_config.phich_power_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &(pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &(pNfapiMsg->sch_config.physical_cell_id), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->prach_config.configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &(pNfapiMsg->prach_config.zero_correlation_zone_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &(pNfapiMsg->prach_config.high_speed_flag), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->prach_config.frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &(pNfapiMsg->pusch_config.hopping_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &(pNfapiMsg->pusch_config.hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &(pNfapiMsg->pusch_config.number_of_subbands), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &(pNfapiMsg->pucch_config.delta_pucch_shift), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &(pNfapiMsg->pucch_config.n_cqi_rb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &(pNfapiMsg->pucch_config.n_an_cs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &(pNfapiMsg->pucch_config.n1_pucch_an), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.bandwidth_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &(pNfapiMsg->srs_config.max_up_pts), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &(pNfapiMsg->srs_config.srs_subframe_configuration), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &(pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &(pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &(pNfapiMsg->uplink_reference_signal_config.group_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &(pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_PDSCH_TAG, &(pNfapiMsg->laa_config.ed_threshold_lbt_pdsch), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_DRS_TAG, &(pNfapiMsg->laa_config.ed_threshold_lbt_drs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_PD_THRESHOLD_TAG, &(pNfapiMsg->laa_config.pd_threshold), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_MULTI_CARRIER_TYPE_TAG, &(pNfapiMsg->laa_config.multi_carrier_type), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_MULTI_CARRIER_TX_TAG, &(pNfapiMsg->laa_config.multi_carrier_tx), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_MULTI_CARRIER_FREEZE_TAG, &(pNfapiMsg->laa_config.multi_carrier_freeze), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_TX_ANTENNA_PORTS_FOR_DRS_TAG, &(pNfapiMsg->laa_config.tx_antenna_ports_drs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_LAA_CONFIG_TRANSMISSION_POWER_FOR_DRS_TAG, &(pNfapiMsg->laa_config.tx_power_drs), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PBCH_REPETITIONS_ENABLE_R13_TAG, &(pNfapiMsg->emtc_config.pbch_repetitions_enable_r13), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CATM_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_catm_root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CATM_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &(pNfapiMsg->emtc_config.prach_catm_zero_correlation_zone_configuration), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CATM_HIGH_SPEED_FLAG, &(pNfapiMsg->emtc_config.prach_catm_high_speed_flag), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_number_of_repetitions_per_attempt), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_starting_subframe_periodicity), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_0_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_number_of_repetitions_per_attempt), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_starting_subframe_periodicity), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_1_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_number_of_repetitions_per_attempt), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_starting_subframe_periodicity), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_2_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_CONFIGURATION_INDEX_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_configuration_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_FREQUENCY_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_frequency_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_number_of_repetitions_per_attempt), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_STARTING_SUBFRAME_PERIODICITY_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_starting_subframe_periodicity), ppWritePackedMsg, end, + &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_ENABLE_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_hopping_enable), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_OFFSET_TAG, &(pNfapiMsg->emtc_config.prach_ce_level_3_hopping_offset), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEA_TAG, &(pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodea), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEB_TAG, &(pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodeb), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &(pNfapiMsg->tdd_frame_structure_config.subframe_assignment), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &(pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &(pNfapiMsg->l23_config.data_report_mode), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_L23_CONFIG_SFNSF_TAG, &(pNfapiMsg->l23_config.sfnsf), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.dl_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.ul_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_PHY_RF_BANDS_TAG, &(pNfapiMsg->nfapi_config.rf_bands), ppWritePackedMsg, end, &pack_rf_bands_value) && + pack_tlv(NFAPI_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &(pNfapiMsg->nfapi_config.max_transmit_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_EARFCN_TAG, &(pNfapiMsg->nfapi_config.earfcn), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && + pack_tlv(NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_umts_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && + pack_tlv(NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &(pNfapiMsg->nfapi_config.nmm_lte_frequency_bands), ppWritePackedMsg, end, &pack_nmm_frequency_bands_value) && + pack_tlv(NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &(pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + + +static uint8_t pack_nr_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t* config) +{ + + nfapi_nr_config_request_scf_t *pNfapiMsg = (nfapi_nr_config_request_scf_t*)msg; + + for(int i = 0; i<40; i++){ //packing tdd slot config + for(int symbol = 0; symbol<14;symbol++){ + push8(pNfapiMsg->tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list[symbol].slot_config.value, ppWritePackedMsg,end); + } + } + + return (push8(pNfapiMsg->num_tlv, ppWritePackedMsg, end) && + pack_tlv(NFAPI_NR_CONFIG_DL_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.dl_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_DL_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.dl_frequency), ppWritePackedMsg, end, &pack_uint32_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_DL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.dl_grid_size[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_DL_K0_TAG, &(pNfapiMsg->carrier_config.dl_k0[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_NUM_RX_ANT_TAG, &(pNfapiMsg->carrier_config.num_rx_ant), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_NUM_TX_ANT_TAG, &(pNfapiMsg->carrier_config.num_tx_ant), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_UL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.ul_grid_size[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_UL_K0_TAG, &(pNfapiMsg->carrier_config.ul_k0[1]), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_UPLINK_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.uplink_bandwidth), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_UPLINK_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.uplink_frequency), ppWritePackedMsg, end, &pack_uint32_tlv_value) && + + pack_tlv(NFAPI_NR_CONFIG_FRAME_DUPLEX_TYPE_TAG, &(pNfapiMsg->cell_config.frame_duplex_type), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_PHY_CELL_ID_TAG, &(pNfapiMsg->cell_config.phy_cell_id), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + + pack_tlv(NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_PRACH_SEQUENCE_LENGTH_TAG, &(pNfapiMsg->prach_config.prach_sequence_length), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG, &(pNfapiMsg->prach_config.restricted_set_config), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_SSB_PER_RACH_TAG, &(pNfapiMsg->prach_config.ssb_per_rach), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_PRACH_SUB_C_SPACING_TAG, &(pNfapiMsg->prach_config.prach_sub_c_spacing), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_PRACH_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_root_sequence_index), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_K1_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].k1), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_PRACH_ZERO_CORR_CONF_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_zero_corr_conf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_NUM_ROOT_SEQUENCES_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].num_root_sequences), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + + + pack_tlv(NFAPI_NR_CONFIG_SCS_COMMON_TAG, &(pNfapiMsg->ssb_config.scs_common), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_SS_PBCH_POWER_TAG, &(pNfapiMsg->ssb_config.ss_pbch_power), ppWritePackedMsg, end, &pack_uint32_tlv_value) && + + pack_tlv(NFAPI_NR_CONFIG_BETA_PSS_TAG, &(pNfapiMsg->ssb_table.beta_pss), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_MIB_TAG, &(pNfapiMsg->ssb_table.MIB), ppWritePackedMsg, end, &pack_uint32_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[0].ssb_mask), ppWritePackedMsg, end, &pack_uint32_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[1].ssb_mask), ppWritePackedMsg, end, &pack_uint32_tlv_value) && + + pack_tlv(NFAPI_NR_CONFIG_SSB_OFFSET_POINT_A_TAG, &(pNfapiMsg->ssb_table.ssb_offset_point_a), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_SSB_PERIOD_TAG, &(pNfapiMsg->ssb_table.ssb_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_CONFIG_SSB_SUBCARRIER_OFFSET_TAG, &(pNfapiMsg->ssb_table.ssb_subcarrier_offset), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + + pack_tlv(NFAPI_NR_CONFIG_TDD_PERIOD_TAG, &(pNfapiMsg->tdd_table.tdd_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), ppWritePackedMsg, end, &pack_ipv4_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), ppWritePackedMsg, end, &pack_ipv6_address_value) && + pack_tlv(NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + //pack_tlv(NFAPI_NR_NFAPI_RF_BANDS_TAG, &(pNfapiMsg->nfapi_config.rf_bands), ppWritePackedMsg, end, &pack_rf_bands_value) && + pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + pack_tlv(NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + //pack_tlv(NFAPI_NR_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &(pNfapiMsg->nfapi_config.ul_ue_per_sf), ppWritePackedMsg, end, &pack_uint8_tlv_value) && + + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + +static uint8_t pack_nr_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_config_response_scf_t *pNfapiMsg = (nfapi_nr_config_response_scf_t *)msg; + return ( push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + +static uint8_t pack_config_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_config_response_t *pNfapiMsg = (nfapi_config_response_t *)msg; + return ( push8(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + +static uint8_t pack_nr_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_start_request_scf_t *pNfapiMsg = (nfapi_nr_start_request_scf_t *)msg; + return pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); +} + +static uint8_t pack_start_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_start_request_t *pNfapiMsg = (nfapi_start_request_t *)msg; + return pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); +} + +static uint8_t pack_nr_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_start_response_scf_t *pNfapiMsg = (nfapi_nr_start_response_scf_t *)msg; + return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end ) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + +static uint8_t pack_start_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_start_response_t *pNfapiMsg = (nfapi_start_response_t *)msg; + return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end ) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + + +static uint8_t pack_stop_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_stop_request_t *pNfapiMsg = (nfapi_stop_request_t *)msg; + return pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); +} + +static uint8_t pack_stop_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_stop_response_t *pNfapiMsg = (nfapi_stop_response_t *)msg; + return ( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config) ); +} + +static uint8_t pack_measurement_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_measurement_request_t *pNfapiMsg = (nfapi_measurement_request_t *)msg; + return( pack_tlv(NFAPI_MEASUREMENT_REQUEST_DL_RS_XTX_POWER_TAG, &(pNfapiMsg->dl_rs_tx_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_MEASUREMENT_REQUEST_RECEIVED_INTERFERENCE_POWER_TAG, &(pNfapiMsg->received_interference_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_tlv(NFAPI_MEASUREMENT_REQUEST_THERMAL_NOISE_POWER_TAG, &(pNfapiMsg->thermal_noise_power), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_recevied_interference_power_measurement_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_received_interference_power_measurement_t *value = (nfapi_received_interference_power_measurement_t *)tlv; + return ( push16(value->number_of_resource_blocks, ppWritePackedMsg, end) && + pusharrays16(value->received_interference_power, NFAPI_MAX_RECEIVED_INTERFERENCE_POWER_RESULTS, value->number_of_resource_blocks, ppWritePackedMsg, end)); +} + +static uint8_t pack_measurement_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + nfapi_measurement_response_t *pNfapiMsg = (nfapi_measurement_response_t *)msg; + return( push32(pNfapiMsg->error_code, ppWritePackedMsg, end) && + pack_tlv(NFAPI_MEASUREMENT_RESPONSE_DL_RS_POWER_MEASUREMENT_TAG, &(pNfapiMsg->dl_rs_tx_power_measurement), ppWritePackedMsg, end, &pack_int16_tlv_value) && + pack_tlv(NFAPI_MEASUREMENT_RESPONSE_RECEIVED_INTERFERENCE_POWER_MEASUREMENT_TAG, &(pNfapiMsg->received_interference_power_measurement), ppWritePackedMsg, end, + &pack_recevied_interference_power_measurement_value) && + pack_tlv(NFAPI_MEASUREMENT_RESPONSE_THERMAL_NOISE_MEASUREMENT_TAG, &(pNfapiMsg->thermal_noise_power_measurement), ppWritePackedMsg, end, &pack_uint16_tlv_value) && + pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nr_p5_message_body(nfapi_p4_p5_message_header_t *header, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + uint8_t result = 0; + + // look for the specific message + switch (header->message_id) { + case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_REQUEST: + result = pack_nr_pnf_param_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_PARAM_RESPONSE: + result = pack_nr_pnf_param_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_REQUEST: + result = pack_nr_pnf_config_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_RESPONSE: + result = pack_nr_pnf_config_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_START_REQUEST: + result = pack_nr_pnf_start_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_START_RESPONSE: + result = pack_nr_pnf_start_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_REQUEST: + result = pack_nr_pnf_stop_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_STOP_RESPONSE: + result = pack_nr_pnf_stop_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PARAM_REQUEST: + result = pack_nr_param_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE: + result = pack_nr_param_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_CONFIG_REQUEST: + result = pack_nr_config_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE: + result = pack_nr_config_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_START_REQUEST: + result = pack_nr_start_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE: + result = pack_nr_start_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST: + result = pack_stop_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: + result = pack_stop_response(header, ppWritePackedMsg, end, config); + break; + + default: { + if(header->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + header->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { + if(config && config->pack_p4_p5_vendor_extension) { + result = (config->pack_p4_p5_vendor_extension)(header, ppWritePackedMsg, end, config); + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, header->message_id); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, header->message_id); + } + } + break; + } + + return result; +} + + +static uint8_t pack_p5_message_body(nfapi_p4_p5_message_header_t *header, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config) { + uint8_t result = 0; + + // look for the specific message + switch (header->message_id) { + case NFAPI_PNF_PARAM_REQUEST: + result = pack_pnf_param_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_PARAM_RESPONSE: + result = pack_pnf_param_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_CONFIG_REQUEST: + result = pack_pnf_config_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_CONFIG_RESPONSE: + result = pack_pnf_config_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_START_REQUEST: + result = pack_pnf_start_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_START_RESPONSE: + result = pack_pnf_start_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_STOP_REQUEST: + result = pack_pnf_stop_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PNF_STOP_RESPONSE: + result = pack_pnf_stop_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PARAM_REQUEST: + result = pack_param_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_PARAM_RESPONSE: + result = pack_param_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_CONFIG_REQUEST: + result = pack_config_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_CONFIG_RESPONSE: + result = pack_config_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_START_REQUEST: + result = pack_start_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_START_RESPONSE: + result = pack_start_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_STOP_REQUEST: + result = pack_stop_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_STOP_RESPONSE: + result = pack_stop_response(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_MEASUREMENT_REQUEST: + result = pack_measurement_request(header, ppWritePackedMsg, end, config); + break; + + case NFAPI_MEASUREMENT_RESPONSE: + result = pack_measurement_response(header, ppWritePackedMsg, end, config); + break; + + default: { + if(header->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + header->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { + if(config && config->pack_p4_p5_vendor_extension) { + result = (config->pack_p4_p5_vendor_extension)(header, ppWritePackedMsg, end, config); + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, header->message_id); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, header->message_id); + } + } + break; + } + + return result; +} + + +// helper function for message length calculation - +// takes the pointers to the start of message to end of message + +static uint32_t get_packed_msg_len(uintptr_t msgHead, uintptr_t msgEnd) { + if (msgEnd < msgHead) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "get_packed_msg_len: Error in pointers supplied %d, %d\n", msgHead, msgEnd); + return 0; + } + + return (msgEnd - msgHead); +} + +// Main pack function - public +int nfapi_nr_p5_message_pack(void *pMessageBuf, uint32_t messageBufLen, void *pPackedBuf, uint32_t packedBufLen, nfapi_p4_p5_codec_config_t *config) { + nfapi_p4_p5_message_header_t *pMessageHeader = pMessageBuf; + uint8_t *pWritePackedMessage = pPackedBuf; + uint8_t *pPackMessageEnd = pPackedBuf + packedBufLen; + uint8_t *pPackedLengthField = &pWritePackedMessage[4]; + uint32_t packedMsgLen; + uint16_t packedMsgLen16; + + if (pMessageBuf == NULL || pPackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Pack supplied pointers are null\n"); + return -1; + } + + // pack the message + if(push16(pMessageHeader->phy_id, &pWritePackedMessage, pPackMessageEnd) && + push16(pMessageHeader->message_id, &pWritePackedMessage, pPackMessageEnd) && + push16(0, &pWritePackedMessage, pPackMessageEnd) && + push16(pMessageHeader->spare, &pWritePackedMessage, pPackMessageEnd) && + pack_nr_p5_message_body(pMessageHeader, &pWritePackedMessage, pPackMessageEnd, config)) { + // check for a valid message length + packedMsgLen = get_packed_msg_len((uintptr_t)pPackedBuf, (uintptr_t)pWritePackedMessage); + + if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); + return -1; + } else { + packedMsgLen16 = (uint16_t)packedMsgLen; + } + + // Update the message length in the header + if(!push16(packedMsgLen16, &pPackedLengthField, pPackMessageEnd)) + return -1; + + // return the packed length + return (packedMsgLen); + } else { + // Failed to pack the meassage + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Failed to pack message\n"); + return -1; + } +} + +int nfapi_p5_message_pack(void *pMessageBuf, uint32_t messageBufLen, void *pPackedBuf, uint32_t packedBufLen, nfapi_p4_p5_codec_config_t *config) { + nfapi_p4_p5_message_header_t *pMessageHeader = pMessageBuf; + uint8_t *pWritePackedMessage = pPackedBuf; + uint8_t *pPackMessageEnd = pPackedBuf + packedBufLen; + uint8_t *pPackedLengthField = &pWritePackedMessage[4]; + uint32_t packedMsgLen; + uint16_t packedMsgLen16; + + if (pMessageBuf == NULL || pPackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Pack supplied pointers are null\n"); + return -1; + } + + // pack the message + if(push16(pMessageHeader->phy_id, &pWritePackedMessage, pPackMessageEnd) && + push16(pMessageHeader->message_id, &pWritePackedMessage, pPackMessageEnd) && + push16(0/*pMessageHeader->message_length*/, &pWritePackedMessage, pPackMessageEnd) && + push16(pMessageHeader->spare, &pWritePackedMessage, pPackMessageEnd) && + pack_p5_message_body(pMessageHeader, &pWritePackedMessage, pPackMessageEnd, config)) { + // check for a valid message length + packedMsgLen = get_packed_msg_len((uintptr_t)pPackedBuf, (uintptr_t)pWritePackedMessage); + + if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); + return -1; + } else { + packedMsgLen16 = (uint16_t)packedMsgLen; + } + + // Update the message length in the header + if(!push16(packedMsgLen16, &pPackedLengthField, pPackMessageEnd)) + return -1; + + // return the packed length + return (packedMsgLen); + } else { + // Failed to pack the meassage + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 Failed to pack message\n"); + return -1; + } +} + + + +// Unpack routines + + +static uint8_t unpack_nr_pnf_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_param_request_t *pNfapiMsg = (nfapi_nr_pnf_param_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_pnf_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_param_request_t *pNfapiMsg = (nfapi_pnf_param_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_pnf_param_general_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_param_general_t *value = (nfapi_pnf_param_general_t *)tlv; + return( pull8(ppReadPackedMsg, &value->nfapi_sync_mode, end) && + pull8(ppReadPackedMsg, &value->location_mode, end) && + pull16(ppReadPackedMsg, &value->location_coordinates_length, end) && + pullarray8(ppReadPackedMsg, value->location_coordinates, NFAPI_PNF_PARAM_GENERAL_LOCATION_LENGTH, value->location_coordinates_length, end) && + pull32(ppReadPackedMsg, &value->dl_config_timing, end) && + pull32(ppReadPackedMsg, &value->tx_timing, end) && + pull32(ppReadPackedMsg, &value->ul_config_timing, end) && + pull32(ppReadPackedMsg, &value->hi_dci0_timing, end) && + pull16(ppReadPackedMsg, &value->maximum_number_phys, end) && + pull16(ppReadPackedMsg, &value->maximum_total_bandwidth, end) && + pull8(ppReadPackedMsg, &value->maximum_total_number_dl_layers, end) && + pull8(ppReadPackedMsg, &value->maximum_total_number_ul_layers, end) && + pull8(ppReadPackedMsg, &value->shared_bands, end) && + pull8(ppReadPackedMsg, &value->shared_pa, end) && + pulls16(ppReadPackedMsg, &value->maximum_total_power, end) && + pullarray8(ppReadPackedMsg, value->oui, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, NFAPI_PNF_PARAM_GENERAL_OUI_LENGTH, end)); +} + +static uint8_t unpack_rf_config_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_rf_config_info_t *info = (nfapi_rf_config_info_t *)elem; + return pull16(ppReadPackedMsg, &info->rf_config_index, end); +} + +static uint8_t unpack_pnf_phy_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_info_t *phy = (nfapi_pnf_phy_info_t *)elem; + return ( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && + pull16(ppReadPackedMsg, &phy->number_of_rfs, end) && + unpackarray(ppReadPackedMsg, phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, end, &unpack_rf_config_info) && + pull16(ppReadPackedMsg, &phy->number_of_rf_exclusions, end) && + unpackarray(ppReadPackedMsg, phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, end, &unpack_rf_config_info) && + pull16(ppReadPackedMsg, &phy->downlink_channel_bandwidth_supported, end) && + pull16(ppReadPackedMsg, &phy->uplink_channel_bandwidth_supported, end) && + pull8(ppReadPackedMsg, &phy->number_of_dl_layers_supported, end) && + pull8(ppReadPackedMsg, &phy->number_of_ul_layers_supported, end) && + pull16(ppReadPackedMsg, &phy->maximum_3gpp_release_supported, end) && + pull8(ppReadPackedMsg, &phy->nmm_modes_supported, end)); +} + + +static uint8_t unpack_pnf_phy_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_t *value = (nfapi_pnf_phy_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && + unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_info)); +} + +static uint8_t unpack_pnf_rf_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_rf_info_t *rf = (nfapi_pnf_rf_info_t *)elem; + return( pull16(ppReadPackedMsg, &rf->rf_config_index, end) && + pull16(ppReadPackedMsg, &rf->band, end) && + pulls16(ppReadPackedMsg, &rf->maximum_transmit_power, end) && + pulls16(ppReadPackedMsg, &rf->minimum_transmit_power, end) && + pull8(ppReadPackedMsg, &rf->number_of_antennas_suppported, end) && + pull32(ppReadPackedMsg, &rf->minimum_downlink_frequency, end) && + pull32(ppReadPackedMsg, &rf->maximum_downlink_frequency, end) && + pull32(ppReadPackedMsg, &rf->minimum_uplink_frequency, end) && + pull32(ppReadPackedMsg, &rf->maximum_uplink_frequency, end)); +} +static uint8_t unpack_pnf_rf_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_rf_t *value = (nfapi_pnf_rf_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_rfs, end) && + unpackarray(ppReadPackedMsg, value->rf, sizeof(nfapi_pnf_rf_info_t), NFAPI_MAX_PNF_RF, value->number_of_rfs, end, &unpack_pnf_rf_info)); +} + +static uint8_t unpack_pnf_phy_rel10_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel10_info_t *phy = (nfapi_pnf_phy_rel10_info_t *)elem; + return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && + pull16(ppReadPackedMsg, &phy->transmission_mode_7_supported, end) && + pull16(ppReadPackedMsg, &phy->transmission_mode_8_supported, end) && + pull16(ppReadPackedMsg, &phy->two_antenna_ports_for_pucch, end) && + pull16(ppReadPackedMsg, &phy->transmission_mode_9_supported, end) && + pull16(ppReadPackedMsg, &phy->simultaneous_pucch_pusch, end) && + pull16(ppReadPackedMsg, &phy->four_layer_tx_with_tm3_and_tm4, end)); +} +static uint8_t unpack_pnf_phy_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel10_t *value = (nfapi_pnf_phy_rel10_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && + unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel10_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel10_info)); +} + + +static uint8_t unpack_pnf_phy_rel11_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel11_info_t *phy = (nfapi_pnf_phy_rel11_info_t *)elem; + return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && + pull16(ppReadPackedMsg, &phy->edpcch_supported, end) && + pull16(ppReadPackedMsg, &phy->multi_ack_csi_reporting, end ) && + pull16(ppReadPackedMsg, &phy->pucch_tx_diversity, end) && + pull16(ppReadPackedMsg, &phy->ul_comp_supported, end) && + pull16(ppReadPackedMsg, &phy->transmission_mode_5_supported, end)); +} + +static uint8_t unpack_pnf_phy_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel11_t *value = (nfapi_pnf_phy_rel11_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && + unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel11_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel11_info)); +} + +static uint8_t unpack_phy_phy_rel12_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel12_info_t *phy = (nfapi_pnf_phy_rel12_info_t *)elem; + return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && + pull16(ppReadPackedMsg, &phy->csi_subframe_set, end) && + pull16(ppReadPackedMsg, &phy->enhanced_4tx_codebook, end) && + pull16(ppReadPackedMsg, &phy->drs_supported, end) && + pull16(ppReadPackedMsg, &phy->ul_64qam_supported, end) && + pull16(ppReadPackedMsg, &phy->transmission_mode_10_supported, end) && + pull16(ppReadPackedMsg, &phy->alternative_bts_indices, end)); +} + +static uint8_t unpack_pnf_phy_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel12_t *value = (nfapi_pnf_phy_rel12_t *)tlv; + return (pull16(ppReadPackedMsg, &value->number_of_phys, end) && + unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel12_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_phy_phy_rel12_info)); +} + +static uint8_t unpack_pnf_phy_rel13_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_info_t *phy = (nfapi_pnf_phy_rel13_info_t *)elem; + return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && + pull16(ppReadPackedMsg, &phy->pucch_format4_supported, end) && + pull16(ppReadPackedMsg, &phy->pucch_format5_supported, end) && + pull16(ppReadPackedMsg, &phy->more_than_5_ca_support, end) && + pull16(ppReadPackedMsg, &phy->laa_supported, end) && + pull16(ppReadPackedMsg, &phy->laa_ending_in_dwpts_supported, end) && + pull16(ppReadPackedMsg, &phy->laa_starting_in_second_slot_supported, end) && + pull16(ppReadPackedMsg, &phy->beamforming_supported, end) && + pull16(ppReadPackedMsg, &phy->csi_rs_enhancement_supported, end) && + pull16(ppReadPackedMsg, &phy->drms_enhancement_supported, end) && + pull16(ppReadPackedMsg, &phy->srs_enhancement_supported, end)); +} + +static uint8_t unpack_pnf_phy_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_t *value = (nfapi_pnf_phy_rel13_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && + unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel13_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel13_info)); +} + +static uint8_t unpack_pnf_phy_rel13_nb_info_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_nb_iot_info_t *phy = (nfapi_pnf_phy_rel13_nb_iot_info_t *)elem; + return( pull16(ppReadPackedMsg, &phy->phy_config_index, end) && + pull16(ppReadPackedMsg, &phy->number_of_rfs, end) && + unpackarray(ppReadPackedMsg, phy->rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rfs, end, &unpack_rf_config_info) && + pull16(ppReadPackedMsg, &phy->number_of_rf_exclusions, end) && + unpackarray(ppReadPackedMsg, phy->excluded_rf_config, sizeof(nfapi_rf_config_info_t), NFAPI_MAX_PNF_PHY_RF_CONFIG, phy->number_of_rf_exclusions, end, &unpack_rf_config_info) && + pull8(ppReadPackedMsg, &phy->number_of_dl_layers_supported, end) && + pull8(ppReadPackedMsg, &phy->number_of_ul_layers_supported, end) && + pull16(ppReadPackedMsg, &phy->maximum_3gpp_release_supported, end) && + pull8(ppReadPackedMsg, &phy->nmm_modes_supported, end)); +} + +static uint8_t unpack_pnf_phy_rel13_nb_iot_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rel13_nb_iot_t *value = (nfapi_pnf_phy_rel13_nb_iot_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_phys, end) && + unpackarray(ppReadPackedMsg, value->phy, sizeof(nfapi_pnf_phy_rel13_nb_iot_info_t), NFAPI_MAX_PNF_PHY, value->number_of_phys, end, &unpack_pnf_phy_rel13_nb_info_info)); +} + +static uint8_t unpack_nr_pnf_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_param_response_t *pNfapiMsg = (nfapi_nr_pnf_param_response_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, &unpack_pnf_param_general_value}, + { NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, &unpack_pnf_phy_value}, + }; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + + + +static uint8_t unpack_pnf_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_param_response_t *pNfapiMsg = (nfapi_pnf_param_response_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_PNF_PARAM_GENERAL_TAG, &pNfapiMsg->pnf_param_general, &unpack_pnf_param_general_value}, + { NFAPI_PNF_PHY_TAG, &pNfapiMsg->pnf_phy, &unpack_pnf_phy_value}, + { NFAPI_PNF_RF_TAG, &pNfapiMsg->pnf_rf, &unpack_pnf_rf_value}, + { NFAPI_PNF_PHY_REL10_TAG, &pNfapiMsg->pnf_phy_rel10, &unpack_pnf_phy_rel10_value}, + { NFAPI_PNF_PHY_REL11_TAG, &pNfapiMsg->pnf_phy_rel11, &unpack_pnf_phy_rel11_value}, + { NFAPI_PNF_PHY_REL12_TAG, &pNfapiMsg->pnf_phy_rel12, &unpack_pnf_phy_rel12_value}, + { NFAPI_PNF_PHY_REL13_TAG, &pNfapiMsg->pnf_phy_rel13, &unpack_pnf_phy_rel13_value}, + { NFAPI_PNF_PHY_REL13_NB_IOT_TAG, &pNfapiMsg->pnf_phy_rel13_nb_iot, &unpack_pnf_phy_rel13_nb_iot_value}, + + }; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_phy_rf_config_info(void *elem, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_phy_rf_config_info_t *rf = (nfapi_phy_rf_config_info_t *)elem; + return( pull16(ppReadPackedMsg, &rf->phy_id, end) && + pull16(ppReadPackedMsg, &rf->phy_config_index, end) && + pull16(ppReadPackedMsg, &rf->rf_config_index, end)); +} + +static uint8_t unpack_pnf_phy_rf_config_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_pnf_phy_rf_config_t *value = (nfapi_pnf_phy_rf_config_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_phy_rf_config_info, end) && + unpackarray(ppReadPackedMsg, value->phy_rf_config, sizeof(nfapi_phy_rf_config_info_t), NFAPI_MAX_PHY_RF_INSTANCES, value->number_phy_rf_config_info, end, &unpack_phy_rf_config_info)); +} + +static uint8_t unpack_nr_pnf_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_config_request_t *pNfapiMsg = (nfapi_nr_pnf_config_request_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, &unpack_pnf_phy_rf_config_value}, + }; + return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension); +} + + + +static uint8_t unpack_pnf_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_config_request_t *pNfapiMsg = (nfapi_pnf_config_request_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_PNF_PHY_RF_TAG, &pNfapiMsg->pnf_phy_rf_config, &unpack_pnf_phy_rf_config_value}, + }; + return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension); +} + + +static uint8_t unpack_nr_pnf_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_config_response_t *pNfapiMsg = (nfapi_nr_pnf_config_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_pnf_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_config_response_t *pNfapiMsg = (nfapi_pnf_config_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_nr_pnf_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_start_request_t *pNfapiMsg = (nfapi_nr_pnf_start_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_pnf_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_start_request_t *pNfapiMsg = (nfapi_pnf_start_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_pnf_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_start_response_t *pNfapiMsg = (nfapi_pnf_start_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end ) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_nr_pnf_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_pnf_start_response_t *pNfapiMsg = (nfapi_nr_pnf_start_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end ) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + + +static uint8_t unpack_pnf_stop_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_stop_request_t *pNfapiMsg = (nfapi_pnf_stop_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_pnf_stop_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_pnf_stop_response_t *pNfapiMsg = (nfapi_pnf_stop_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_param_request_t *pNfapiMsg = (nfapi_param_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_nr_param_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_param_request_scf_t *pNfapiMsg = (nfapi_nr_param_request_scf_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_param_response_t *pNfapiMsg = (nfapi_param_response_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_L1_STATUS_PHY_STATE_TAG, &pNfapiMsg->l1_status.phy_state, &unpack_uint16_tlv_value}, + + { NFAPI_PHY_CAPABILITIES_DL_BANDWIDTH_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.dl_bandwidth_support, &unpack_uint16_tlv_value}, + { NFAPI_PHY_CAPABILITIES_UL_BANDWIDTH_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.ul_bandwidth_support, &unpack_uint16_tlv_value}, + { NFAPI_PHY_CAPABILITIES_DL_MODULATION_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.dl_modulation_support, &unpack_uint16_tlv_value}, + { NFAPI_PHY_CAPABILITIES_UL_MODULATION_SUPPORT_TAG, &pNfapiMsg->phy_capabilities.ul_modulation_support, &unpack_uint16_tlv_value}, + { NFAPI_PHY_CAPABILITIES_PHY_ANTENNA_CAPABILITY_TAG, &pNfapiMsg->phy_capabilities.phy_antenna_capability, &unpack_uint16_tlv_value}, + { NFAPI_PHY_CAPABILITIES_RELEASE_CAPABILITY_TAG, &pNfapiMsg->phy_capabilities.release_capability, &unpack_uint16_tlv_value}, + { NFAPI_PHY_CAPABILITIES_MBSFN_CAPABILITY_TAG, &pNfapiMsg->phy_capabilities.mbsfn_capability, &unpack_uint16_tlv_value}, + + { NFAPI_LAA_CAPABILITY_LAA_SUPPORT_TAG, &pNfapiMsg->laa_capability.laa_support, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CAPABILITY_PD_SENSING_LBT_SUPPORT_TAG, &pNfapiMsg->laa_capability.pd_sensing_lbt_support, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CAPABILITY_MULTI_CARRIER_LBT_SUPPORT_TAG, &pNfapiMsg->laa_capability.multi_carrier_lbt_support, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CAPABILITY_PARTIAL_SF_SUPPORT_TAG, &pNfapiMsg->laa_capability.partial_sf_support, &unpack_uint16_tlv_value}, + + { NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &pNfapiMsg->subframe_config.duplex_mode, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &pNfapiMsg->subframe_config.pcfich_power_offset, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_PB_TAG, &pNfapiMsg->subframe_config.pb, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.dl_cyclic_prefix_type, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.ul_cyclic_prefix_type, &unpack_uint16_tlv_value}, + + { NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.dl_channel_bandwidth, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.ul_channel_bandwidth, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &pNfapiMsg->rf_config.reference_signal_power, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.tx_antenna_ports, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.rx_antenna_ports, &unpack_uint16_tlv_value}, + + { NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &pNfapiMsg->phich_config.phich_resource, &unpack_uint16_tlv_value}, + { NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &pNfapiMsg->phich_config.phich_duration, &unpack_uint16_tlv_value}, + { NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &pNfapiMsg->phich_config.phich_power_offset, &unpack_uint16_tlv_value}, + + { NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, + { NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, + { NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &pNfapiMsg->sch_config.physical_cell_id, &unpack_uint16_tlv_value}, + + { NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &pNfapiMsg->prach_config.configuration_index, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &pNfapiMsg->prach_config.root_sequence_index, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &pNfapiMsg->prach_config.zero_correlation_zone_configuration, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &pNfapiMsg->prach_config.high_speed_flag, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &pNfapiMsg->prach_config.frequency_offset, &unpack_uint16_tlv_value}, + + { NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &pNfapiMsg->pusch_config.hopping_mode, &unpack_uint16_tlv_value}, + { NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &pNfapiMsg->pusch_config.hopping_offset, &unpack_uint16_tlv_value}, + { NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &pNfapiMsg->pusch_config.number_of_subbands, &unpack_uint16_tlv_value}, + + { NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &pNfapiMsg->pucch_config.delta_pucch_shift, &unpack_uint16_tlv_value}, + { NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &pNfapiMsg->pucch_config.n_cqi_rb, &unpack_uint16_tlv_value}, + { NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &pNfapiMsg->pucch_config.n_an_cs, &unpack_uint16_tlv_value}, + { NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &pNfapiMsg->pucch_config.n1_pucch_an, &unpack_uint16_tlv_value}, + + { NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &pNfapiMsg->srs_config.bandwidth_configuration, &unpack_uint16_tlv_value}, + { NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &pNfapiMsg->srs_config.max_up_pts, &unpack_uint16_tlv_value}, + { NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &pNfapiMsg->srs_config.srs_subframe_configuration, &unpack_uint16_tlv_value}, + { NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission, &unpack_uint16_tlv_value}, + + { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping, &unpack_uint16_tlv_value}, + { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &pNfapiMsg->uplink_reference_signal_config.group_assignment, &unpack_uint16_tlv_value}, + { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms, &unpack_uint16_tlv_value}, + + { NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &pNfapiMsg->tdd_frame_structure_config.subframe_assignment, &unpack_uint16_tlv_value}, + { NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns, &unpack_uint16_tlv_value}, + + { NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &pNfapiMsg->l23_config.data_report_mode, &unpack_uint16_tlv_value}, + { NFAPI_L23_CONFIG_SFNSF_TAG, &pNfapiMsg->l23_config.sfnsf, &unpack_uint16_tlv_value}, + + { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv4, &unpack_ipv4_address_value}, + { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv6, &unpack_ipv6_address_value}, + { NFAPI_NFAPI_P7_VNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_vnf_port, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv4, &unpack_ipv4_address_value}, + { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv6, &unpack_ipv6_address_value}, + { NFAPI_NFAPI_P7_PNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_pnf_port, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.dl_ue_per_sf, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.ul_ue_per_sf, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_RF_BANDS_TAG, &pNfapiMsg->nfapi_config.rf_bands, &unpack_rf_bands_value}, + { NFAPI_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &pNfapiMsg->nfapi_config.timing_info_mode, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &pNfapiMsg->nfapi_config.timing_info_period, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &pNfapiMsg->nfapi_config.max_transmit_power, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_EARFCN_TAG, &pNfapiMsg->nfapi_config.earfcn, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands, &unpack_nmm_frequency_bands_value}, + { NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_umts_frequency_bands, &unpack_nmm_frequency_bands_value}, + { NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_lte_frequency_bands, &unpack_nmm_frequency_bands_value}, + { NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported, &unpack_uint8_tlv_value}, + + }; + return ( pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_nr_param_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_param_response_scf_t *pNfapiMsg = (nfapi_nr_param_response_scf_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_NR_PARAM_TLV_RELEASE_CAPABILITY_TAG, &(pNfapiMsg->cell_param.release_capability), &unpack_uint16_tlv_value}, + { NFAPI_NR_PARAM_TLV_PHY_STATE_TAG, &(pNfapiMsg->cell_param.phy_state),&unpack_uint16_tlv_value}, + { NFAPI_NR_PARAM_TLV_SKIP_BLANK_DL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_dl_config), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_SKIP_BLANK_UL_CONFIG_TAG, &(pNfapiMsg->cell_param.skip_blank_ul_config), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_NUM_CONFIG_TLVS_TO_REPORT_TAG, &(pNfapiMsg->cell_param.num_config_tlvs_to_report ), &unpack_uint16_tlv_value}, + + { NFAPI_NR_PARAM_TLV_CYCLIC_PREFIX_TAG, &(pNfapiMsg->carrier_param.cyclic_prefix), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_DL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_dl), &unpack_uint16_tlv_value}, + { NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_DL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_dl), &unpack_uint16_tlv_value}, + { NFAPI_NR_PARAM_TLV_SUPPORTED_SUBCARRIER_SPACINGS_UL_TAG, &(pNfapiMsg->carrier_param.supported_subcarrier_spacings_ul), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_SUPPORTED_BANDWIDTH_UL_TAG, &(pNfapiMsg->carrier_param.supported_bandwidth_ul), &unpack_uint16_tlv_value}, + + + { NFAPI_NR_PARAM_TLV_CCE_MAPPING_TYPE_TAG, &(pNfapiMsg->pdcch_param.cce_mapping_type), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_CORESET_OUTSIDE_FIRST_3_OFDM_SYMS_OF_SLOT_TAG, &(pNfapiMsg->pdcch_param.coreset_outside_first_3_of_ofdm_syms_of_slot), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PRECODER_GRANULARITY_CORESET_TAG, &(pNfapiMsg->pdcch_param.coreset_precoder_granularity_coreset), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDCCH_MU_MIMO_TAG, &(pNfapiMsg->pdcch_param.pdcch_mu_mimo), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDCCH_PRECODER_CYCLING_TAG, &(pNfapiMsg->pdcch_param.pdcch_precoder_cycling), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_PDCCHS_PER_SLOT_TAG, &(pNfapiMsg->pdcch_param.max_pdcch_per_slot), &unpack_uint8_tlv_value}, + + { NFAPI_NR_PARAM_TLV_PUCCH_FORMATS_TAG, &(pNfapiMsg->pucch_param.pucch_formats), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_PUCCHS_PER_SLOT_TAG, &(pNfapiMsg->pucch_param.max_pucchs_per_slot), &unpack_uint8_tlv_value}, + + { NFAPI_NR_PARAM_TLV_PDSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pdsch_param.pdsch_mapping_type), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_allocation_types), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pdsch_param.pdsch_vrb_to_prb_mapping), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_CBG_TAG, &(pNfapiMsg->pdsch_param.pdsch_cbg), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_config_types), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_DMRS_MAX_LENGTH_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_max_length), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pdsch_param.pdsch_dmrs_additional_pos), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_PDSCH_S_YBS_PER_SLOT_TAG, &(pNfapiMsg->pdsch_param.max_pdsch_tbs_per_slot), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_PDSCH_TAG, &(pNfapiMsg->pdsch_param.max_number_mimo_layers_pdsch), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_DL_TAG, &(pNfapiMsg->pdsch_param.max_mu_mimo_users_dl), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_DATA_IN_DMRS_SYMBOLS_TAG, &(pNfapiMsg->pdsch_param.pdsch_data_in_dmrs_symbols), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PREMPTION_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.premption_support), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PDSCH_NON_SLOT_SUPPORT_TAG, &(pNfapiMsg->pdsch_param.pdsch_non_slot_support), &unpack_uint8_tlv_value}, + + { NFAPI_NR_PARAM_TLV_UCI_MUX_ULSCH_IN_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_mux_ulsch_in_pusch), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_UCI_ONLY_PUSCH_TAG, &(pNfapiMsg->pusch_param.uci_only_pusch), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_FREQUENCY_HOPPING_TAG, &(pNfapiMsg->pusch_param.pusch_frequency_hopping), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_DMRS_CONFIG_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_config_types), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_DMRS_MAX_LEN_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_max_len), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_DMRS_ADDITIONAL_POS_TAG, &(pNfapiMsg->pusch_param.pusch_dmrs_additional_pos), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_CBG_TAG, &(pNfapiMsg->pusch_param.pusch_cbg), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_MAPPING_TYPE_TAG, &(pNfapiMsg->pusch_param.pusch_mapping_type), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_ALLOCATION_TYPES_TAG, &(pNfapiMsg->pusch_param.pusch_allocation_types), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_VRB_TO_PRB_MAPPING_TAG, &(pNfapiMsg->pusch_param.pusch_vrb_to_prb_mapping), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_MAX_PTRS_PORTS_TAG, &(pNfapiMsg->pusch_param.pusch_max_ptrs_ports), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_PDUSCHS_TBS_PER_SLOT_TAG, &(pNfapiMsg->pusch_param.max_pduschs_tbs_per_slot), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_NUMBER_MIMO_LAYERS_NON_CB_PUSCH_TAG, &(pNfapiMsg->pusch_param.max_number_mimo_layers_non_cb_pusch), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_SUPPORTED_MODULATION_ORDER_UL_TAG, &(pNfapiMsg->pusch_param.supported_modulation_order_ul), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_MU_MIMO_USERS_UL_TAG, &(pNfapiMsg->pusch_param.max_mu_mimo_users_ul), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_DFTS_OFDM_SUPPORT_TAG, &(pNfapiMsg->pusch_param.dfts_ofdm_support), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PUSCH_AGGREGATION_FACTOR_TAG, &(pNfapiMsg->pusch_param.pusch_aggregation_factor), &unpack_uint8_tlv_value}, + + { NFAPI_NR_PARAM_TLV_PRACH_LONG_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_long_formats), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PRACH_SHORT_FORMATS_TAG, &(pNfapiMsg->prach_param.prach_short_formats), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_PRACH_RESTRICTED_SETS_TAG, &(pNfapiMsg->prach_param.prach_restricted_sets), &unpack_uint8_tlv_value}, + { NFAPI_NR_PARAM_TLV_MAX_PRACH_FD_OCCASIONS_IN_A_SLOT_TAG, &(pNfapiMsg->prach_param.max_prach_fd_occasions_in_a_slot), &unpack_uint8_tlv_value}, + + { NFAPI_NR_PARAM_TLV_RSSI_MEASUREMENT_SUPPORT_TAG, &(pNfapiMsg->measurement_param.rssi_measurement_support), &unpack_uint8_tlv_value}, + //config + { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv4, &unpack_ipv4_address_value}, + { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv6, &unpack_ipv6_address_value}, + { NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_vnf_port, &unpack_uint16_tlv_value}, + { NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv4, &unpack_ipv4_address_value}, + { NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv6, &unpack_ipv6_address_value}, + { NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_pnf_port, &unpack_uint16_tlv_value}, + { NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint8_tlv_value}, + { NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &pNfapiMsg->nfapi_config.timing_info_mode, &unpack_uint8_tlv_value}, + { NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &pNfapiMsg->nfapi_config.timing_info_period, &unpack_uint8_tlv_value}, + }; + // print ppReadPackedMsg + uint8_t *ptr = *ppReadPackedMsg; + printf("\n Read message unpack_param_response: "); + + while(ptr < end) { + printf(" %d ", *ptr); + ptr++; + } + + printf("\n"); + return ( pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_config_request_t *pNfapiMsg = (nfapi_config_request_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_SUBFRAME_CONFIG_DUPLEX_MODE_TAG, &pNfapiMsg->subframe_config.duplex_mode, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_PCFICH_POWER_OFFSET_TAG, &pNfapiMsg->subframe_config.pcfich_power_offset, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_PB_TAG, &pNfapiMsg->subframe_config.pb, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_DL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.dl_cyclic_prefix_type, &unpack_uint16_tlv_value}, + { NFAPI_SUBFRAME_CONFIG_UL_CYCLIC_PREFIX_TYPE_TAG, &pNfapiMsg->subframe_config.ul_cyclic_prefix_type, &unpack_uint16_tlv_value}, + + { NFAPI_RF_CONFIG_DL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.dl_channel_bandwidth, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_UL_CHANNEL_BANDWIDTH_TAG, &pNfapiMsg->rf_config.ul_channel_bandwidth, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_REFERENCE_SIGNAL_POWER_TAG, &pNfapiMsg->rf_config.reference_signal_power, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_TX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.tx_antenna_ports, &unpack_uint16_tlv_value}, + { NFAPI_RF_CONFIG_RX_ANTENNA_PORTS_TAG, &pNfapiMsg->rf_config.rx_antenna_ports, &unpack_uint16_tlv_value}, + + { NFAPI_PHICH_CONFIG_PHICH_RESOURCE_TAG, &pNfapiMsg->phich_config.phich_resource, &unpack_uint16_tlv_value}, + { NFAPI_PHICH_CONFIG_PHICH_DURATION_TAG, &pNfapiMsg->phich_config.phich_duration, &unpack_uint16_tlv_value}, + { NFAPI_PHICH_CONFIG_PHICH_POWER_OFFSET_TAG, &pNfapiMsg->phich_config.phich_power_offset, &unpack_uint16_tlv_value}, + + { NFAPI_SCH_CONFIG_PRIMARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.primary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, + { NFAPI_SCH_CONFIG_SECONDARY_SYNCHRONIZATION_SIGNAL_EPRE_EPRERS_TAG, &pNfapiMsg->sch_config.secondary_synchronization_signal_epre_eprers, &unpack_uint16_tlv_value}, + { NFAPI_SCH_CONFIG_PHYSICAL_CELL_ID_TAG, &pNfapiMsg->sch_config.physical_cell_id, &unpack_uint16_tlv_value}, + + { NFAPI_PRACH_CONFIG_CONFIGURATION_INDEX_TAG, &pNfapiMsg->prach_config.configuration_index, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_ROOT_SEQUENCE_INDEX_TAG, &pNfapiMsg->prach_config.root_sequence_index, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &pNfapiMsg->prach_config.zero_correlation_zone_configuration, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_HIGH_SPEED_FLAG_TAG, &pNfapiMsg->prach_config.high_speed_flag, &unpack_uint16_tlv_value}, + { NFAPI_PRACH_CONFIG_FREQUENCY_OFFSET_TAG, &pNfapiMsg->prach_config.frequency_offset, &unpack_uint16_tlv_value}, + + { NFAPI_PUSCH_CONFIG_HOPPING_MODE_TAG, &pNfapiMsg->pusch_config.hopping_mode, &unpack_uint16_tlv_value}, + { NFAPI_PUSCH_CONFIG_HOPPING_OFFSET_TAG, &pNfapiMsg->pusch_config.hopping_offset, &unpack_uint16_tlv_value}, + { NFAPI_PUSCH_CONFIG_NUMBER_OF_SUBBANDS_TAG, &pNfapiMsg->pusch_config.number_of_subbands, &unpack_uint16_tlv_value}, + + { NFAPI_PUCCH_CONFIG_DELTA_PUCCH_SHIFT_TAG, &pNfapiMsg->pucch_config.delta_pucch_shift, &unpack_uint16_tlv_value}, + { NFAPI_PUCCH_CONFIG_N_CQI_RB_TAG, &pNfapiMsg->pucch_config.n_cqi_rb, &unpack_uint16_tlv_value}, + { NFAPI_PUCCH_CONFIG_N_AN_CS_TAG, &pNfapiMsg->pucch_config.n_an_cs, &unpack_uint16_tlv_value}, + { NFAPI_PUCCH_CONFIG_N1_PUCCH_AN_TAG, &pNfapiMsg->pucch_config.n1_pucch_an, &unpack_uint16_tlv_value}, + + { NFAPI_SRS_CONFIG_BANDWIDTH_CONFIGURATION_TAG, &pNfapiMsg->srs_config.bandwidth_configuration, &unpack_uint16_tlv_value}, + { NFAPI_SRS_CONFIG_MAX_UP_PTS_TAG, &pNfapiMsg->srs_config.max_up_pts, &unpack_uint16_tlv_value}, + { NFAPI_SRS_CONFIG_SRS_SUBFRAME_CONFIGURATION_TAG, &pNfapiMsg->srs_config.srs_subframe_configuration, &unpack_uint16_tlv_value}, + { NFAPI_SRS_CONFIG_SRS_ACKNACK_SRS_SIMULTANEOUS_TRANSMISSION_TAG, &pNfapiMsg->srs_config.srs_acknack_srs_simultaneous_transmission, &unpack_uint16_tlv_value}, + + { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_UPLINK_RS_HOPPING_TAG, &pNfapiMsg->uplink_reference_signal_config.uplink_rs_hopping, &unpack_uint16_tlv_value}, + { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_GROUP_ASSIGNMENT_TAG, &pNfapiMsg->uplink_reference_signal_config.group_assignment, &unpack_uint16_tlv_value}, + { NFAPI_UPLINK_REFERENCE_SIGNAL_CONFIG_CYCLIC_SHIFT_1_FOR_DRMS_TAG, &pNfapiMsg->uplink_reference_signal_config.cyclic_shift_1_for_drms, &unpack_uint16_tlv_value}, + + + { NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_PDSCH_TAG, &pNfapiMsg->laa_config.ed_threshold_lbt_pdsch, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CONFIG_ED_THRESHOLD_FOR_LBT_FOR_DRS_TAG, &pNfapiMsg->laa_config.ed_threshold_lbt_drs, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CONFIG_PD_THRESHOLD_TAG, &pNfapiMsg->laa_config.pd_threshold, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CONFIG_MULTI_CARRIER_TYPE_TAG, &pNfapiMsg->laa_config.multi_carrier_type, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CONFIG_MULTI_CARRIER_TX_TAG, &pNfapiMsg->laa_config.multi_carrier_tx, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CONFIG_MULTI_CARRIER_FREEZE_TAG, &pNfapiMsg->laa_config.multi_carrier_freeze, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CONFIG_TX_ANTENNA_PORTS_FOR_DRS_TAG, &pNfapiMsg->laa_config.tx_antenna_ports_drs, &unpack_uint16_tlv_value}, + { NFAPI_LAA_CONFIG_TRANSMISSION_POWER_FOR_DRS_TAG, &pNfapiMsg->laa_config.tx_power_drs, &unpack_uint16_tlv_value}, + + { NFAPI_EMTC_CONFIG_PBCH_REPETITIONS_ENABLE_R13_TAG, &pNfapiMsg->emtc_config.pbch_repetitions_enable_r13, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CATM_ROOT_SEQUENCE_INDEX_TAG, &pNfapiMsg->emtc_config.prach_catm_root_sequence_index, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CATM_ZERO_CORRELATION_ZONE_CONFIGURATION_TAG, &pNfapiMsg->emtc_config.prach_catm_zero_correlation_zone_configuration, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CATM_HIGH_SPEED_FLAG, &pNfapiMsg->emtc_config.prach_catm_high_speed_flag, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_configuration_index, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_frequency_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_starting_subframe_periodicity, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_hopping_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_0_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_0_hopping_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_configuration_index, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_frequency_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_starting_subframe_periodicity, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_hopping_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_1_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_1_hopping_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_configuration_index, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_frequency_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_starting_subframe_periodicity, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_hopping_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_2_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_2_hopping_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_CONFIGURATION_INDEX_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_configuration_index, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_FREQUENCY_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_frequency_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_NUMBER_OF_REPETITIONS_PER_ATTEMPT_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_number_of_repetitions_per_attempt, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_STARTING_SUBFRAME_PERIODICITY_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_starting_subframe_periodicity, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_ENABLE_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_hopping_enable, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PRACH_CE_LEVEL_3_HOPPING_OFFSET_TAG, &pNfapiMsg->emtc_config.prach_ce_level_3_hopping_offset, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEA_TAG, &pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodea, &unpack_uint16_tlv_value}, + { NFAPI_EMTC_CONFIG_PUCCH_INTERVAL_ULHOPPINGCONFIGCOMMONMODEB_TAG, &pNfapiMsg->emtc_config.pucch_interval_ulhoppingconfigcommonmodeb, &unpack_uint16_tlv_value}, + + { NFAPI_TDD_FRAME_STRUCTURE_SUBFRAME_ASSIGNMENT_TAG, &pNfapiMsg->tdd_frame_structure_config.subframe_assignment, &unpack_uint16_tlv_value}, + { NFAPI_TDD_FRAME_STRUCTURE_SPECIAL_SUBFRAME_PATTERNS_TAG, &pNfapiMsg->tdd_frame_structure_config.special_subframe_patterns, &unpack_uint16_tlv_value}, + + { NFAPI_L23_CONFIG_DATA_REPORT_MODE_TAG, &pNfapiMsg->l23_config.data_report_mode, &unpack_uint16_tlv_value}, + { NFAPI_L23_CONFIG_SFNSF_TAG, &pNfapiMsg->l23_config.sfnsf, &unpack_uint16_tlv_value}, + + { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv4, &unpack_ipv4_address_value}, + { NFAPI_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_vnf_address_ipv6, &unpack_ipv6_address_value}, + { NFAPI_NFAPI_P7_VNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_vnf_port, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV4_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv4, &unpack_ipv4_address_value}, + { NFAPI_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &pNfapiMsg->nfapi_config.p7_pnf_address_ipv6, &unpack_ipv6_address_value}, + { NFAPI_NFAPI_P7_PNF_PORT_TAG, &pNfapiMsg->nfapi_config.p7_pnf_port, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_DOWNLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.dl_ue_per_sf, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_UPLINK_UES_PER_SUBFRAME_TAG, &pNfapiMsg->nfapi_config.ul_ue_per_sf, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_RF_BANDS_TAG, &pNfapiMsg->nfapi_config.rf_bands, &unpack_rf_bands_value}, + { NFAPI_NFAPI_TIMING_WINDOW_TAG, &pNfapiMsg->nfapi_config.timing_window, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_TIMING_INFO_MODE_TAG, &pNfapiMsg->nfapi_config.timing_info_mode, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_TIMING_INFO_PERIOD_TAG, &pNfapiMsg->nfapi_config.timing_info_period, &unpack_uint8_tlv_value}, + { NFAPI_NFAPI_MAXIMUM_TRANSMIT_POWER_TAG, &pNfapiMsg->nfapi_config.max_transmit_power, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_EARFCN_TAG, &pNfapiMsg->nfapi_config.earfcn, &unpack_uint16_tlv_value}, + { NFAPI_NFAPI_NMM_GSM_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_gsm_frequency_bands, &unpack_nmm_frequency_bands_value}, + { NFAPI_NFAPI_NMM_UMTS_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_umts_frequency_bands, &unpack_nmm_frequency_bands_value}, + { NFAPI_NFAPI_NMM_LTE_FREQUENCY_BANDS_TAG, &pNfapiMsg->nfapi_config.nmm_lte_frequency_bands, &unpack_nmm_frequency_bands_value}, + { NFAPI_NFAPI_NMM_UPLINK_RSSI_SUPPORTED_TAG, &pNfapiMsg->nfapi_config.nmm_uplink_rssi_supported, &unpack_uint8_tlv_value}, + + }; + return ( pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} +static uint8_t unpack_nr_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t* config) +{ + nfapi_nr_config_request_scf_t *pNfapiMsg = (nfapi_nr_config_request_scf_t*)msg; + + pNfapiMsg->tdd_table.max_tdd_periodicity_list = (nfapi_nr_max_tdd_periodicity_t*) malloc(40*sizeof(nfapi_nr_max_tdd_periodicity_t)); + + for(int i=0;i<40;i++) + pNfapiMsg->tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list = (nfapi_nr_max_num_of_symbol_per_slot_t*) malloc(14*sizeof(nfapi_nr_max_num_of_symbol_per_slot_t)); + + pNfapiMsg->prach_config.num_prach_fd_occasions_list=(nfapi_nr_num_prach_fd_occasions_t *) malloc(sizeof(nfapi_nr_num_prach_fd_occasions_t)); + + for(int i = 0; i < 40; i++){ //unpacking tdd slot config + for(int symbol = 0; symbol<14;symbol++){ + pull8(ppReadPackedMsg,&pNfapiMsg->tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list[symbol].slot_config.value, end); + } + } + unpack_tlv_t unpack_fns[] = + { + { NFAPI_NR_CONFIG_DL_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.dl_bandwidth), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_DL_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.dl_frequency), &unpack_uint32_tlv_value}, + { NFAPI_NR_CONFIG_DL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.dl_grid_size[1]), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_DL_K0_TAG, &(pNfapiMsg->carrier_config.dl_k0[1]), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_NUM_RX_ANT_TAG, &(pNfapiMsg->carrier_config.num_rx_ant), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_NUM_TX_ANT_TAG, &(pNfapiMsg->carrier_config.num_tx_ant), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_UL_GRID_SIZE_TAG, &(pNfapiMsg->carrier_config.ul_grid_size[1]), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_UL_K0_TAG, &(pNfapiMsg->carrier_config.ul_k0[1]), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_UPLINK_BANDWIDTH_TAG, &(pNfapiMsg->carrier_config.uplink_bandwidth), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_UPLINK_FREQUENCY_TAG, &(pNfapiMsg->carrier_config.uplink_frequency), &unpack_uint32_tlv_value}, + { NFAPI_NR_CONFIG_FRAME_DUPLEX_TYPE_TAG, &(pNfapiMsg->cell_config.frame_duplex_type), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_PHY_CELL_ID_TAG, &(pNfapiMsg->cell_config.phy_cell_id), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_PRACH_SEQUENCE_LENGTH_TAG, &(pNfapiMsg->prach_config.prach_sequence_length), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG, &(pNfapiMsg->prach_config.restricted_set_config), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_SSB_PER_RACH_TAG, &(pNfapiMsg->prach_config.ssb_per_rach), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_PRACH_SUB_C_SPACING_TAG, &(pNfapiMsg->prach_config.prach_sub_c_spacing), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_PRACH_ROOT_SEQUENCE_INDEX_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_root_sequence_index), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_K1_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].k1), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_PRACH_ZERO_CORR_CONF_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].prach_zero_corr_conf), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_NUM_ROOT_SEQUENCES_TAG, &(pNfapiMsg->prach_config.num_prach_fd_occasions_list[0].num_root_sequences), &unpack_uint8_tlv_value}, + + { NFAPI_NR_CONFIG_SCS_COMMON_TAG, &(pNfapiMsg->ssb_config.scs_common), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_SS_PBCH_POWER_TAG, &(pNfapiMsg->ssb_config.ss_pbch_power), &unpack_uint32_tlv_value}, + { NFAPI_NR_CONFIG_BETA_PSS_TAG, &(pNfapiMsg->ssb_table.beta_pss), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_MIB_TAG, &(pNfapiMsg->ssb_table.MIB), &unpack_uint32_tlv_value}, + { NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[0].ssb_mask), &unpack_uint32_tlv_value}, + { NFAPI_NR_CONFIG_SSB_MASK_TAG, &(pNfapiMsg->ssb_table.ssb_mask_list[1].ssb_mask), &unpack_uint32_tlv_value}, + + { NFAPI_NR_CONFIG_SSB_OFFSET_POINT_A_TAG, &(pNfapiMsg->ssb_table.ssb_offset_point_a), &unpack_uint16_tlv_value}, + { NFAPI_NR_CONFIG_SSB_PERIOD_TAG, &(pNfapiMsg->ssb_table.ssb_period), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_SSB_SUBCARRIER_OFFSET_TAG, &(pNfapiMsg->ssb_table.ssb_subcarrier_offset), &unpack_uint8_tlv_value}, + { NFAPI_NR_CONFIG_TDD_PERIOD_TAG, &(pNfapiMsg->tdd_table.tdd_period), &unpack_uint8_tlv_value}, + { NFAPI_NR_NFAPI_P7_PNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_address_ipv6), &unpack_ipv6_address_value}, + { NFAPI_NR_NFAPI_P7_PNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_pnf_port), &unpack_uint16_tlv_value}, + { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), &unpack_ipv4_address_value}, + { NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), &unpack_ipv6_address_value}, + { NFAPI_NR_NFAPI_P7_VNF_PORT_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_port), &unpack_uint16_tlv_value}, + { NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG, &(pNfapiMsg->nfapi_config.timing_info_mode), &unpack_uint8_tlv_value}, + { NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, &(pNfapiMsg->nfapi_config.timing_info_period), &unpack_uint8_tlv_value}, + { NFAPI_NR_NFAPI_TIMING_WINDOW_TAG, &(pNfapiMsg->nfapi_config.timing_window), &unpack_uint8_tlv_value}, + }; + + return ( pull8(ppReadPackedMsg, &pNfapiMsg->num_tlv, end) && + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); + +} + +static uint8_t unpack_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_config_response_t *pNfapiMsg = (nfapi_config_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_nr_config_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_config_response_scf_t *pNfapiMsg = (nfapi_nr_config_response_scf_t *)msg; + return ( pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_nr_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_start_request_scf_t *pNfapiMsg = ( nfapi_nr_start_request_scf_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_start_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_start_request_t *pNfapiMsg = ( nfapi_start_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_start_response_t *pNfapiMsg = (nfapi_start_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_nr_start_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_nr_start_response_scf_t *pNfapiMsg = (nfapi_nr_start_response_scf_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} + +static uint8_t unpack_stop_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_stop_request_t *pNfapiMsg = (nfapi_stop_request_t *)msg; + return unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_stop_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_stop_response_t *pNfapiMsg = (nfapi_stop_response_t *)msg; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))); +} +static uint8_t unpack_measurement_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_measurement_request_t *pNfapiMsg = (nfapi_measurement_request_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_MEASUREMENT_REQUEST_DL_RS_XTX_POWER_TAG, &pNfapiMsg->dl_rs_tx_power, &unpack_uint16_tlv_value}, + { NFAPI_MEASUREMENT_REQUEST_RECEIVED_INTERFERENCE_POWER_TAG, &pNfapiMsg->received_interference_power, &unpack_uint16_tlv_value}, + { NFAPI_MEASUREMENT_REQUEST_THERMAL_NOISE_POWER_TAG, &pNfapiMsg->thermal_noise_power, &unpack_uint16_tlv_value}, + }; + return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_received_interference_power_measurement_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_received_interference_power_measurement_t *value = (nfapi_received_interference_power_measurement_t *)tlv; + return ( pull16(ppReadPackedMsg, &value->number_of_resource_blocks, end) && + pullarrays16(ppReadPackedMsg, value->received_interference_power, NFAPI_MAX_RECEIVED_INTERFERENCE_POWER_RESULTS, value->number_of_resource_blocks, end)); +} + + +static uint8_t unpack_measurement_response(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config) { + nfapi_measurement_response_t *pNfapiMsg = (nfapi_measurement_response_t *)msg; + unpack_tlv_t unpack_fns[] = { + { NFAPI_MEASUREMENT_RESPONSE_DL_RS_POWER_MEASUREMENT_TAG, &pNfapiMsg->dl_rs_tx_power_measurement, &unpack_int16_tlv_value}, + { NFAPI_MEASUREMENT_RESPONSE_RECEIVED_INTERFERENCE_POWER_MEASUREMENT_TAG, &pNfapiMsg->received_interference_power_measurement, &unpack_received_interference_power_measurement_value}, + { NFAPI_MEASUREMENT_RESPONSE_THERMAL_NOISE_MEASUREMENT_TAG, &pNfapiMsg->thermal_noise_power_measurement, &unpack_int16_tlv_value}, + }; + return ( pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) && + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +// unpack length check + +static int check_nr_unpack_length(nfapi_nr_phy_msg_type_e msgId, uint32_t unpackedBufLen) { + int retLen = 0; + + switch (msgId) { + case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_pnf_param_request_t)) + retLen = sizeof(nfapi_pnf_param_request_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_nr_pnf_param_response_t)) + retLen = sizeof(nfapi_nr_pnf_param_response_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_pnf_config_request_t)) + retLen = sizeof(nfapi_nr_pnf_config_request_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_nr_pnf_config_response_t)) + retLen = sizeof(nfapi_nr_pnf_config_response_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_START_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_pnf_start_request_t)) + retLen = sizeof(nfapi_nr_pnf_start_request_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_START_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_nr_pnf_start_response_t)) + retLen = sizeof(nfapi_nr_pnf_start_response_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_pnf_stop_request_t)) + retLen = sizeof(nfapi_nr_pnf_stop_request_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_nr_pnf_stop_response_t)) + retLen = sizeof(nfapi_nr_pnf_stop_response_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PARAM_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_param_request_scf_t)) + retLen = sizeof(nfapi_nr_param_request_scf_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_nr_param_response_scf_t)) + retLen = sizeof(nfapi_nr_param_response_scf_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_CONFIG_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_config_request_scf_t)) + retLen = sizeof(nfapi_nr_config_request_scf_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_nr_config_response_scf_t)) + retLen = sizeof(nfapi_nr_config_response_scf_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_START_REQUEST: + if (unpackedBufLen >= sizeof( nfapi_nr_start_request_scf_t)) + retLen = sizeof( nfapi_nr_start_request_scf_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_nr_start_response_scf_t)) + retLen = sizeof(nfapi_nr_start_response_scf_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_stop_request_t)) + retLen = sizeof(nfapi_stop_request_t); + + break; + + case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_stop_response_t)) + retLen = sizeof(nfapi_stop_response_t); + + break; + + default: + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s Unknown message ID %d\n", __FUNCTION__, msgId); + break; + } + + return retLen; +} + + +static int check_unpack_length(nfapi_message_id_e msgId, uint32_t unpackedBufLen) { + int retLen = 0; + + switch (msgId) { + case NFAPI_PNF_PARAM_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_pnf_param_request_t)) + retLen = sizeof(nfapi_pnf_param_request_t); + + break; + + case NFAPI_PNF_PARAM_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_pnf_param_response_t)) + retLen = sizeof(nfapi_pnf_param_response_t); + + break; + + case NFAPI_PNF_CONFIG_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_pnf_config_request_t)) + retLen = sizeof(nfapi_pnf_config_request_t); + + break; + + case NFAPI_PNF_CONFIG_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_pnf_config_response_t)) + retLen = sizeof(nfapi_pnf_config_response_t); + + break; + + case NFAPI_PNF_START_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_pnf_start_request_t)) + retLen = sizeof(nfapi_pnf_start_request_t); + + break; + + case NFAPI_PNF_START_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_pnf_start_response_t)) + retLen = sizeof(nfapi_pnf_start_response_t); + + break; + + case NFAPI_PNF_STOP_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_pnf_stop_request_t)) + retLen = sizeof(nfapi_pnf_stop_request_t); + + break; + + case NFAPI_PNF_STOP_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_pnf_stop_response_t)) + retLen = sizeof(nfapi_pnf_stop_response_t); + + break; + + case NFAPI_PARAM_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_param_request_t)) + retLen = sizeof(nfapi_param_request_t); + + break; + + case NFAPI_PARAM_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_param_response_t)) + retLen = sizeof(nfapi_param_response_t); + + break; + + case NFAPI_CONFIG_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_config_request_t)) + retLen = sizeof(nfapi_config_request_t); + + break; + + case NFAPI_CONFIG_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_config_response_t)) + retLen = sizeof(nfapi_config_response_t); + + break; + + case NFAPI_START_REQUEST: + if (unpackedBufLen >= sizeof( nfapi_start_request_t)) + retLen = sizeof( nfapi_start_request_t); + + break; + + case NFAPI_START_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_start_response_t)) + retLen = sizeof(nfapi_start_response_t); + + break; + + case NFAPI_STOP_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_stop_request_t)) + retLen = sizeof(nfapi_stop_request_t); + + break; + + case NFAPI_STOP_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_stop_response_t)) + retLen = sizeof(nfapi_stop_response_t); + + break; + + case NFAPI_MEASUREMENT_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_measurement_request_t)) + retLen = sizeof(nfapi_measurement_request_t); + + break; + + case NFAPI_MEASUREMENT_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_measurement_response_t)) + retLen = sizeof(nfapi_measurement_response_t); + + break; + + default: + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s Unknown message ID %d\n", __FUNCTION__, msgId); + break; + } + + return retLen; +} + + +// Main unpack functions - public + +int nfapi_p5_message_header_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p4_p5_codec_config_t *config) { + nfapi_p4_p5_message_header_t *pMessageHeader = pUnpackedBuf; + uint8_t *pReadPackedMessage = pMessageBuf; + uint8_t *end = pMessageBuf + messageBufLen; + + if (pMessageBuf == NULL || pUnpackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 header unpack supplied pointers are null\n"); + return -1; + } + + if (messageBufLen < NFAPI_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p4_p5_message_header_t)) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 header unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); + return -1; + } + + // process the header + return ( pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && + pull16(&pReadPackedMessage, &pMessageHeader->spare, end) ); +} + +int nfapi_nr_p5_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p4_p5_codec_config_t *config) { + nfapi_p4_p5_message_header_t *pMessageHeader = pUnpackedBuf; + uint8_t *pReadPackedMessage = pMessageBuf; + uint8_t *end = pMessageBuf + messageBufLen; + + if (pMessageBuf == NULL || pUnpackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied pointers are null\n"); + return -1; + } + + if (messageBufLen < NFAPI_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p4_p5_message_header_t)) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); + return -1; + } + + uint8_t *ptr = pReadPackedMessage; + printf("\n Read message unpack: "); + + while(ptr < end) { + printf(" %d ", *ptr); + ptr++; + } + + printf("\n"); + // clean the supplied buffer for - tag value blanking + (void)memset(pUnpackedBuf, 0, unpackedBufLen); + + // process the header + if( !(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end ) && + pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && + pull16(&pReadPackedMessage, &pMessageHeader->spare, end))) { + // failed to read the header + return -1; + } + + int result = -1; + + if(check_nr_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) { + // the unpack buffer is not big enough for the struct + return -1; + } + + // look for the specific message + switch (pMessageHeader->message_id) { + case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_REQUEST: + result = unpack_nr_pnf_param_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_PARAM_RESPONSE: + result = unpack_nr_pnf_param_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_REQUEST: + result = unpack_nr_pnf_config_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_CONFIG_RESPONSE: + result = unpack_nr_pnf_config_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_START_REQUEST: + result = unpack_nr_pnf_start_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_START_RESPONSE: + result = unpack_nr_pnf_start_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_REQUEST: + result = unpack_pnf_stop_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_RESPONSE: + result = unpack_pnf_stop_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PARAM_REQUEST: + result = unpack_nr_param_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE: + result = unpack_nr_param_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_CONFIG_REQUEST: + result = unpack_nr_config_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE: + result = unpack_nr_config_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_START_REQUEST: + result = unpack_nr_start_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE: + result = unpack_nr_start_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST: + result = unpack_stop_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: + result = unpack_stop_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_MEASUREMENT_REQUEST: + result = unpack_measurement_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_MEASUREMENT_RESPONSE: + result = unpack_measurement_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + default: + if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { + if(config && config->unpack_p4_p5_vendor_extension) { + result = (config->unpack_p4_p5_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown P5 message ID %d\n", __FUNCTION__, pMessageHeader->message_id); + } + + break; + } + + return result; +} + +int nfapi_p5_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p4_p5_codec_config_t *config) { + nfapi_p4_p5_message_header_t *pMessageHeader = pUnpackedBuf; + uint8_t *pReadPackedMessage = pMessageBuf; + uint8_t *end = pMessageBuf + messageBufLen; + + if (pMessageBuf == NULL || pUnpackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied pointers are null\n"); + return -1; + } + + if (messageBufLen < NFAPI_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p4_p5_message_header_t)) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P5 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); + return -1; + } + + uint8_t *ptr = pReadPackedMessage; + printf("\n Read message unpack: "); + + while(ptr < end) { + printf(" %d ", *ptr); + ptr++; + } + + printf("\n"); + // clean the supplied buffer for - tag value blanking + (void)memset(pUnpackedBuf, 0, unpackedBufLen); + + // process the header + if( !(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end ) && + pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && + pull16(&pReadPackedMessage, &pMessageHeader->spare, end))) { + // failed to read the header + return -1; + } + + int result = -1; + + if(check_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) { + // the unpack buffer is not big enough for the struct + return -1; + } + + // look for the specific message + switch (pMessageHeader->message_id) { + case NFAPI_PNF_PARAM_REQUEST: + result = unpack_pnf_param_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PNF_PARAM_RESPONSE: + result = unpack_pnf_param_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PNF_CONFIG_REQUEST: + result = unpack_pnf_config_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PNF_CONFIG_RESPONSE: + result = unpack_pnf_config_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PNF_START_REQUEST: + result = unpack_pnf_start_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PNF_START_RESPONSE: + result = unpack_pnf_start_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PNF_STOP_REQUEST: + result = unpack_pnf_stop_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PNF_STOP_RESPONSE: + result = unpack_pnf_stop_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PARAM_REQUEST: + result = unpack_param_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_PARAM_RESPONSE: + result = unpack_param_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_CONFIG_REQUEST: + result = unpack_config_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_CONFIG_RESPONSE: + result = unpack_config_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_START_REQUEST: + result = unpack_start_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_START_RESPONSE: + result = unpack_start_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_STOP_REQUEST: + result = unpack_stop_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_STOP_RESPONSE: + result = unpack_stop_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_MEASUREMENT_REQUEST: + result = unpack_measurement_request(&pReadPackedMessage, end, pMessageHeader, config); + break; + + case NFAPI_MEASUREMENT_RESPONSE: + result = unpack_measurement_response(&pReadPackedMessage, end, pMessageHeader, config); + break; + + default: + if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { + if(config && config->unpack_p4_p5_vendor_extension) { + result = (config->unpack_p4_p5_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown P5 message ID %d\n", __FUNCTION__, pMessageHeader->message_id); + } + + break; + } + + return result; +} + diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c index 9e9c9d5496..ecd8e3dbfe 100644 --- a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c +++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c @@ -1,7362 +1,7948 @@ -/* - * Copyright 2017 Cisco Systems, Inc. - * - * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "nfapi_nr_interface_scf.h" - -extern int nfapi_unpack_p7_vendor_extension(nfapi_p7_message_header_t *header, uint8_t **ppReadPackedMsg, void *user_data); -extern int nfapi_pack_p7_vendor_extension(nfapi_p7_message_header_t *header, uint8_t **ppWritePackedMsg, void *user_data); - -uint32_t nfapi_calculate_checksum(uint8_t *buffer, uint16_t len) { - uint32_t chksum = 0; - // calcaulte upto the checksum - chksum = crc32(chksum, buffer, 8); - // skip the checksum - uint8_t zeros[4] = {0, 0, 0, 0}; - chksum = crc32(chksum, zeros, 4); - // continu with the rest of the mesage - chksum = crc32(chksum, &buffer[NFAPI_P7_HEADER_LENGTH], len - NFAPI_P7_HEADER_LENGTH); - // return the inverse - return ~(chksum); -} - -int nfapi_p7_update_checksum(uint8_t *buffer, uint32_t len) { - uint32_t checksum = nfapi_calculate_checksum(buffer, len); - uint8_t *p_write = &buffer[8]; - return (push32(checksum, &p_write, buffer + len) > 0 ? 0 : -1); -} - -int nfapi_p7_update_transmit_timestamp(uint8_t *buffer, uint32_t timestamp) { - uint8_t *p_write = &buffer[12]; - return (push32(timestamp, &p_write, buffer + 16) > 0 ? 0 : -1); -} - -uint32_t nfapi_p7_calculate_checksum(uint8_t *buffer, uint32_t len) { - return nfapi_calculate_checksum(buffer, len); -} - -void *nfapi_p7_allocate(size_t size, nfapi_p7_codec_config_t *config) { - if(size == 0) - return 0; - - void *buffer_p = NULL; - - if(config && config->allocate) { - buffer_p = (config->allocate)(size); - - if(buffer_p != NULL) { - memset(buffer_p,0,size); - } - - return buffer_p; - } else { - buffer_p = calloc(1, size); - return buffer_p; - } -} - -void nfapi_p7_deallocate(void *ptr, nfapi_p7_codec_config_t *config) { - if(ptr == NULL) - return; - - if(config && config->deallocate) { - return (config->deallocate)(ptr); - } else { - return free(ptr); - } -} -// Pack routines - - -static uint8_t pack_dl_config_dci_dl_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel8_t *value = (nfapi_dl_config_dci_dl_pdu_rel8_t *)tlv; - //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() dci_format:%u\n", __FUNCTION__, value->dci_format); - return ( push8(value->dci_format, ppWritePackedMsg, end) && - push8(value->cce_idx, ppWritePackedMsg, end) && - push8(value->aggregation_level, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push8(value->resource_allocation_type, ppWritePackedMsg, end) && - push8(value->virtual_resource_block_assignment_flag, ppWritePackedMsg, end) && - push32(value->resource_block_coding, ppWritePackedMsg, end) && - push8(value->mcs_1, ppWritePackedMsg, end) && - push8(value->redundancy_version_1, ppWritePackedMsg, end) && - push8(value->new_data_indicator_1, ppWritePackedMsg, end) && - push8(value->transport_block_to_codeword_swap_flag, ppWritePackedMsg, end) && - push8(value->mcs_2, ppWritePackedMsg, end) && - push8(value->redundancy_version_2, ppWritePackedMsg, end) && - push8(value->new_data_indicator_2, ppWritePackedMsg, end) && - push8(value->harq_process, ppWritePackedMsg, end) && - push8(value->tpmi, ppWritePackedMsg, end) && - push8(value->pmi, ppWritePackedMsg, end) && - push8(value->precoding_information, ppWritePackedMsg, end) && - push8(value->tpc, ppWritePackedMsg, end) && - push8(value->downlink_assignment_index, ppWritePackedMsg, end) && - push8(value->ngap, ppWritePackedMsg, end) && - push8(value->transport_block_size_index, ppWritePackedMsg, end) && - push8(value->downlink_power_offset, ppWritePackedMsg, end) && - push8(value->allocate_prach_flag, ppWritePackedMsg, end) && - push8(value->preamble_index, ppWritePackedMsg, end) && - push8(value->prach_mask_index, ppWritePackedMsg, end) && - push8(value->rnti_type, ppWritePackedMsg, end) && - push16(value->transmission_power, ppWritePackedMsg, end)); -} - -static uint8_t pack_dl_config_dci_dl_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel9_t *value = (nfapi_dl_config_dci_dl_pdu_rel9_t *)tlv; - return( push8(value->mcch_flag, ppWritePackedMsg, end) && - push8(value->mcch_change_notification, ppWritePackedMsg, end) && - push8(value->scrambling_identity, ppWritePackedMsg, end)); -} - -static uint8_t pack_dl_config_dci_dl_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel10_t *value = (nfapi_dl_config_dci_dl_pdu_rel10_t *)tlv; - return ( push8(value->cross_carrier_scheduling_flag, ppWritePackedMsg, end) && - push8(value->carrier_indicator, ppWritePackedMsg, end) && - push8(value->srs_flag, ppWritePackedMsg, end) && - push8(value->srs_request, ppWritePackedMsg, end) && - push8(value->antenna_ports_scrambling_and_layers, ppWritePackedMsg, end) && - push8(value->total_dci_length_including_padding, ppWritePackedMsg, end) && - push8(value->n_dl_rb, ppWritePackedMsg, end)); -} - -static uint8_t pack_dl_config_dci_dl_pdu_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel11_t *value = (nfapi_dl_config_dci_dl_pdu_rel11_t *)tlv; - return ( push8(value->harq_ack_resource_offset, ppWritePackedMsg, end) && - push8(value->pdsch_re_mapping_quasi_co_location_indicator, ppWritePackedMsg, end)); -} - -static uint8_t pack_dl_config_dci_dl_pdu_rel12_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel12_t *value = (nfapi_dl_config_dci_dl_pdu_rel12_t *)tlv; - return ( push8(value->primary_cell_type, ppWritePackedMsg, end) && - push8(value->ul_dl_configuration_flag, ppWritePackedMsg, end) && - push8(value->number_ul_dl_configurations, ppWritePackedMsg, end) && - pusharray8(value->ul_dl_configuration_indication, NFAPI_MAX_UL_DL_CONFIGURATIONS, value->number_ul_dl_configurations, ppWritePackedMsg, end)); -} - -static uint8_t pack_tpm_value(nfapi_dl_config_dci_dl_tpm_t *value, uint8_t **ppWritePackedMsg, uint8_t *end) { - if (!( push8(value->num_prb_per_subband, ppWritePackedMsg, end) && - push8(value->number_of_subbands, ppWritePackedMsg, end) && - push8(value->num_antennas, ppWritePackedMsg, end))) - return 0; - - uint8_t idx = 0; - - for(idx = 0; idx < value->number_of_subbands; ++idx) { - nfapi_dl_config_dci_dl_tpm_subband_info_t *subband_info = &(value->subband_info[idx]); - - if(!(push8(subband_info->subband_index, ppWritePackedMsg, end) && - push8(subband_info->scheduled_ues, ppWritePackedMsg, end))) - return 0; - - uint8_t antenna_idx = 0; - uint8_t scheduled_ue_idx = 0; - - for(antenna_idx = 0; antenna_idx < value->num_antennas; ++antenna_idx) { - for(scheduled_ue_idx = 0; scheduled_ue_idx < subband_info->scheduled_ues; ++scheduled_ue_idx) { - if(!push16(subband_info->precoding_value[antenna_idx][scheduled_ue_idx], ppWritePackedMsg, end)) - return 0; - } - } - } - - return 1; -} - - -static uint8_t pack_dl_tti_csi_rs_pdu_rel15_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *value = (nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *)tlv; - return( - push16(value->bwp_size, ppWritePackedMsg, end) && - push16(value->bwp_start, ppWritePackedMsg, end) && - push8(value->subcarrier_spacing, ppWritePackedMsg, end) && - push8(value->cyclic_prefix, ppWritePackedMsg, end) && - push16(value->start_rb, ppWritePackedMsg, end) && - push16(value->nr_of_rbs, ppWritePackedMsg, end) && - push8(value->csi_type, ppWritePackedMsg, end) && - push8(value->row, ppWritePackedMsg, end) && - push16(value->freq_domain, ppWritePackedMsg, end) && - push8(value->symb_l0, ppWritePackedMsg, end) && - push8(value->symb_l1, ppWritePackedMsg, end) && - push8(value->cdm_type, ppWritePackedMsg, end) && - push8(value->freq_density, ppWritePackedMsg, end) && - push16(value->scramb_id, ppWritePackedMsg, end) && - push8(value->power_control_offset, ppWritePackedMsg, end) && - push8(value->power_control_offset_ss, ppWritePackedMsg, end) - ); -} - - -static uint8_t pack_dl_tti_pdcch_pdu_rel15_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *value = (nfapi_nr_dl_tti_pdcch_pdu_rel15_t *)tlv; - - for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) { - if(!push16(value->dci_pdu[i].RNTI, ppWritePackedMsg, end) && - push16(value->dci_pdu[i].ScramblingId, ppWritePackedMsg, end) && - push16(value->dci_pdu[i].ScramblingRNTI, ppWritePackedMsg, end) && - push8(value->dci_pdu[i].CceIndex, ppWritePackedMsg, end) && - push8(value->dci_pdu[i].AggregationLevel, ppWritePackedMsg, end) && - push8(value->dci_pdu[i].beta_PDCCH_1_0, ppWritePackedMsg, end) && - push8(value->dci_pdu[i].powerControlOffsetSS, ppWritePackedMsg, end) && - push16(value->dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end) && - pusharray8(value->dci_pdu[i].Payload, DCI_PAYLOAD_BYTE_LEN, value->dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end)); - - return 0; - } - - // TODO: resolve the packaging of array (currently sending a single element) - return( - push16(value->BWPSize, ppWritePackedMsg, end) && - push16(value->BWPStart, ppWritePackedMsg, end) && - push8(value->SubcarrierSpacing, ppWritePackedMsg, end) && - push8(value->CyclicPrefix, ppWritePackedMsg, end) && - push8(value->StartSymbolIndex, ppWritePackedMsg, end) && - push8(value->DurationSymbols, ppWritePackedMsg, end) && - pusharray8(value->FreqDomainResource, 6, 6, ppWritePackedMsg, end) && - push8(value->CceRegMappingType, ppWritePackedMsg, end) && - push8(value->RegBundleSize, ppWritePackedMsg, end) && - push8(value->InterleaverSize, ppWritePackedMsg, end) && - push8(value->CoreSetType, ppWritePackedMsg, end) && - push16(value->ShiftIndex, ppWritePackedMsg, end) && - push8(value->precoderGranularity, ppWritePackedMsg, end) && - push16(value->numDlDci, ppWritePackedMsg, end)); -} - - -static uint8_t pack_dl_tti_pdsch_pdu_rel15_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *value = (nfapi_nr_dl_tti_pdsch_pdu_rel15_t *)tlv; - // TODO: resolve the packaging of array (currently sending a single element) - return( - push16(value->pduBitmap, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push16(value->pduIndex, ppWritePackedMsg, end) && - push16(value->BWPSize, ppWritePackedMsg, end) && - push16(value->BWPStart, ppWritePackedMsg, end) && - push8(value->SubcarrierSpacing, ppWritePackedMsg, end) && - push8(value->CyclicPrefix, ppWritePackedMsg, end) && - push8(value->NrOfCodewords, ppWritePackedMsg, end) && - pusharray16(value->targetCodeRate, 2, 1, ppWritePackedMsg, end) && - pusharray8(value->qamModOrder, 2, 1, ppWritePackedMsg, end) && - pusharray8(value->mcsIndex, 2, 1, ppWritePackedMsg, end) && - pusharray8(value->mcsTable, 2, 1, ppWritePackedMsg, end) && - pusharray8(value->rvIndex, 2, 1, ppWritePackedMsg, end) && - pusharray32(value->TBSize, 2, 1, ppWritePackedMsg, end) && - push16(value->dataScramblingId, ppWritePackedMsg, end) && - push8(value->nrOfLayers, ppWritePackedMsg, end) && - push8(value->transmissionScheme, ppWritePackedMsg, end) && - push8(value->refPoint, ppWritePackedMsg, end) && - push16(value->dlDmrsSymbPos, ppWritePackedMsg, end) && - push8(value->dmrsConfigType, ppWritePackedMsg, end) && - push16(value->dlDmrsScramblingId, ppWritePackedMsg, end) && - push8(value->SCID, ppWritePackedMsg, end) && - push8(value->numDmrsCdmGrpsNoData, ppWritePackedMsg, end) && - push16(value->dmrsPorts, ppWritePackedMsg, end) && - push8(value->resourceAlloc, ppWritePackedMsg, end) && - push16(value->rbStart, ppWritePackedMsg, end) && - push16(value->rbSize, ppWritePackedMsg, end) && - push8(value->VRBtoPRBMapping, ppWritePackedMsg, end) && - push8(value->StartSymbolIndex, ppWritePackedMsg, end) && - push8(value->NrOfSymbols, ppWritePackedMsg, end) && - push8(value->PTRSPortIndex, ppWritePackedMsg, end) && - push8(value->PTRSTimeDensity, ppWritePackedMsg, end) && - push8(value->PTRSFreqDensity, ppWritePackedMsg, end) && - push8(value->PTRSReOffset, ppWritePackedMsg, end) - ); -} - - -static uint8_t pack_dl_tti_ssb_pdu_rel15_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_ssb_pdu_rel15_t *value = (nfapi_nr_dl_tti_ssb_pdu_rel15_t *)tlv; - return( - push16(value->PhysCellId, ppWritePackedMsg, end) && - push8(value->BetaPss, ppWritePackedMsg, end) && - push8(value->SsbBlockIndex, ppWritePackedMsg, end) && - push8(value->SsbSubcarrierOffset, ppWritePackedMsg, end) && - push16(value->ssbOffsetPointA, ppWritePackedMsg, end) && - push8(value->bchPayloadFlag, ppWritePackedMsg, end) && - push32(value->bchPayload, ppWritePackedMsg, end) - // TODO: pack precoding_and_beamforming too - ); -} - - -static uint8_t pack_dl_config_dci_dl_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel13_t *value = (nfapi_dl_config_dci_dl_pdu_rel13_t *)tlv; - return( push8(value->laa_end_partial_sf_flag, ppWritePackedMsg, end) && - push8(value->laa_end_partial_sf_configuration, ppWritePackedMsg, end) && - push8(value->initial_lbt_sf, ppWritePackedMsg, end) && - push8(value->codebook_size_determination, ppWritePackedMsg, end) && - push8(value->drms_table_flag, ppWritePackedMsg, end) && - push8(value->tpm_struct_flag, ppWritePackedMsg, end) && - (value->tpm_struct_flag == 1 ? pack_tpm_value(&(value->tpm), ppWritePackedMsg, end) : 1)); -} - -static uint8_t pack_dl_config_bch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_bch_pdu_rel8_t *value = (nfapi_dl_config_bch_pdu_rel8_t *)tlv; - //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s()\n", __FUNCTION__); - return( push16(value->length, ppWritePackedMsg, end) && - push16(value->pdu_index, ppWritePackedMsg, end) && - push16(value->transmission_power, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_mch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_mch_pdu_rel8_t *value = (nfapi_dl_config_mch_pdu_rel8_t *)tlv; - return ( push16(value->length, ppWritePackedMsg, end) && - push16(value->pdu_index, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push8(value->resource_allocation_type, ppWritePackedMsg, end) && - push32(value->resource_block_coding, ppWritePackedMsg, end) && - push8(value->modulation, ppWritePackedMsg, end) && - push16(value->transmission_power, ppWritePackedMsg, end) && - push16(value->mbsfn_area_id, ppWritePackedMsg, end)); -} - -static uint8_t pack_bf_vector_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_bf_vector_t *bf = (nfapi_bf_vector_t *)elem; - return ( push8(bf->subband_index, ppWritePackedMsg, end) && - push8(bf->num_antennas, ppWritePackedMsg, end) && - pusharray16(bf->bf_value, NFAPI_MAX_NUM_ANTENNAS, bf->num_antennas, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_dlsch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel8_t *value = (nfapi_dl_config_dlsch_pdu_rel8_t *)tlv; - return ( push16(value->length, ppWritePackedMsg, end) && - push16(value->pdu_index, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push8(value->resource_allocation_type, ppWritePackedMsg, end) && - push8(value->virtual_resource_block_assignment_flag, ppWritePackedMsg, end) && - push32(value->resource_block_coding, ppWritePackedMsg, end) && - push8(value->modulation, ppWritePackedMsg, end) && - push8(value->redundancy_version, ppWritePackedMsg, end) && - push8(value->transport_blocks, ppWritePackedMsg, end) && - push8(value->transport_block_to_codeword_swap_flag, ppWritePackedMsg, end) && - push8(value->transmission_scheme, ppWritePackedMsg, end) && - push8(value->number_of_layers, ppWritePackedMsg, end) && - push8(value->number_of_subbands, ppWritePackedMsg, end) && - pusharray8(value->codebook_index, NFAPI_MAX_NUM_SUBBANDS, value->number_of_subbands, ppWritePackedMsg, end) && - push8(value->ue_category_capacity, ppWritePackedMsg, end) && - push8(value->pa, ppWritePackedMsg, end) && - push8(value->delta_power_offset_index, ppWritePackedMsg, end) && - push8(value->ngap, ppWritePackedMsg, end) && - push8(value->nprb, ppWritePackedMsg, end) && - push8(value->transmission_mode, ppWritePackedMsg, end) && - push8(value->num_bf_prb_per_subband, ppWritePackedMsg, end) && - push8(value->num_bf_vector, ppWritePackedMsg, end) && - packarray(value->bf_vector, sizeof(nfapi_bf_vector_t), NFAPI_MAX_BF_VECTORS, value->num_bf_vector, ppWritePackedMsg, end, &pack_bf_vector_info)); -} -static uint8_t pack_dl_config_dlsch_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel9_t *value = (nfapi_dl_config_dlsch_pdu_rel9_t *)tlv; - return ( push8(value->nscid, ppWritePackedMsg, end) ); -} -static uint8_t pack_dl_config_dlsch_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel10_t *value = (nfapi_dl_config_dlsch_pdu_rel10_t *)tlv; - return ( push8(value->csi_rs_flag, ppWritePackedMsg, end) && - push8(value->csi_rs_resource_config_r10, ppWritePackedMsg, end) && - push16(value->csi_rs_zero_tx_power_resource_config_bitmap_r10, ppWritePackedMsg, end) && - push8(value->csi_rs_number_nzp_configuration, ppWritePackedMsg, end) && - pusharray8(value->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, value->csi_rs_number_nzp_configuration, ppWritePackedMsg, end) && - push8(value->pdsch_start, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_dlsch_pdu_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel11_t *value = (nfapi_dl_config_dlsch_pdu_rel11_t *)tlv; - return( push8(value->drms_config_flag, ppWritePackedMsg, end) && - push16(value->drms_scrambling, ppWritePackedMsg, end) && - push8(value->csi_config_flag, ppWritePackedMsg, end) && - push16(value->csi_scrambling, ppWritePackedMsg, end) && - push8(value->pdsch_re_mapping_flag, ppWritePackedMsg, end) && - push8(value->pdsch_re_mapping_atenna_ports, ppWritePackedMsg, end) && - push8(value->pdsch_re_mapping_freq_shift, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_dlsch_pdu_rel12_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel12_t *value = (nfapi_dl_config_dlsch_pdu_rel12_t *)tlv; - return( push8(value->altcqi_table_r12, ppWritePackedMsg, end) && - push8(value->maxlayers, ppWritePackedMsg, end) && - push8(value->n_dl_harq, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_dlsch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel13_t *value = (nfapi_dl_config_dlsch_pdu_rel13_t *)tlv; - return( push8(value->dwpts_symbols, ppWritePackedMsg, end) && - push8(value->initial_lbt_sf, ppWritePackedMsg, end) && - push8(value->ue_type, ppWritePackedMsg, end) && - push8(value->pdsch_payload_type, ppWritePackedMsg, end) && - push16(value->initial_transmission_sf_io, ppWritePackedMsg, end) && - push8(value->drms_table_flag, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_pch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_pch_pdu_rel8_t *value = (nfapi_dl_config_pch_pdu_rel8_t *)tlv; - return( push16(value->length, ppWritePackedMsg, end) && - push16(value->pdu_index, ppWritePackedMsg, end) && - push16(value->p_rnti, ppWritePackedMsg, end) && - push8(value->resource_allocation_type, ppWritePackedMsg, end) && - push8(value->virtual_resource_block_assignment_flag, ppWritePackedMsg, end) && - push32(value->resource_block_coding, ppWritePackedMsg, end) && - push8(value->mcs, ppWritePackedMsg, end) && - push8(value->redundancy_version, ppWritePackedMsg, end) && - push8(value->number_of_transport_blocks, ppWritePackedMsg, end) && - push8(value->transport_block_to_codeword_swap_flag, ppWritePackedMsg, end) && - push8(value->transmission_scheme, ppWritePackedMsg, end) && - push8(value->number_of_layers, ppWritePackedMsg, end) && - push8(value->codebook_index, ppWritePackedMsg, end) && - push8(value->ue_category_capacity, ppWritePackedMsg, end) && - push8(value->pa, ppWritePackedMsg, end) && - push16(value->transmission_power, ppWritePackedMsg, end) && - push8(value->nprb, ppWritePackedMsg, end) && - push8(value->ngap, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_pch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_pch_pdu_rel13_t *value = (nfapi_dl_config_pch_pdu_rel13_t *)tlv; - return ( push8(value->ue_mode, ppWritePackedMsg, end) && - push16(value->initial_transmission_sf_io, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_prs_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_prs_pdu_rel9_t *value = (nfapi_dl_config_prs_pdu_rel9_t *)tlv; - return( push16(value->transmission_power, ppWritePackedMsg, end) && - push8(value->prs_bandwidth, ppWritePackedMsg, end) && - push8(value->prs_cyclic_prefix_type, ppWritePackedMsg, end) && - push8(value->prs_muting, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_csi_rs_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_csi_rs_pdu_rel10_t *value = (nfapi_dl_config_csi_rs_pdu_rel10_t *)tlv; - return( push8(value->csi_rs_antenna_port_count_r10, ppWritePackedMsg, end) && - push8(value->csi_rs_resource_config_r10, ppWritePackedMsg, end) && - push16(value->transmission_power, ppWritePackedMsg, end) && - push16(value->csi_rs_zero_tx_power_resource_config_bitmap_r10, ppWritePackedMsg, end) && - push8(value->csi_rs_number_of_nzp_configuration, ppWritePackedMsg, end) && - pusharray8(value->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, value->csi_rs_number_of_nzp_configuration, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_csi_rs_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_csi_rs_pdu_rel13_t *value = (nfapi_dl_config_csi_rs_pdu_rel13_t *)tlv; - - if(!(push8(value->csi_rs_class, ppWritePackedMsg, end) && - push8(value->cdm_type, ppWritePackedMsg, end) && - push8(value->num_bf_vector, ppWritePackedMsg, end))) { - return 0; - } - - uint16_t i; - - for(i = 0; i < value->num_bf_vector; ++i) { - if(!(push8(value->bf_vector[i].csi_rs_resource_index, ppWritePackedMsg, end) && - pusharray16(value->bf_vector[i].bf_value, NFAPI_MAX_ANTENNA_PORT_COUNT, NFAPI_MAX_ANTENNA_PORT_COUNT, ppWritePackedMsg, end))) - return 0; - } - - return 1; -} -static uint8_t pack_bf_vector(nfapi_bf_vector_t *vector, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( push8(vector->subband_index, ppWritePackedMsg, end) && - push8(vector->num_antennas, ppWritePackedMsg, end) && - pusharray16(vector->bf_value, NFAPI_MAX_NUM_ANTENNAS, vector->num_antennas, ppWritePackedMsg, end)); -} - -static uint8_t pack_dl_config_epdcch_parameters_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_epdcch_parameters_rel11_t *value = (nfapi_dl_config_epdcch_parameters_rel11_t *)tlv; - return ( push8(value->epdcch_resource_assignment_flag, ppWritePackedMsg, end) && - push16(value->epdcch_id, ppWritePackedMsg, end) && - push8(value->epdcch_start_symbol, ppWritePackedMsg, end) && - push8(value->epdcch_num_prb, ppWritePackedMsg, end) && - pusharray8(value->epdcch_prb_index, NFAPI_MAX_EPDCCH_PRB, value->epdcch_num_prb, ppWritePackedMsg, end) && - pack_bf_vector(&value->bf_vector, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_epdcch_parameters_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_epdcch_parameters_rel13_t *value = (nfapi_dl_config_epdcch_parameters_rel13_t *)tlv; - return (push8(value->dwpts_symbols, ppWritePackedMsg, end) && - push8(value->initial_lbt_sf, ppWritePackedMsg, end)); -} -static uint8_t pack_dl_config_mpdcch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_mpdcch_pdu_rel13_t *value = (nfapi_dl_config_mpdcch_pdu_rel13_t *)tlv; - return ( push8(value->mpdcch_narrow_band, ppWritePackedMsg, end) && - push8(value->number_of_prb_pairs, ppWritePackedMsg, end) && - push8(value->resource_block_assignment, ppWritePackedMsg, end) && - push8(value->mpdcch_tansmission_type, ppWritePackedMsg, end) && - push8(value->start_symbol, ppWritePackedMsg, end) && - push8(value->ecce_index, ppWritePackedMsg, end) && - push8(value->aggregation_level, ppWritePackedMsg, end) && - push8(value->rnti_type, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push8(value->ce_mode, ppWritePackedMsg, end) && - push16(value->drms_scrambling_init, ppWritePackedMsg, end) && - push16(value->initial_transmission_sf_io, ppWritePackedMsg, end) && - push16(value->transmission_power, ppWritePackedMsg, end) && - push8(value->dci_format, ppWritePackedMsg, end) && - push16(value->resource_block_coding, ppWritePackedMsg, end) && - push8(value->mcs, ppWritePackedMsg, end) && - push8(value->pdsch_reptition_levels, ppWritePackedMsg, end) && - push8(value->redundancy_version, ppWritePackedMsg, end) && - push8(value->new_data_indicator, ppWritePackedMsg, end) && - push8(value->harq_process, ppWritePackedMsg, end) && - push8(value->tpmi_length, ppWritePackedMsg, end) && - push8(value->tpmi, ppWritePackedMsg, end) && - push8(value->pmi_flag, ppWritePackedMsg, end) && - push8(value->pmi, ppWritePackedMsg, end) && - push8(value->harq_resource_offset, ppWritePackedMsg, end) && - push8(value->dci_subframe_repetition_number, ppWritePackedMsg, end) && - push8(value->tpc, ppWritePackedMsg, end) && - push8(value->downlink_assignment_index_length, ppWritePackedMsg, end) && - push8(value->downlink_assignment_index, ppWritePackedMsg, end) && - push8(value->allocate_prach_flag, ppWritePackedMsg, end) && - push8(value->preamble_index, ppWritePackedMsg, end) && - push8(value->prach_mask_index, ppWritePackedMsg, end) && - push8(value->starting_ce_level, ppWritePackedMsg, end) && - push8(value->srs_request, ppWritePackedMsg, end) && - push8(value->antenna_ports_and_scrambling_identity_flag, ppWritePackedMsg, end) && - push8(value->antenna_ports_and_scrambling_identity, ppWritePackedMsg, end) && - push8(value->frequency_hopping_enabled_flag, ppWritePackedMsg, end) && - push8(value->paging_direct_indication_differentiation_flag, ppWritePackedMsg, end) && - push8(value->direct_indication, ppWritePackedMsg, end) && - push8(value->total_dci_length_including_padding, ppWritePackedMsg, end) && - push8(value->number_of_tx_antenna_ports, ppWritePackedMsg, end) && - pusharray16(value->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, value->number_of_tx_antenna_ports, ppWritePackedMsg, end)); -} - - -static uint8_t pack_dl_config_nbch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_nbch_pdu_rel13_t *value = (nfapi_dl_config_nbch_pdu_rel13_t *)tlv; - return (push16(value->length, ppWritePackedMsg, end) && - push16(value->pdu_index, ppWritePackedMsg, end) && - push16(value->transmission_power, ppWritePackedMsg, end) && - push16(value->hyper_sfn_2_lsbs, ppWritePackedMsg, end)); -} - - -static uint8_t pack_dl_config_npdcch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_npdcch_pdu_rel13_t *value = (nfapi_dl_config_npdcch_pdu_rel13_t *)tlv; - return (push16(value->length, ppWritePackedMsg, end) && - push16(value->pdu_index, ppWritePackedMsg, end) && - push8(value->ncce_index, ppWritePackedMsg, end) && - push8(value->aggregation_level, ppWritePackedMsg, end) && - push8(value->start_symbol, ppWritePackedMsg, end) && - push8(value->rnti_type, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push8(value->scrambling_reinitialization_batch_index, ppWritePackedMsg, end) && - push8(value->nrs_antenna_ports_assumed_by_the_ue, ppWritePackedMsg, end) && - push8(value->dci_format, ppWritePackedMsg, end) && - push8(value->scheduling_delay, ppWritePackedMsg, end) && - push8(value->resource_assignment, ppWritePackedMsg, end) && - push8(value->repetition_number, ppWritePackedMsg, end) && - push8(value->mcs, ppWritePackedMsg, end) && - push8(value->new_data_indicator, ppWritePackedMsg, end) && - push8(value->harq_ack_resource, ppWritePackedMsg, end) && - push8(value->npdcch_order_indication, ppWritePackedMsg, end) && - push8(value->starting_number_of_nprach_repetitions, ppWritePackedMsg, end) && - push8(value->subcarrier_indication_of_nprach, ppWritePackedMsg, end) && - push8(value->paging_direct_indication_differentation_flag, ppWritePackedMsg, end) && - push8(value->direct_indication, ppWritePackedMsg, end) && - push8(value->dci_subframe_repetition_number, ppWritePackedMsg, end) && - push8(value->total_dci_length_including_padding, ppWritePackedMsg, end)); -} - -static uint8_t pack_dl_config_ndlsch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_ndlsch_pdu_rel13_t *value = (nfapi_dl_config_ndlsch_pdu_rel13_t *)tlv; - return (push16(value->length, ppWritePackedMsg, end) && - push16(value->pdu_index, ppWritePackedMsg, end) && - push8(value->start_symbol, ppWritePackedMsg, end) && - push8(value->rnti_type, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push16(value->resource_assignment, ppWritePackedMsg, end) && - push16(value->repetition_number, ppWritePackedMsg, end) && - push8(value->modulation, ppWritePackedMsg, end) && - push8(value->number_of_subframes_for_resource_assignment, ppWritePackedMsg, end) && - push8(value->scrambling_sequence_initialization_cinit, ppWritePackedMsg, end) && - push16(value->sf_idx, ppWritePackedMsg, end) && - push8(value->nrs_antenna_ports_assumed_by_the_ue, ppWritePackedMsg, end)); -} - - -static uint8_t pack_dl_tti_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_request_pdu_t *value = (nfapi_nr_dl_tti_request_pdu_t *)tlv; - - if(!(push16(value->PDUSize, ppWritePackedMsg, end) && - push16(value->PDUType, ppWritePackedMsg, end) )) - return 0; - - // first match the pdu type, then call the respective function - switch(value->PDUType) { - case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: { - if(!(pack_dl_tti_csi_rs_pdu_rel15_value(&value->csi_rs_pdu.csi_rs_pdu_rel15,ppWritePackedMsg,end))) - return 0; - } - break; - - case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: { - if(!(pack_dl_tti_pdcch_pdu_rel15_value(&value->pdcch_pdu.pdcch_pdu_rel15,ppWritePackedMsg,end))) - return 0; - } - break; - - case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: { - if(!(pack_dl_tti_pdsch_pdu_rel15_value(&value->pdsch_pdu.pdsch_pdu_rel15,ppWritePackedMsg,end))) - return 0; - } - break; - - case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: { - if(!(pack_dl_tti_ssb_pdu_rel15_value(&value->ssb_pdu.ssb_pdu_rel15,ppWritePackedMsg,end))) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid DL_TTI pdu type %d \n", value->PDUType ); - } - break; - } - - return 1; -} - -static uint8_t pack_dl_config_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_dl_config_request_body_t *value = (nfapi_dl_config_request_body_t *)tlv; - - //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() dci:%u pdu:%u pdsch:%u rnti:%u pcfich:%u\n", __FUNCTION__, value->number_dci, value->number_pdu, value->number_pdsch_rnti, value->transmission_power_pcfich); - - if(!(push8(value->number_pdcch_ofdm_symbols, ppWritePackedMsg, end) && - push8(value->number_dci, ppWritePackedMsg, end) && - push16(value->number_pdu, ppWritePackedMsg, end) && - push8(value->number_pdsch_rnti, ppWritePackedMsg, end) && - push16(value->transmission_power_pcfich, ppWritePackedMsg, end))) { - return 0; - } - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_pdu; - - for(; i < total_number_of_pdus; ++i) { - nfapi_dl_config_request_pdu_t *pdu = &(value->dl_config_pdu_list[i]); - - if(push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) - return 0; - - // Put a 0 size in and then determine the size after the pdu - // has been writen and write the calculated size - uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; - pdu->pdu_size = 0; - - if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) - return 0; - - switch(pdu->pdu_type) { - case NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE: { - //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE\n", __FUNCTION__); - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL8_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel8_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL9_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel9_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL10_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel10_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL11_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel11, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel11_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL12_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel12, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel12_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL13_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel13_value))) { - return 0; - } - } - break; - - case NFAPI_DL_CONFIG_BCH_PDU_TYPE: { - //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() NFAPI_DL_CONFIG_BCH_PDU_TYPE\n", __FUNCTION__); - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_BCH_PDU_REL8_TAG, &pdu->bch_pdu.bch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_bch_pdu_rel8_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_MCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_MCH_PDU_REL8_TAG, &pdu->mch_pdu.mch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_mch_pdu_rel8_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_DLSCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL8_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel8_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL9_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel9_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL10_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel10_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL11_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel11, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel11_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL12_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel12, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel12_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL13_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_PCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL8_TAG, &pdu->pch_pdu.pch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_pch_pdu_rel8_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL13_TAG, &pdu->pch_pdu.pch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_pch_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_PRS_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_PRS_PDU_REL9_TAG, &pdu->prs_pdu.prs_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_prs_pdu_rel9_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_CSI_RS_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL10_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_csi_rs_pdu_rel10_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL13_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_csi_rs_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_EPDCCH_DL_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL8_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel8_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL9_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel9_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL10_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel10_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL11_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel11, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel11_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL12_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel12, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel12_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL13_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel13_value) && - pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL11_TAG, &pdu->epdcch_pdu.epdcch_params_rel11, ppWritePackedMsg, end, &pack_dl_config_epdcch_parameters_rel11_value) & - pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL13_TAG, &pdu->epdcch_pdu.epdcch_params_rel13, ppWritePackedMsg, end, &pack_dl_config_epdcch_parameters_rel13_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_MPDCCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_MPDCCH_PDU_REL13_TAG, &pdu->mpdcch_pdu.mpdcch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_mpdcch_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_NBCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_NBCH_PDU_REL13_TAG, &pdu->nbch_pdu.nbch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_nbch_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_NPDCCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_NPDCCH_PDU_REL13_TAG, &pdu->npdcch_pdu.npdcch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_npdcch_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_DL_CONFIG_NDLSCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_NDLSCH_PDU_REL13_TAG, &pdu->ndlsch_pdu.ndlsch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_ndlsch_pdu_rel13_value))) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); - } - break; - }; - - // add 1 for the pdu_type. The delta will include the pdu_size - pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); - - push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); - } - - return 1; -} - - -static uint8_t pack_dl_tti_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nr_dl_tti_request_t *pNfapiMsg = (nfapi_nr_dl_tti_request_t *)msg; - - if (!(push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && - push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && - push8(pNfapiMsg->dl_tti_request_body.nGroup, ppWritePackedMsg, end) && - push8(pNfapiMsg->dl_tti_request_body.nPDUs, ppWritePackedMsg, end) && - pusharray8(pNfapiMsg->dl_tti_request_body.nUe,256,pNfapiMsg->dl_tti_request_body.nGroup, ppWritePackedMsg, end) - //pusharray8(pNfapiMsg->PduIdx[0] ,256,256, ppWritePackedMsg, end) - )) - return 0; - - int arr[12]; - - for(int i=0; idl_tti_request_body.nGroup; i++) { - for(int j=0; jdl_tti_request_body.nUe[i]; j++) { - arr[j] = pNfapiMsg->dl_tti_request_body.PduIdx[i][j]; - } - - if(!(pusharrays32(arr,12,pNfapiMsg->dl_tti_request_body.nUe[i],ppWritePackedMsg, end))) - return 0; - } - - for(int i=0; idl_tti_request_body.nPDUs; i++) { - if(!pack_dl_tti_request_body_value(&pNfapiMsg->dl_tti_request_body.dl_tti_pdu_list[i],ppWritePackedMsg,end)) - return 0; - } - - return 1; -} - - -static uint8_t pack_dl_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_dl_config_request_t *pNfapiMsg = (nfapi_dl_config_request_t *)msg; - //return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - //pack_tlv(NFAPI_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->dl_config_request_body, ppWritePackedMsg, end, &pack_dl_config_request_body_value) && - //pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); - { - uint8_t x = push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end); - uint8_t y = pack_tlv(NFAPI_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->dl_config_request_body, ppWritePackedMsg, end, &pack_dl_config_request_body_value); - uint8_t z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); - - if (!x || !y || !z) { - NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() NFAPI_DL_CONFIG_REQUEST x:%u y:%u z:%u \n", __FUNCTION__,x,y,z); - } - - return x && y && z; - } -} - - - - -static uint8_t pack_ul_config_request_ulsch_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel8_t *ulsch_pdu_rel8 = (nfapi_ul_config_ulsch_pdu_rel8_t *)tlv; - return( push32(ulsch_pdu_rel8->handle, ppWritePackedMsg, end) && - push16(ulsch_pdu_rel8->size, ppWritePackedMsg, end) && - push16(ulsch_pdu_rel8->rnti, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->resource_block_start, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->number_of_resource_blocks, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->modulation_type, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->cyclic_shift_2_for_drms, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->frequency_hopping_enabled_flag, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->frequency_hopping_bits, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->new_data_indication, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->redundancy_version, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->harq_process_number, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->ul_tx_mode, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->current_tx_nb, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel8->n_srs, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_ulsch_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel10_t *ulsch_pdu_rel10 = (nfapi_ul_config_ulsch_pdu_rel10_t *)tlv; - return (push8(ulsch_pdu_rel10->resource_allocation_type, ppWritePackedMsg, end) && - push32(ulsch_pdu_rel10->resource_block_coding, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel10->transport_blocks, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel10->transmission_scheme, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel10->number_of_layers, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel10->codebook_index, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel10->disable_sequence_hopping_flag, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_ulsch_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel11_t *ulsch_pdu_rel11 = (nfapi_ul_config_ulsch_pdu_rel11_t *)tlv; - return (push8(ulsch_pdu_rel11->virtual_cell_id_enabled_flag, ppWritePackedMsg, end) && - push16(ulsch_pdu_rel11->npusch_identity, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel11->dmrs_config_flag, ppWritePackedMsg, end) && - push16(ulsch_pdu_rel11->ndmrs_csh_identity, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_ulsch_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel13_t *ulsch_pdu_rel13 = (nfapi_ul_config_ulsch_pdu_rel13_t *)tlv; - return (push8(ulsch_pdu_rel13->ue_type, ppWritePackedMsg, end) && - push16(ulsch_pdu_rel13->total_number_of_repetitions, ppWritePackedMsg, end) && - push16(ulsch_pdu_rel13->repetition_number, ppWritePackedMsg, end) && - push16(ulsch_pdu_rel13->initial_transmission_sf_io, ppWritePackedMsg, end) && - push8(ulsch_pdu_rel13->empty_symbols_due_to_re_tunning, ppWritePackedMsg, end)); -} - -//Pack fns for ul_tti PDUS - - -static uint8_t pack_ul_tti_request_prach_pdu(nfapi_nr_prach_pdu_t *prach_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { - return( - push16(prach_pdu->phys_cell_id, ppWritePackedMsg, end) && - push8(prach_pdu->num_prach_ocas, ppWritePackedMsg, end) && - push8(prach_pdu->prach_format, ppWritePackedMsg, end) && - push8(prach_pdu->num_ra, ppWritePackedMsg, end) && - push8(prach_pdu->prach_start_symbol, ppWritePackedMsg, end) && - push16(prach_pdu->num_cs, ppWritePackedMsg, end) - // TODO: ignoring beamforming tlv for now - ); -} - -static uint8_t pack_ul_tti_request_pucch_pdu(nfapi_nr_pucch_pdu_t *pucch_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { - return( - push16(pucch_pdu->rnti, ppWritePackedMsg, end) && - push32(pucch_pdu->handle, ppWritePackedMsg, end) && - push16(pucch_pdu->bwp_size, ppWritePackedMsg, end) && - push16(pucch_pdu->bwp_start, ppWritePackedMsg, end) && - push8(pucch_pdu->subcarrier_spacing, ppWritePackedMsg, end) && - push8(pucch_pdu->cyclic_prefix, ppWritePackedMsg, end) && - push8(pucch_pdu->format_type, ppWritePackedMsg, end) && - push8(pucch_pdu->multi_slot_tx_indicator, ppWritePackedMsg, end) && - push16(pucch_pdu->prb_start, ppWritePackedMsg, end) && - push16(pucch_pdu->prb_size, ppWritePackedMsg, end) && - push8(pucch_pdu->start_symbol_index, ppWritePackedMsg, end) && - push8(pucch_pdu->nr_of_symbols, ppWritePackedMsg, end) && - push8(pucch_pdu->freq_hop_flag, ppWritePackedMsg, end) && - push16(pucch_pdu->second_hop_prb, ppWritePackedMsg, end) && - push8(pucch_pdu->group_hop_flag, ppWritePackedMsg, end) && - push8(pucch_pdu->sequence_hop_flag, ppWritePackedMsg, end) && - push16(pucch_pdu->hopping_id, ppWritePackedMsg, end) && - push16(pucch_pdu->initial_cyclic_shift, ppWritePackedMsg, end) && - push16(pucch_pdu->data_scrambling_id, ppWritePackedMsg, end) && - push8(pucch_pdu->time_domain_occ_idx, ppWritePackedMsg, end) && - push8(pucch_pdu->pre_dft_occ_idx, ppWritePackedMsg, end) && - push8(pucch_pdu->pre_dft_occ_len, ppWritePackedMsg, end) && - push8(pucch_pdu->add_dmrs_flag, ppWritePackedMsg, end) && - push16(pucch_pdu->dmrs_scrambling_id, ppWritePackedMsg, end) && - push8(pucch_pdu->dmrs_cyclic_shift, ppWritePackedMsg, end) && - push8(pucch_pdu->sr_flag, ppWritePackedMsg, end) && - push8(pucch_pdu->bit_len_harq, ppWritePackedMsg, end) && - push16(pucch_pdu->bit_len_csi_part1, ppWritePackedMsg, end) && - push16(pucch_pdu->bit_len_csi_part2, ppWritePackedMsg, end) - // TODO: ignoring beamforming tlv for now - ); -} - - -static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { - if (!( - push16(pusch_pdu->pdu_bit_map, ppWritePackedMsg, end) && - push16(pusch_pdu->rnti, ppWritePackedMsg, end) && - push32(pusch_pdu->handle, ppWritePackedMsg, end) && - push16(pusch_pdu->bwp_size, ppWritePackedMsg, end) && - push16(pusch_pdu->bwp_start, ppWritePackedMsg, end) && - push8(pusch_pdu->subcarrier_spacing, ppWritePackedMsg, end) && - push8(pusch_pdu->cyclic_prefix, ppWritePackedMsg, end) && - push16(pusch_pdu->target_code_rate, ppWritePackedMsg, end) && - push8(pusch_pdu->qam_mod_order, ppWritePackedMsg, end) && - push8(pusch_pdu->mcs_index, ppWritePackedMsg, end) && - push8(pusch_pdu->mcs_table, ppWritePackedMsg, end) && - push8(pusch_pdu->transform_precoding, ppWritePackedMsg, end) && - push16(pusch_pdu->data_scrambling_id, ppWritePackedMsg, end) && - push8(pusch_pdu->nrOfLayers, ppWritePackedMsg, end) && - push16(pusch_pdu->ul_dmrs_symb_pos, ppWritePackedMsg, end) && - push8(pusch_pdu->dmrs_config_type, ppWritePackedMsg, end) && - push16(pusch_pdu->ul_dmrs_scrambling_id, ppWritePackedMsg, end) && - push8(pusch_pdu->scid, ppWritePackedMsg, end) && - push8(pusch_pdu->num_dmrs_cdm_grps_no_data, ppWritePackedMsg, end) && - push16(pusch_pdu->dmrs_ports, ppWritePackedMsg, end) && - push8(pusch_pdu->resource_alloc, ppWritePackedMsg, end) && - push8(pusch_pdu->resource_alloc,ppWritePackedMsg, end) && - push16(pusch_pdu->dmrs_ports, ppWritePackedMsg, end) && - push16(pusch_pdu->rb_start, ppWritePackedMsg, end) && - push16(pusch_pdu->rb_size, ppWritePackedMsg, end) && - push8(pusch_pdu->vrb_to_prb_mapping, ppWritePackedMsg, end) && - push8(pusch_pdu->frequency_hopping, ppWritePackedMsg, end) && - push16(pusch_pdu->tx_direct_current_location, ppWritePackedMsg, end) && - push8(pusch_pdu->uplink_frequency_shift_7p5khz, ppWritePackedMsg, end) && - push8(pusch_pdu->start_symbol_index, ppWritePackedMsg, end) && - push8(pusch_pdu->nr_of_symbols, ppWritePackedMsg, end) - // TODO: ignoring beamforming tlv for now - )) - return 0; - - //Pack Optional Data only included if indicated in pduBitmap - switch(pusch_pdu->pdu_bit_map) { - case PUSCH_PDU_BITMAP_PUSCH_DATA: { - // pack optional TLVs - return( - push8(pusch_pdu->pusch_data.rv_index, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_data.harq_process_id, ppWritePackedMsg, end) && - push32(pusch_pdu->pusch_data.tb_size, ppWritePackedMsg, end) && - push16(pusch_pdu->pusch_data.num_cb, ppWritePackedMsg, end) && - pusharray8(pusch_pdu->pusch_data.cb_present_and_position,1,1,ppWritePackedMsg, end) - ); - } - break; - - case PUSCH_PDU_BITMAP_PUSCH_UCI: { - return( - push16(pusch_pdu->pusch_uci.harq_ack_bit_length, ppWritePackedMsg, end) && - push16(pusch_pdu->pusch_uci.csi_part1_bit_length, ppWritePackedMsg, end) && - push16(pusch_pdu->pusch_uci.csi_part2_bit_length, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_uci.alpha_scaling, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_uci.beta_offset_harq_ack, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_uci.beta_offset_csi1, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_uci.beta_offset_csi2, ppWritePackedMsg, end) - ); - } - break; - - case PUSCH_PDU_BITMAP_PUSCH_PTRS: { - return( - push8(pusch_pdu->pusch_ptrs.num_ptrs_ports, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_dmrs_port, ppWritePackedMsg, end) && - push16(pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_port_index, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_ptrs.ptrs_time_density, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_ptrs.ptrs_freq_density, ppWritePackedMsg, end) && - push8(pusch_pdu->pusch_ptrs.ul_ptrs_power, ppWritePackedMsg, end) - ); - } - break; - - case PUSCH_PDU_BITMAP_DFTS_OFDM: { - return( - push8(pusch_pdu->dfts_ofdm.low_papr_group_number, ppWritePackedMsg, end) && - push16(pusch_pdu->dfts_ofdm.low_papr_sequence_number, ppWritePackedMsg, end) && - push8(pusch_pdu->dfts_ofdm.ul_ptrs_sample_density, ppWritePackedMsg, end) && - push8(pusch_pdu->dfts_ofdm.ul_ptrs_time_density_transform_precoding, ppWritePackedMsg, end) - ); - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_INFO, "Invalid pdu bitmap %d \n", pusch_pdu->pdu_bit_map ); - } - } - - return 1; -} - -static uint8_t pack_ul_tti_request_srs_pdu(nfapi_nr_srs_pdu_t *srs_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { - return( - push16(srs_pdu->rnti, ppWritePackedMsg, end) && - push32(srs_pdu->handle, ppWritePackedMsg, end) && - push16(srs_pdu->bwp_size, ppWritePackedMsg, end) && - push16(srs_pdu->bwp_start, ppWritePackedMsg, end) && - push8(srs_pdu->subcarrier_spacing, ppWritePackedMsg, end) && - push8(srs_pdu->cyclic_prefix, ppWritePackedMsg, end) && - push8(srs_pdu->num_ant_ports, ppWritePackedMsg, end) && - push8(srs_pdu->num_symbols, ppWritePackedMsg, end) && - push8(srs_pdu->num_repetitions, ppWritePackedMsg, end) && - push8(srs_pdu->time_start_position, ppWritePackedMsg, end) && - push8(srs_pdu->config_index, ppWritePackedMsg, end) && - push16(srs_pdu->sequence_id, ppWritePackedMsg, end) && - push8(srs_pdu->bandwidth_index, ppWritePackedMsg, end) && - push8(srs_pdu->comb_size, ppWritePackedMsg, end) && - push8(srs_pdu->comb_offset, ppWritePackedMsg, end) && - push8(srs_pdu->cyclic_shift, ppWritePackedMsg, end) && - push8(srs_pdu->frequency_position, ppWritePackedMsg, end) && - push8(srs_pdu->frequency_shift, ppWritePackedMsg, end) && - push8(srs_pdu->frequency_hopping, ppWritePackedMsg, end) && - push8(srs_pdu->group_or_sequence_hopping, ppWritePackedMsg, end) && - push8(srs_pdu->resource_type, ppWritePackedMsg, end) && - push16(srs_pdu->t_srs, ppWritePackedMsg, end) && - push16(srs_pdu->t_offset, ppWritePackedMsg, end) - // TODO: ignoring beamforming tlv for now - ); -} - -static uint8_t pack_ul_config_request_ulsch_pdu(nfapi_ul_config_ulsch_pdu *ulsch_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL8_TAG, &ulsch_pdu->ulsch_pdu_rel8, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel8_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL10_TAG, &ulsch_pdu->ulsch_pdu_rel10, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel10_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL11_TAG, &ulsch_pdu->ulsch_pdu_rel11, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel11_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL13_TAG, &ulsch_pdu->ulsch_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel13_value)); -} - -static uint8_t pack_ul_config_request_cqi_ri_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_ri_information_rel8_t *cqi_ri_info_rel8 = (nfapi_ul_config_cqi_ri_information_rel8_t *)tlv; - return ( push8(cqi_ri_info_rel8->dl_cqi_pmi_size_rank_1, ppWritePackedMsg, end) && - push8(cqi_ri_info_rel8->dl_cqi_pmi_size_rank_greater_1, ppWritePackedMsg, end) && - push8(cqi_ri_info_rel8->ri_size, ppWritePackedMsg, end) && - push8(cqi_ri_info_rel8->delta_offset_cqi, ppWritePackedMsg, end) && - push8(cqi_ri_info_rel8->delta_offset_ri, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_cqi_ri_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_ri_information_rel9_t *cqi_ri_info_rel9 = (nfapi_ul_config_cqi_ri_information_rel9_t *)tlv; - - if(!(push8(cqi_ri_info_rel9->report_type, ppWritePackedMsg, end) && - push8(cqi_ri_info_rel9->delta_offset_cqi, ppWritePackedMsg, end) && - push8(cqi_ri_info_rel9->delta_offset_ri, ppWritePackedMsg, end))) { - return 0; - } - - switch(cqi_ri_info_rel9->report_type) { - case NFAPI_CSI_REPORT_TYPE_PERIODIC: { - if(!(push8(cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size, ppWritePackedMsg, end) && - push8(cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.control_type, ppWritePackedMsg, end))) { - return 0; - } - } - break; - - case NFAPI_CSI_REPORT_TYPE_APERIODIC: { - if(push8(cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc, ppWritePackedMsg, end) == 0) - return 0; - - uint8_t i; - - for(i = 0; i < cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc; ++i) { - if(push8(cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].ri_size, ppWritePackedMsg, end) == 0) - return 0; - - uint8_t j; - - for(j = 0; j < 8; ++j) { - if(push8(cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].dl_cqi_pmi_size[j], ppWritePackedMsg, end) == 0) - return 0; - } - } - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid report type %d \n", cqi_ri_info_rel9->report_type ); - } - break; - }; - - return 1; -} - -static uint8_t pack_ul_config_request_cqi_ri_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_ri_information_rel13_t *cqi_ri_info_rel13 = (nfapi_ul_config_cqi_ri_information_rel13_t *)tlv; - - switch(cqi_ri_info_rel13->report_type) { - case NFAPI_CSI_REPORT_TYPE_PERIODIC: { - if(push16(cqi_ri_info_rel13->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size_2, ppWritePackedMsg, end) == 0) - return 0; - } - break; - - case NFAPI_CSI_REPORT_TYPE_APERIODIC: { - // No parameters - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid report type %d \n", cqi_ri_info_rel13->report_type ); - } - break; - }; - - return 1; -} - -static uint8_t pack_ul_config_request_cqi_ri_information(nfapi_ul_config_cqi_ri_information *cqi_ri_info, uint8_t **ppWritePackedMsg, uint8_t *end) { - return (pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL8_TAG, &cqi_ri_info->cqi_ri_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_cqi_ri_rel8_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL9_TAG, &cqi_ri_info->cqi_ri_information_rel9, ppWritePackedMsg, end, &pack_ul_config_request_cqi_ri_rel9_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL13_TAG, &cqi_ri_info->cqi_ri_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_cqi_ri_rel13_value)); -} - -static uint8_t pack_ul_config_request_init_tx_params_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_initial_transmission_parameters_rel8_t *init_tx_params_rel8 = (nfapi_ul_config_initial_transmission_parameters_rel8_t *)tlv; - return (push8(init_tx_params_rel8->n_srs_initial, ppWritePackedMsg, end) && - push8(init_tx_params_rel8->initial_number_of_resource_blocks, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_initial_transmission_parameters(nfapi_ul_config_initial_transmission_parameters *init_tx_params, uint8_t **ppWritePackedMsg, uint8_t *end) { - return pack_tlv(NFAPI_UL_CONFIG_REQUEST_INITIAL_TRANSMISSION_PARAMETERS_REL8_TAG, &init_tx_params->initial_transmission_parameters_rel8, ppWritePackedMsg, end, - &pack_ul_config_request_init_tx_params_rel8_value); -} - -static uint8_t pack_ul_config_request_ulsch_harq_info_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_harq_information_rel10_t *harq_info_rel10 = (nfapi_ul_config_ulsch_harq_information_rel10_t *)tlv; - return (push8(harq_info_rel10->harq_size, ppWritePackedMsg, end) && - push8(harq_info_rel10->delta_offset_harq, ppWritePackedMsg, end) && - push8(harq_info_rel10->ack_nack_mode, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_ulsch_harq_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_ulsch_harq_information_rel13_t *)tlv; - return (push16(harq_info_rel13->harq_size_2, ppWritePackedMsg, end) && - push8(harq_info_rel13->delta_offset_harq_2, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_ulsch_harq_information(nfapi_ul_config_ulsch_harq_information *harq_info, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL10_TAG, &harq_info->harq_information_rel10, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_harq_info_rel10_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL13_TAG, &harq_info->harq_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_harq_info_rel13_value)); -} - -static uint8_t pack_ul_config_request_ue_info_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ue_information_rel8_t *ue_info_rel8 = (nfapi_ul_config_ue_information_rel8_t *)tlv; - return ( push32(ue_info_rel8->handle, ppWritePackedMsg, end) && - push16(ue_info_rel8->rnti, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_ue_info_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ue_information_rel11_t *ue_info_rel11 = (nfapi_ul_config_ue_information_rel11_t *)tlv; - return ( push8(ue_info_rel11->virtual_cell_id_enabled_flag, ppWritePackedMsg, end) && - push16(ue_info_rel11->npusch_identity, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_ue_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_ue_information_rel13_t *ue_info_rel13 = (nfapi_ul_config_ue_information_rel13_t *)tlv; - return ( push8(ue_info_rel13->ue_type, ppWritePackedMsg, end) && - push8(ue_info_rel13->empty_symbols, ppWritePackedMsg, end) && - push16(ue_info_rel13->total_number_of_repetitions, ppWritePackedMsg, end) && - push16(ue_info_rel13->repetition_number, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_ue_information(nfapi_ul_config_ue_information *ue_info, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL8_TAG, &ue_info->ue_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_ue_info_rel8_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL11_TAG, &ue_info->ue_information_rel11, ppWritePackedMsg, end, &pack_ul_config_request_ue_info_rel11_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL13_TAG, &ue_info->ue_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_ue_info_rel13_value)); -} - -static uint8_t pack_ul_config_request_harq_info_rel10_tdd_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel10_tdd_t *harq_info_rel10_tdd = (nfapi_ul_config_harq_information_rel10_tdd_t *)tlv; - return ( push8(harq_info_rel10_tdd->harq_size, ppWritePackedMsg, end) && - push8(harq_info_rel10_tdd->ack_nack_mode, ppWritePackedMsg, end) && - push8(harq_info_rel10_tdd->number_of_pucch_resources, ppWritePackedMsg, end) && - push16(harq_info_rel10_tdd->n_pucch_1_0, ppWritePackedMsg, end) && - push16(harq_info_rel10_tdd->n_pucch_1_1, ppWritePackedMsg, end) && - push16(harq_info_rel10_tdd->n_pucch_1_2, ppWritePackedMsg, end) && - push16(harq_info_rel10_tdd->n_pucch_1_3, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_harq_info_rel8_fdd_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel8_fdd_t *harq_info_rel8_fdd = (nfapi_ul_config_harq_information_rel8_fdd_t *)tlv; - return ( push16(harq_info_rel8_fdd->n_pucch_1_0, ppWritePackedMsg, end) && - push8(harq_info_rel8_fdd->harq_size, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_harq_info_rel9_fdd_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel9_fdd_t *harq_info_rel9_fdd = (nfapi_ul_config_harq_information_rel9_fdd_t *)tlv; - return ( push8(harq_info_rel9_fdd->harq_size, ppWritePackedMsg, end) && - push8(harq_info_rel9_fdd->ack_nack_mode, ppWritePackedMsg, end) && - push8(harq_info_rel9_fdd->number_of_pucch_resources, ppWritePackedMsg, end) && - push16(harq_info_rel9_fdd->n_pucch_1_0, ppWritePackedMsg, end) && - push16(harq_info_rel9_fdd->n_pucch_1_1, ppWritePackedMsg, end) && - push16(harq_info_rel9_fdd->n_pucch_1_2, ppWritePackedMsg, end) && - push16(harq_info_rel9_fdd->n_pucch_1_3, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_harq_info_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel11_t *harq_info_rel11 = (nfapi_ul_config_harq_information_rel11_t *)tlv; - return ( push8(harq_info_rel11->num_ant_ports, ppWritePackedMsg, end) && - push16(harq_info_rel11->n_pucch_2_0, ppWritePackedMsg, end) && - push16(harq_info_rel11->n_pucch_2_1, ppWritePackedMsg, end) && - push16(harq_info_rel11->n_pucch_2_2, ppWritePackedMsg, end) && - push16(harq_info_rel11->n_pucch_2_3, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_harq_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_harq_information_rel13_t *)tlv; - return ( push16(harq_info_rel13->harq_size_2, ppWritePackedMsg, end) && - push8(harq_info_rel13->starting_prb, ppWritePackedMsg, end) && - push8(harq_info_rel13->n_prb, ppWritePackedMsg, end) && - push8(harq_info_rel13->cdm_index, ppWritePackedMsg, end) && - push8(harq_info_rel13->n_srs, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_harq_information(nfapi_ul_config_harq_information *harq_info, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL10_TDD_TAG, &harq_info->harq_information_rel10_tdd, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel10_tdd_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL8_FDD_TAG, &harq_info->harq_information_rel8_fdd, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel8_fdd_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL9_FDD_TAG, &harq_info->harq_information_rel9_fdd, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel9_fdd_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL11_TAG, &harq_info->harq_information_rel11, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel11_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL13_TAG, &harq_info->harq_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel13_value)); -} - -static uint8_t pack_ul_config_request_cqi_info_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_information_rel8_t *cqi_info_rel8 = (nfapi_ul_config_cqi_information_rel8_t *)tlv; - return ( push16(cqi_info_rel8->pucch_index, ppWritePackedMsg, end) && - push8(cqi_info_rel8->dl_cqi_pmi_size, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_cqi_info_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_information_rel10_t *cqi_info_rel10 = (nfapi_ul_config_cqi_information_rel10_t *)tlv; - return ( push8(cqi_info_rel10->number_of_pucch_resource, ppWritePackedMsg, end) && - push16(cqi_info_rel10->pucch_index_p1, ppWritePackedMsg, end)); -} -static uint8_t pack_ul_config_request_cqi_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_information_rel13_t *cqi_info_rel13 = (nfapi_ul_config_cqi_information_rel13_t *)tlv; - return ( push8(cqi_info_rel13->csi_mode, ppWritePackedMsg, end) && - push16(cqi_info_rel13->dl_cqi_pmi_size_2, ppWritePackedMsg, end) && - push8(cqi_info_rel13->starting_prb, ppWritePackedMsg, end) && - push8(cqi_info_rel13->n_prb, ppWritePackedMsg, end) && - push8(cqi_info_rel13->cdm_index, ppWritePackedMsg, end) && - push8(cqi_info_rel13->n_srs, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_cqi_information(nfapi_ul_config_cqi_information *cqi_info, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL8_TAG, &cqi_info->cqi_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_cqi_info_rel8_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL10_TAG, &cqi_info->cqi_information_rel10, ppWritePackedMsg, end, &pack_ul_config_request_cqi_info_rel10_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL13_TAG, &cqi_info->cqi_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_cqi_info_rel13_value)); -} - -static uint8_t pack_ul_config_request_sr_info_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_sr_information_rel8_t *sr_info_rel8 = (nfapi_ul_config_sr_information_rel8_t *)tlv; - return push16(sr_info_rel8->pucch_index, ppWritePackedMsg, end); -} -static uint8_t pack_ul_config_request_sr_info_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_sr_information_rel10_t *sr_info_rel10 = (nfapi_ul_config_sr_information_rel10_t *)tlv; - return ( push8(sr_info_rel10->number_of_pucch_resources, ppWritePackedMsg, end) && - push16(sr_info_rel10->pucch_index_p1, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_sr_information(nfapi_ul_config_sr_information *sr_info, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL8_TAG, &sr_info->sr_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_sr_info_rel8_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL10_TAG, &sr_info->sr_information_rel10, ppWritePackedMsg, end, &pack_ul_config_request_sr_info_rel10_value)); -} - -static uint8_t pack_ul_config_request_srs_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_srs_pdu_rel8_t *srs_pdu_rel8 = (nfapi_ul_config_srs_pdu_rel8_t *)tlv; - return (push32(srs_pdu_rel8->handle, ppWritePackedMsg, end) && - push16(srs_pdu_rel8->size, ppWritePackedMsg, end) && - push16(srs_pdu_rel8->rnti, ppWritePackedMsg, end) && - push8(srs_pdu_rel8->srs_bandwidth, ppWritePackedMsg, end) && - push8(srs_pdu_rel8->frequency_domain_position, ppWritePackedMsg, end) && - push8(srs_pdu_rel8->srs_hopping_bandwidth, ppWritePackedMsg, end) && - push8(srs_pdu_rel8->transmission_comb, ppWritePackedMsg, end) && - push16(srs_pdu_rel8->i_srs, ppWritePackedMsg, end) && - push8(srs_pdu_rel8->sounding_reference_cyclic_shift, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_srs_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_srs_pdu_rel10_t *srs_pdu_rel10 = (nfapi_ul_config_srs_pdu_rel10_t *)tlv; - return push8(srs_pdu_rel10->antenna_port, ppWritePackedMsg, end); -} - -static uint8_t pack_ul_config_request_srs_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_srs_pdu_rel13_t *srs_pdu_rel13 = (nfapi_ul_config_srs_pdu_rel13_t *)tlv; - return ( push8(srs_pdu_rel13->number_of_combs, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_nb_harq_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_nb_harq_information_rel13_fdd_t *nb_harq_pdu_rel13 = (nfapi_ul_config_nb_harq_information_rel13_fdd_t *)tlv; - return ( push8(nb_harq_pdu_rel13->harq_ack_resource, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_config_request_nulsch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_nulsch_pdu_rel13_t *nulsch_pdu_rel13 = (nfapi_ul_config_nulsch_pdu_rel13_t *)tlv; - return (push8(nulsch_pdu_rel13->nulsch_format, ppWritePackedMsg, end) && - push32(nulsch_pdu_rel13->handle, ppWritePackedMsg, end) && - push16(nulsch_pdu_rel13->size, ppWritePackedMsg, end) && - push16(nulsch_pdu_rel13->rnti, ppWritePackedMsg, end) && - push8(nulsch_pdu_rel13->subcarrier_indication, ppWritePackedMsg, end) && - push8(nulsch_pdu_rel13->resource_assignment, ppWritePackedMsg, end) && - push8(nulsch_pdu_rel13->mcs, ppWritePackedMsg, end) && - push8(nulsch_pdu_rel13->redudancy_version, ppWritePackedMsg, end) && - push8(nulsch_pdu_rel13->repetition_number, ppWritePackedMsg, end) && - push8(nulsch_pdu_rel13->new_data_indication, ppWritePackedMsg, end) && - push8(nulsch_pdu_rel13->n_srs, ppWritePackedMsg, end) && - push16(nulsch_pdu_rel13->scrambling_sequence_initialization_cinit, ppWritePackedMsg, end) && - push16(nulsch_pdu_rel13->sf_idx, ppWritePackedMsg, end) && - pack_ul_config_request_ue_information(&(nulsch_pdu_rel13->ue_information), ppWritePackedMsg, end) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_NB_HARQ_INFORMATION_REL13_FDD_TAG, &nulsch_pdu_rel13->nb_harq_information.nb_harq_information_rel13_fdd, ppWritePackedMsg, end, - &pack_ul_config_request_nb_harq_rel13_value)); -} -static uint8_t pack_ul_config_request_nrach_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_nrach_pdu_rel13_t *nrach_pdu_rel13 = (nfapi_ul_config_nrach_pdu_rel13_t *)tlv; - return ( push8(nrach_pdu_rel13->nprach_config_0, ppWritePackedMsg, end) && - push8(nrach_pdu_rel13->nprach_config_1, ppWritePackedMsg, end) && - push8(nrach_pdu_rel13->nprach_config_2, ppWritePackedMsg, end)); -} - - - -static uint8_t pack_ul_tti_pdu_list_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_ul_tti_request_number_of_pdus_t *value = (nfapi_nr_ul_tti_request_number_of_pdus_t *)tlv; - - if(!(push16(value->pdu_size, ppWritePackedMsg, end) && - push16(value->pdu_type, ppWritePackedMsg, end) )) - return 0; - - // first match the pdu type, then call the respective function - switch(value->pdu_type) { - case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: { - if(!pack_ul_tti_request_prach_pdu(&value->prach_pdu, ppWritePackedMsg, end)) - return 0; - } - break; - - case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE: { - if(!pack_ul_tti_request_pucch_pdu(&value->pucch_pdu, ppWritePackedMsg, end)) - return 0; - } - break; - - case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: { - if(!pack_ul_tti_request_pusch_pdu(&value->pusch_pdu, ppWritePackedMsg, end)) - return 0; - } - break; - - case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: { - if(!pack_ul_tti_request_srs_pdu(&value->srs_pdu, ppWritePackedMsg, end)) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid UL_TTI pdu type %d \n", value->pdu_type ); - } - break; - } - - return 1; -} - -static uint8_t pack_ul_tti_groups_list_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_ul_tti_request_number_of_groups_t *value = (nfapi_nr_ul_tti_request_number_of_groups_t *)tlv; - - if(!push8(value->n_ue, ppWritePackedMsg, end)) - return 0; - - for(int i=0; in_ue; i++) { - if(!push8(value->ue_list[i].pdu_idx, ppWritePackedMsg, end)) - return 0; - } - - return 1; -} - -static uint8_t pack_ul_config_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_config_request_body_t *value = (nfapi_ul_config_request_body_t *)tlv; - - if(!(push8(value->number_of_pdus, ppWritePackedMsg, end) && - push8(value->rach_prach_frequency_resources, ppWritePackedMsg, end) && - push8(value->srs_present, ppWritePackedMsg, end))) - return 0; - - uint16_t i = 0; - - for(i = 0; i < value->number_of_pdus; ++i) { - nfapi_ul_config_request_pdu_t *pdu = &(value->ul_config_pdu_list[i]); - - if(push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) - return 0; - - // Put a 0 size in and then determine the size after the pdu - // has been writen and write the calculated size - uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; - pdu->pdu_size = 0; - - if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) - return 0; - - switch(pdu->pdu_type) { - case NFAPI_UL_CONFIG_ULSCH_PDU_TYPE: { - if(!pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_pdu), ppWritePackedMsg, end)) - return 0; - } - break; - - case NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE: { - if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_cqi_ri_pdu.ulsch_pdu), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_ri_information(&(pdu->ulsch_cqi_ri_pdu.cqi_ri_information), ppWritePackedMsg, end) && - pack_ul_config_request_initial_transmission_parameters(&(pdu->ulsch_cqi_ri_pdu.initial_transmission_parameters), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE: { - if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_harq_pdu.ulsch_pdu), ppWritePackedMsg, end) && - pack_ul_config_request_ulsch_harq_information(&(pdu->ulsch_harq_pdu.harq_information), ppWritePackedMsg, end) && - pack_ul_config_request_initial_transmission_parameters(&(pdu->ulsch_harq_pdu.initial_transmission_parameters), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE: { - if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_ri_information(&(pdu->ulsch_cqi_harq_ri_pdu.cqi_ri_information), ppWritePackedMsg, end) && - pack_ul_config_request_ulsch_harq_information(&(pdu->ulsch_cqi_harq_ri_pdu.harq_information), ppWritePackedMsg, end) && - pack_ul_config_request_initial_transmission_parameters(&(pdu->ulsch_cqi_harq_ri_pdu.initial_transmission_parameters), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_pdu.ue_information), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_information(&(pdu->uci_cqi_pdu.cqi_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->uci_sr_pdu.ue_information), ppWritePackedMsg, end) && - pack_ul_config_request_sr_information(&(pdu->uci_sr_pdu.sr_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->uci_harq_pdu.ue_information), ppWritePackedMsg, end) && - pack_ul_config_request_harq_information(&(pdu->uci_harq_pdu.harq_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->uci_sr_harq_pdu.ue_information), ppWritePackedMsg, end) && - pack_ul_config_request_sr_information(&(pdu->uci_sr_harq_pdu.sr_information), ppWritePackedMsg, end) && - pack_ul_config_request_harq_information(&(pdu->uci_sr_harq_pdu.harq_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_harq_pdu.ue_information), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_information(&(pdu->uci_cqi_harq_pdu.cqi_information), ppWritePackedMsg, end) && - pack_ul_config_request_harq_information(&(pdu->uci_cqi_harq_pdu.harq_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_sr_pdu.ue_information), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_information(&(pdu->uci_cqi_sr_pdu.cqi_information), ppWritePackedMsg, end) && - pack_ul_config_request_sr_information(&(pdu->uci_cqi_sr_pdu.sr_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_sr_harq_pdu.ue_information), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_information(&(pdu->uci_cqi_sr_harq_pdu.cqi_information), ppWritePackedMsg, end) && - pack_ul_config_request_sr_information(&(pdu->uci_cqi_sr_harq_pdu.sr_information), ppWritePackedMsg, end) && - pack_ul_config_request_harq_information(&(pdu->uci_cqi_sr_harq_pdu.harq_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_SRS_PDU_TYPE: { - if(!(pack_tlv(NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL8_TAG, &pdu->srs_pdu.srs_pdu_rel8, ppWritePackedMsg, end, &pack_ul_config_request_srs_pdu_rel8_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL10_TAG, &pdu->srs_pdu.srs_pdu_rel10, ppWritePackedMsg, end, &pack_ul_config_request_srs_pdu_rel10_value) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL13_TAG, &pdu->srs_pdu.srs_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_srs_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_HARQ_BUFFER_PDU_TYPE: { - if(!(pack_ul_config_request_ue_information(&(pdu->harq_buffer_pdu.ue_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_ULSCH_UCI_CSI_PDU_TYPE: { - if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_uci_csi_pdu.ulsch_pdu), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_information(&(pdu->ulsch_uci_csi_pdu.csi_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE: { - if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_uci_harq_pdu.ulsch_pdu), ppWritePackedMsg, end) && - pack_ul_config_request_harq_information(&(pdu->ulsch_uci_harq_pdu.harq_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE: { - if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_csi_uci_harq_pdu.ulsch_pdu), ppWritePackedMsg, end) && - pack_ul_config_request_cqi_information(&(pdu->ulsch_csi_uci_harq_pdu.csi_information), ppWritePackedMsg, end) && - pack_ul_config_request_harq_information(&(pdu->ulsch_csi_uci_harq_pdu.harq_information), ppWritePackedMsg, end))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_NULSCH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_UL_CONFIG_REQUEST_NULSCH_PDU_REL13_TAG, &pdu->nulsch_pdu.nulsch_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_nulsch_pdu_rel13_value))) - return 0; - } - break; - - case NFAPI_UL_CONFIG_NRACH_PDU_TYPE: { - if(!(pack_tlv(NFAPI_UL_CONFIG_REQUEST_NRACH_PDU_REL13_TAG, &pdu->nrach_pdu.nrach_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_nrach_pdu_rel13_value))) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); - } - break; - }; - - // add 1 for the pdu_type. The delta will include the pdu_size - pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); - - push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); - } - - return 1; -} - - -static uint8_t pack_ul_tti_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nr_ul_tti_request_t *pNfapiMsg = (nfapi_nr_ul_tti_request_t *)msg; - - if (!(push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && - push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && - push8(pNfapiMsg->n_pdus, ppWritePackedMsg, end) && - push8(pNfapiMsg->rach_present, ppWritePackedMsg, end) && - push8(pNfapiMsg->n_ulsch, ppWritePackedMsg, end) && - push8(pNfapiMsg->n_ulcch, ppWritePackedMsg, end) && - push8(pNfapiMsg->n_group, ppWritePackedMsg, end) )) - return 0; - - for(int i=0; in_pdus; i++) { - if(!pack_ul_tti_pdu_list_value(&pNfapiMsg->pdus_list[i], ppWritePackedMsg, end)) - return 0; - } - - for(int i=0; in_group; i++) { - if(!pack_ul_tti_groups_list_value(&pNfapiMsg->groups_list[i], ppWritePackedMsg, end)) - return 0; - } - - return 1; -} - - -static uint8_t pack_ul_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_ul_config_request_t *pNfapiMsg = (nfapi_ul_config_request_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_UL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->ul_config_request_body, ppWritePackedMsg, end, &pack_ul_config_request_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)) ; -} - -static uint8_t pack_hi_dci0_hi_rel8_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_hi_pdu_rel8_t *hi_pdu_rel8 = (nfapi_hi_dci0_hi_pdu_rel8_t *)tlv; - return ( push8(hi_pdu_rel8->resource_block_start, ppWritePackedMsg, end) && - push8(hi_pdu_rel8->cyclic_shift_2_for_drms, ppWritePackedMsg, end) && - push8(hi_pdu_rel8->hi_value, ppWritePackedMsg, end) && - push8(hi_pdu_rel8->i_phich, ppWritePackedMsg, end) && - push16(hi_pdu_rel8->transmission_power, ppWritePackedMsg, end)); -} - -static uint8_t pack_hi_dci0_hi_rel10_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_hi_pdu_rel10_t *hi_pdu_rel10 = (nfapi_hi_dci0_hi_pdu_rel10_t *)tlv; - return ( push8(hi_pdu_rel10->flag_tb2, ppWritePackedMsg, end) && - push8(hi_pdu_rel10->hi_value_2, ppWritePackedMsg, end)); -} - -static uint8_t pack_hi_dci0_dci_rel8_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_dci_pdu_rel8_t *dci_pdu_rel8 = (nfapi_hi_dci0_dci_pdu_rel8_t *)tlv; - return ( push8(dci_pdu_rel8->dci_format, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->cce_index, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->aggregation_level, ppWritePackedMsg, end) && - push16(dci_pdu_rel8->rnti, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->resource_block_start, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->number_of_resource_block, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->mcs_1, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->cyclic_shift_2_for_drms, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->frequency_hopping_enabled_flag, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->frequency_hopping_bits, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->new_data_indication_1, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->ue_tx_antenna_seleciton, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->tpc, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->cqi_csi_request, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->ul_index, ppWritePackedMsg, end) && - push8(dci_pdu_rel8->dl_assignment_index, ppWritePackedMsg, end) && - push32(dci_pdu_rel8->tpc_bitmap, ppWritePackedMsg, end) && - push16(dci_pdu_rel8->transmission_power, ppWritePackedMsg, end)); -} - -static uint8_t pack_hi_dci0_dci_rel10_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_dci_pdu_rel10_t *dci_pdu_rel10 = (nfapi_hi_dci0_dci_pdu_rel10_t *)tlv; - return ( push8(dci_pdu_rel10->cross_carrier_scheduling_flag, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->carrier_indicator, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->size_of_cqi_csi_feild, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->srs_flag, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->srs_request, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->resource_allocation_flag, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->resource_allocation_type, ppWritePackedMsg, end) && - push32(dci_pdu_rel10->resource_block_coding, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->mcs_2, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->new_data_indication_2, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->number_of_antenna_ports, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->tpmi, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->total_dci_length_including_padding, ppWritePackedMsg, end) && - push8(dci_pdu_rel10->n_ul_rb, ppWritePackedMsg, end)); -} - -static uint8_t pack_hi_dci0_dci_rel12_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_dci_pdu_rel12_t *dci_pdu_rel12 = (nfapi_hi_dci0_dci_pdu_rel12_t *)tlv; - return ( push8(dci_pdu_rel12->pscch_resource, ppWritePackedMsg, end) && - push8(dci_pdu_rel12->time_resource_pattern, ppWritePackedMsg, end)); -} - -static uint8_t pack_hi_dci0_mpdcch_dci_rel13_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *mpdcch_dci_pdu_rel13 = (nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *)tlv; - return ( push8(mpdcch_dci_pdu_rel13->mpdcch_narrowband, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->number_of_prb_pairs, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->resource_block_assignment, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->mpdcch_transmission_type, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->start_symbol, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->ecce_index, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->aggreagation_level, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->rnti_type, ppWritePackedMsg, end) && - push16(mpdcch_dci_pdu_rel13->rnti, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->ce_mode, ppWritePackedMsg, end) && - push16(mpdcch_dci_pdu_rel13->drms_scrambling_init, ppWritePackedMsg, end) && - push16(mpdcch_dci_pdu_rel13->initial_transmission_sf_io, ppWritePackedMsg, end) && - push16(mpdcch_dci_pdu_rel13->transmission_power, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->dci_format, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->resource_block_start, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->number_of_resource_blocks, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->mcs, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->pusch_repetition_levels, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->frequency_hopping_flag, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->new_data_indication, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->harq_process, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->redudency_version, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->tpc, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->csi_request, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->ul_inex, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->dai_presence_flag, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->dl_assignment_index, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->srs_request, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->dci_subframe_repetition_number, ppWritePackedMsg, end) && - push32(mpdcch_dci_pdu_rel13->tcp_bitmap, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->total_dci_length_include_padding, ppWritePackedMsg, end) && - push8(mpdcch_dci_pdu_rel13->number_of_tx_antenna_ports, ppWritePackedMsg, end) && - pusharray16(mpdcch_dci_pdu_rel13->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, mpdcch_dci_pdu_rel13->number_of_tx_antenna_ports, ppWritePackedMsg, end)); -} - -static uint8_t pack_hi_dci0_npdcch_dci_rel13_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *npdcch_dci_pdu_rel13 = (nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *)tlv; - return ( push8(npdcch_dci_pdu_rel13->ncce_index, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->aggregation_level, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->start_symbol, ppWritePackedMsg, end) && - push16(npdcch_dci_pdu_rel13->rnti, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->scrambling_reinitialization_batch_index, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->nrs_antenna_ports_assumed_by_the_ue, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->subcarrier_indication, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->resource_assignment, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->scheduling_delay, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->mcs, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->redudancy_version, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->repetition_number, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->new_data_indicator, ppWritePackedMsg, end) && - push8(npdcch_dci_pdu_rel13->dci_subframe_repetition_number, ppWritePackedMsg, end)); -} - - -static uint8_t pack_hi_dci0_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_hi_dci0_request_body_t *value = (nfapi_hi_dci0_request_body_t *)tlv; - - if(!(push16(value->sfnsf, ppWritePackedMsg, end) && - push8(value->number_of_dci, ppWritePackedMsg, end) && - push8(value->number_of_hi, ppWritePackedMsg, end))) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_dci + value->number_of_hi; - - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_hi_dci0_request_pdu_t *pdu = &(value->hi_dci0_pdu_list[i]); - - if(push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) - return 0; - - // Put a 0 size in and then determine the size after the pdu - // has been writen and write the calculated size - uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; - pdu->pdu_size = 0; - - if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) - return 0; - - switch(pdu->pdu_type) { - case NFAPI_HI_DCI0_HI_PDU_TYPE: { - if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_HI_PDU_REL8_TAG, &pdu->hi_pdu.hi_pdu_rel8, ppWritePackedMsg, end, pack_hi_dci0_hi_rel8_pdu_value) && - pack_tlv(NFAPI_HI_DCI0_REQUEST_HI_PDU_REL10_TAG, &pdu->hi_pdu.hi_pdu_rel10, ppWritePackedMsg, end, pack_hi_dci0_hi_rel10_pdu_value))) - return 0; - } - break; - - case NFAPI_HI_DCI0_DCI_PDU_TYPE: { - if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL8_TAG, &pdu->dci_pdu.dci_pdu_rel8, ppWritePackedMsg, end, pack_hi_dci0_dci_rel8_pdu_value) && - pack_tlv(NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL10_TAG, &pdu->dci_pdu.dci_pdu_rel10, ppWritePackedMsg, end, pack_hi_dci0_dci_rel10_pdu_value) && - pack_tlv(NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL12_TAG, &pdu->dci_pdu.dci_pdu_rel12, ppWritePackedMsg, end, pack_hi_dci0_dci_rel12_pdu_value))) - return 0; - } - break; - - case NFAPI_HI_DCI0_EPDCCH_DCI_PDU_TYPE: { - if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL8_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel8, ppWritePackedMsg, end, pack_hi_dci0_dci_rel8_pdu_value) && - pack_tlv(NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL10_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel10, ppWritePackedMsg, end, pack_hi_dci0_dci_rel10_pdu_value) && - pack_tlv(NFAPI_HI_DCI0_REQUEST_EPDCCH_PARAMETERS_REL11_TAG, &pdu->epdcch_dci_pdu.epdcch_parameters_rel11, ppWritePackedMsg, end, pack_dl_config_epdcch_parameters_rel11_value))) - return 0; - } - break; - - case NFAPI_HI_DCI0_MPDCCH_DCI_PDU_TYPE: { - if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_MPDCCH_DCI_PDU_REL13_TAG, &pdu->mpdcch_dci_pdu.mpdcch_dci_pdu_rel13, ppWritePackedMsg, end, pack_hi_dci0_mpdcch_dci_rel13_pdu_value))) - return 0; - } - break; - - case NFAPI_HI_DCI0_NPDCCH_DCI_PDU_TYPE: { - if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_NPDCCH_DCI_PDU_REL13_TAG, &pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13, ppWritePackedMsg, end, pack_hi_dci0_npdcch_dci_rel13_pdu_value))) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); - } - break; - }; - - // add 1 for the pdu_type. The delta will include the pdu_size - pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); - - push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); - } - - return 1; -} - -static uint8_t pack_ul_dci_pdu_list_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_ul_dci_request_pdus_t *value = (nfapi_nr_ul_dci_request_pdus_t *)tlv; - - for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) { - if(!push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].RNTI, ppWritePackedMsg, end) && - push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingId, ppWritePackedMsg, end) && - push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingRNTI, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].CceIndex, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].AggregationLevel, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].beta_PDCCH_1_0, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].powerControlOffsetSS, ppWritePackedMsg, end) && - push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end) && - pusharray8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].Payload, DCI_PAYLOAD_BYTE_LEN, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end)); - - return 0; - } - - return (push16(value->PDUType, ppWritePackedMsg, end) && - push16(value->PDUSize, ppWritePackedMsg, end) && - push16(value->pdcch_pdu.pdcch_pdu_rel15.BWPSize, ppWritePackedMsg, end) && - push16(value->pdcch_pdu.pdcch_pdu_rel15.BWPStart, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.SubcarrierSpacing, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.CyclicPrefix, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.StartSymbolIndex, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.DurationSymbols, ppWritePackedMsg, end) && - pusharray8(value->pdcch_pdu.pdcch_pdu_rel15.FreqDomainResource, 6, 6, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.CceRegMappingType, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.RegBundleSize, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.InterleaverSize, ppWritePackedMsg, end) && - push8(value->pdcch_pdu.pdcch_pdu_rel15.CoreSetType, ppWritePackedMsg, end) && - push16(value->pdcch_pdu.pdcch_pdu_rel15.ShiftIndex, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_dci_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nr_ul_dci_request_t *pNfapiMsg = (nfapi_nr_ul_dci_request_t *)msg; - - if (!(push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && - push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && - push8(pNfapiMsg->numPdus, ppWritePackedMsg, end) - )) - return 0; - - for(int i=0; inumPdus; i++) { - if(!pack_ul_dci_pdu_list_value(&pNfapiMsg->ul_dci_pdu_list[i], ppWritePackedMsg, end)) - return 0; - } - - return 1; -} - - - -static uint8_t pack_hi_dci0_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_hi_dci0_request_t *pNfapiMsg = (nfapi_hi_dci0_request_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_HI_DCI0_REQUEST_BODY_TAG, &pNfapiMsg->hi_dci0_request_body, ppWritePackedMsg, end, &pack_hi_dci0_request_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -//pack_tx_data_pdu_list_value -static uint8_t pack_tx_data_pdu_list_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nr_pdu_t *value = (nfapi_nr_pdu_t *)tlv; - - if(!(push32(value->num_TLV, ppWritePackedMsg, end) && - push16(value->PDU_index, ppWritePackedMsg, end) && - push16(value->PDU_length, ppWritePackedMsg, end) - )) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_tlvs = value->num_TLV; - - for(; i < total_number_of_tlvs; ++i) { - if (!(push16(value->TLVs[i].length, ppWritePackedMsg, end) && - push16(value->TLVs[i].tag, ppWritePackedMsg, end))) - return 0; - - switch(value->TLVs[i].tag) { - case 0: { - if(!pusharray32(value->TLVs[i].value.direct, 16384, value->TLVs[i].length, ppWritePackedMsg, end)) - return 0; - - break; - } - - case 1: { - if(!pusharray32(value->TLVs[i].value.ptr, value->TLVs[i].length, value->TLVs[i].length, ppWritePackedMsg, end)) - return 0; - - break; - } - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid tag value %d \n", value->TLVs[i].tag ); - break; - } - } - } - - return 1; -} - -static uint8_t pack_tx_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_tx_request_body_t *value = (nfapi_tx_request_body_t *)tlv; - - if(push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_pdus; - - for(; i < total_number_of_pdus; ++i) { - nfapi_tx_request_pdu_t *pdu = &(value->tx_pdu_list[i]); - - if(!(push16(pdu->pdu_length, ppWritePackedMsg, end) && - push16(pdu->pdu_index, ppWritePackedMsg, end))) - return 0; - - uint8_t j; - - for(j = 0; j < pdu->num_segments; ++j) { - // Use -1 as it is unbounded - // DJP - does not handle -1 - // DJP - if(pusharray8(pdu->segments[j].segment_data, (uint32_t)(-1), pdu->segments[j].segment_length, ppWritePackedMsg, end) == 0) - int push_ret = pusharray8(pdu->segments[j].segment_data, 65535, pdu->segments[j].segment_length, ppWritePackedMsg, end); - - if (pdu->segments[j].segment_length == 3) { - NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() BCH? segment_data:%x %x %x\n", __FUNCTION__, - pdu->segments[j].segment_data[0], - pdu->segments[j].segment_data[1], - pdu->segments[j].segment_data[2] - ); - } - - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() segment_data:%p segment_length:%u pusharray8()=%d\n", __FUNCTION__, pdu->segments[j].segment_data, pdu->segments[j].segment_length, push_ret); - - if (push_ret == 0) { - return 0; - } - } - } - - return 1; -} - -static uint8_t pack_tx_data_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nr_tx_data_request_t *pNfapiMsg = (nfapi_nr_tx_data_request_t *)msg; - - if (!( - push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && - push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && - push16(pNfapiMsg->Number_of_PDUs, ppWritePackedMsg, end) - )) - return 0; - - for(int i=0; iNumber_of_PDUs; i++) { - if(!pack_tx_data_pdu_list_value(&pNfapiMsg->pdu_list[i], ppWritePackedMsg, end)) - return 0; - } - - return 1; -} - -static uint8_t pack_tx_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_tx_request_t *pNfapiMsg = (nfapi_tx_request_t *)msg; - int x = push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end); - int y = pack_tlv(NFAPI_TX_REQUEST_BODY_TAG, &pNfapiMsg->tx_request_body, ppWritePackedMsg, end, &pack_tx_request_body_value); - int z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() x:%d y:%d z:%d\n", __FUNCTION__, x, y, z); - return x && y && z; -} - -static uint8_t pack_release_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ue_release_request_body_t *value = (nfapi_ue_release_request_body_t *)tlv; - - if(push16(value->number_of_TLVs, ppWritePackedMsg, end) == 0) { - return 0; - } - - uint8_t j; - uint16_t num = value->number_of_TLVs; - - for(j = 0; j < num; ++j) { - if(push16(value->ue_release_request_TLVs_list[j].rnti, ppWritePackedMsg, end) == 0) { - return 0; - } - } - - return 1; -} - -static uint8_t pack_ue_release_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_ue_release_request_t *pNfapiMsg = (nfapi_ue_release_request_t *)msg; - int x = push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end); - int y = pack_tlv(NFAPI_UE_RELEASE_BODY_TAG, &pNfapiMsg->ue_release_request_body, ppWritePackedMsg, end, &pack_release_request_body_value); - int z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); - return x && y && z; -} - -static uint8_t pack_ue_release_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_ue_release_response_t *pNfapiMsg = (nfapi_ue_release_response_t *)msg; - int x = push32(pNfapiMsg->error_code, ppWritePackedMsg, end); - int z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); - return x && z; -} - -static uint8_t pack_rx_ue_information_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_rx_ue_information *value = (nfapi_rx_ue_information *)tlv; - return ( push32(value->handle, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) ); -} - -static uint8_t unpack_rx_ue_information_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_rx_ue_information *value = (nfapi_rx_ue_information *)tlv; - return ( pull32(ppReadPackedMsg, &value->handle, end) && - pull16(ppReadPackedMsg, &value->rnti, end)); -} - -static uint8_t pack_harq_indication_tdd_harq_data_bundling(nfapi_harq_indication_tdd_harq_data_bundling_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( push8(data->value_0, ppWritePackedMsg, end) && - push8(data->value_1, ppWritePackedMsg, end)); -} - -static uint8_t pack_harq_indication_tdd_harq_data_multiplexing(nfapi_harq_indication_tdd_harq_data_multiplexing_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( push8(data->value_0, ppWritePackedMsg, end) && - push8(data->value_1, ppWritePackedMsg, end) && - push8(data->value_2, ppWritePackedMsg, end) && - push8(data->value_3, ppWritePackedMsg, end)); -} - -static uint8_t pack_harq_indication_tdd_harq_data_special_bundling(nfapi_harq_indication_tdd_harq_data_special_bundling_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( push8(data->value_0, ppWritePackedMsg, end) ); -} - -static uint8_t pack_harq_indication_tdd_harq_data(nfapi_harq_indication_tdd_harq_data_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { - return ( push8(data->value_0, ppWritePackedMsg, end) ); -} - -static uint8_t pack_harq_indication_tdd_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_rel8_t *harq_indication_tdd_rel8 = (nfapi_harq_indication_tdd_rel8_t *)tlv; - - if(!(push8(harq_indication_tdd_rel8->mode, ppWritePackedMsg, end) && - push8(harq_indication_tdd_rel8->number_of_ack_nack, ppWritePackedMsg, end))) - return 0; - - uint8_t result = 0; - - switch(harq_indication_tdd_rel8->mode) { - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: - result = pack_harq_indication_tdd_harq_data_bundling(&harq_indication_tdd_rel8->harq_data.bundling, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: - result = pack_harq_indication_tdd_harq_data_multiplexing(&harq_indication_tdd_rel8->harq_data.multiplex, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: - result = pack_harq_indication_tdd_harq_data_special_bundling(&harq_indication_tdd_rel8->harq_data.special_bundling, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: - result = 1; - break; - - default: - // err.... - break; - } - - return result; -} - -static uint8_t pack_harq_indication_tdd_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_rel9_t *harq_indication_tdd_rel9 = (nfapi_harq_indication_tdd_rel9_t *)tlv; - - if(!(push8(harq_indication_tdd_rel9->mode, ppWritePackedMsg, end) && - push8(harq_indication_tdd_rel9->number_of_ack_nack, ppWritePackedMsg, end))) - return 0; - - uint8_t idx; - - for(idx = 0; idx < harq_indication_tdd_rel9->number_of_ack_nack; ++idx) { - uint8_t result = 0; - - switch(harq_indication_tdd_rel9->mode) { - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: - result = pack_harq_indication_tdd_harq_data(&(harq_indication_tdd_rel9->harq_data[idx].bundling), ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel9->harq_data[idx].multiplex, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: - result = pack_harq_indication_tdd_harq_data_special_bundling(&harq_indication_tdd_rel9->harq_data[idx].special_bundling, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel9->harq_data[idx].channel_selection, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel9->harq_data[idx].format_3, ppWritePackedMsg, end); - break; - - default: - // err.... - break; - } - - if(result == 0) - return 0; - } - - return 1; -} - -static uint8_t pack_harq_indication_tdd_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_rel13_t *harq_indication_tdd_rel13 = (nfapi_harq_indication_tdd_rel13_t *)tlv; - - if(!(push8(harq_indication_tdd_rel13->mode, ppWritePackedMsg, end) && - push16(harq_indication_tdd_rel13->number_of_ack_nack, ppWritePackedMsg, end))) - return 0; - - uint8_t idx; - - for(idx = 0; idx < harq_indication_tdd_rel13->number_of_ack_nack; ++idx) { - uint8_t result = 0; - - switch(harq_indication_tdd_rel13->mode) { - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].bundling, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].multiplex, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: - result = pack_harq_indication_tdd_harq_data_special_bundling(&harq_indication_tdd_rel13->harq_data[idx].special_bundling, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].channel_selection, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].format_3, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_4: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].format_4, ppWritePackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_5: - result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].format_5, ppWritePackedMsg, end); - break; - - default: - // err.... - break; - } - - if(result == 0) - return 0; - } - - return 1; -} - -static uint8_t pack_harq_indication_fdd_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_harq_indication_fdd_rel8_t *harq_indication_fdd_rel8 = (nfapi_harq_indication_fdd_rel8_t *)tlv; - return ( push8(harq_indication_fdd_rel8->harq_tb1, ppWritePackedMsg, end) && - push8(harq_indication_fdd_rel8->harq_tb2, ppWritePackedMsg, end)); -} - -static uint8_t pack_harq_indication_fdd_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_harq_indication_fdd_rel9_t *harq_indication_fdd_rel9 = (nfapi_harq_indication_fdd_rel9_t *)tlv; - return ( push8(harq_indication_fdd_rel9->mode, ppWritePackedMsg, end) && - push8(harq_indication_fdd_rel9->number_of_ack_nack, ppWritePackedMsg, end) && - pusharray8(harq_indication_fdd_rel9->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL9_MAX, harq_indication_fdd_rel9->number_of_ack_nack, ppWritePackedMsg, end)); -} - -static uint8_t pack_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_harq_indication_fdd_rel13_t *harq_indication_fdd_rel13 = (nfapi_harq_indication_fdd_rel13_t *)tlv; - return ( push8(harq_indication_fdd_rel13->mode, ppWritePackedMsg, end) && - push16(harq_indication_fdd_rel13->number_of_ack_nack, ppWritePackedMsg, end) && - pusharray8(harq_indication_fdd_rel13->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL13_MAX, harq_indication_fdd_rel13->number_of_ack_nack, ppWritePackedMsg, end)); -} - -static uint8_t pack_ul_cqi_information_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_ul_cqi_information_t *value = (nfapi_ul_cqi_information_t *)tlv; - return ( push8(value->ul_cqi, ppWritePackedMsg, end) && - push8(value->channel, ppWritePackedMsg, end)); -} - -static uint8_t pack_harq_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_harq_indication_body_t *value = (nfapi_harq_indication_body_t *)tlv; - - if(push16(value->number_of_harqs, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_harqs; - - for(; i < total_number_of_pdus; ++i) { - nfapi_harq_indication_pdu_t *pdu = &(value->harq_pdu_list[i]); - uint8_t *instance_length_p = *ppWritePackedMsg; - - if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - return 0; - - if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && - pack_tlv(NFAPI_HARQ_INDICATION_TDD_REL8_TAG, &pdu->harq_indication_tdd_rel8, ppWritePackedMsg, end, pack_harq_indication_tdd_rel8_value) && - pack_tlv(NFAPI_HARQ_INDICATION_TDD_REL9_TAG, &pdu->harq_indication_tdd_rel9, ppWritePackedMsg, end, pack_harq_indication_tdd_rel9_value) && - pack_tlv(NFAPI_HARQ_INDICATION_TDD_REL13_TAG, &pdu->harq_indication_tdd_rel13, ppWritePackedMsg, end, pack_harq_indication_tdd_rel13_value) && - pack_tlv(NFAPI_HARQ_INDICATION_FDD_REL8_TAG, &pdu->harq_indication_fdd_rel8, ppWritePackedMsg, end, pack_harq_indication_fdd_rel8_value) && - pack_tlv(NFAPI_HARQ_INDICATION_FDD_REL9_TAG, &pdu->harq_indication_fdd_rel9, ppWritePackedMsg, end, pack_harq_indication_fdd_rel9_value) && - pack_tlv(NFAPI_HARQ_INDICATION_FDD_REL13_TAG, &pdu->harq_indication_fdd_rel13, ppWritePackedMsg, end, pack_harq_indication_fdd_rel13_value) && - pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - push16(instance_length, &instance_length_p, end); - } - - return 1; -} - -static uint8_t pack_harq_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_harq_indication_t *pNfapiMsg = (nfapi_harq_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->harq_indication_body, ppWritePackedMsg, end, pack_harq_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_crc_indication_rel8_body(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_crc_indication_rel8_t *crc_indication_rel8 = (nfapi_crc_indication_rel8_t *)tlv; - return ( push8(crc_indication_rel8->crc_flag, ppWritePackedMsg, end) ); -} - -static uint8_t pack_crc_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_crc_indication_body_t *value = (nfapi_crc_indication_body_t *)tlv; - - if(push16(value->number_of_crcs, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_crcs; - - for(; i < total_number_of_pdus; ++i) { - nfapi_crc_indication_pdu_t *pdu = &(value->crc_pdu_list[i]); - uint8_t *instance_length_p = *ppWritePackedMsg; - - if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - return 0; - - if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && - pack_tlv(NFAPI_CRC_INDICATION_REL8_TAG, &pdu->crc_indication_rel8, ppWritePackedMsg, end, pack_crc_indication_rel8_body))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - push16(instance_length, &instance_length_p, end); - } - - return 1; -} - -static uint8_t pack_crc_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_crc_indication_t *pNfapiMsg = (nfapi_crc_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_CRC_INDICATION_BODY_TAG, &pNfapiMsg->crc_indication_body, ppWritePackedMsg, end, &pack_crc_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} -static uint8_t pack_rx_indication_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_rx_indication_rel8_t *value = (nfapi_rx_indication_rel8_t *)tlv; - return ( push16(value->length, ppWritePackedMsg, end) && - push16(value->offset, ppWritePackedMsg, end) && - push8(value->ul_cqi, ppWritePackedMsg, end) && - push16(value->timing_advance, ppWritePackedMsg, end)); -} -static uint8_t pack_rx_indication_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_rx_indication_rel9_t *value = (nfapi_rx_indication_rel9_t *)tlv; - return ( push16(value->timing_advance_r9, ppWritePackedMsg, end)); -} - -static uint8_t pack_rx_ulsch_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_rx_indication_body_t *value = (nfapi_rx_indication_body_t *)tlv; - - //printf("RX ULSCH BODY\n"); - - if( push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) - return 0; - - // need to calculate the data offset's. - uint16_t i = 0; - uint16_t offset = 2; // taking into account the number_of_pdus - uint16_t total_number_of_pdus = value->number_of_pdus; - //printf("ULSCH:pdus:%d\n", total_number_of_pdus); - - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); - - if(pdu->rx_ue_information.tl.tag == NFAPI_RX_UE_INFORMATION_TAG) { - //printf("NFAPI_RX_UE_INFORMATION_TAG\n"); - offset += 4 + 6; - } - - if(pdu->rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { - //printf("NFAPI_RX_INDICATION_REL8_TAG\n"); - offset += 4 + 7; - } - - if(pdu->rx_indication_rel9.tl.tag == NFAPI_RX_INDICATION_REL9_TAG) { - //printf("NFAPI_RX_INDICATION_REL9_TAG\n"); - offset += 4 + 2; - } - } - - // Now update the structure to include the offset - for(i =0; i < total_number_of_pdus; ++i) { - nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); - - if(pdu->rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { - if(pdu->rx_indication_rel8.offset == 1) { - pdu->rx_indication_rel8.offset = offset; - offset += pdu->rx_indication_rel8.length; - } - } - } - - // Write out the pdu - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); - - if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && - pack_tlv(NFAPI_RX_INDICATION_REL8_TAG, &pdu->rx_indication_rel8, ppWritePackedMsg, end, pack_rx_indication_rel8_value) && - pack_tlv(NFAPI_RX_INDICATION_REL9_TAG, &pdu->rx_indication_rel9, ppWritePackedMsg, end, pack_rx_indication_rel9_value))) - return 0; - } - - // Write out the pdu data - for(i = 0; i < total_number_of_pdus; ++i) { - uint16_t length = 0; - nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); - - if(pdu->rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { - length = pdu->rx_indication_rel8.length; - } - - if( pusharray8(value->rx_pdu_list[i].data, length, length, ppWritePackedMsg, end) == 0) - return 0; - } - - return 1; -} - - -static uint8_t pack_rx_ulsch_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_rx_indication_t *pNfapiMsg = (nfapi_rx_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_RX_INDICATION_BODY_TAG, &pNfapiMsg->rx_indication_body, ppWritePackedMsg, end, pack_rx_ulsch_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_preamble_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_preamble_pdu_rel8_t *preamble_rel8 = (nfapi_preamble_pdu_rel8_t *)tlv; - return ( push16(preamble_rel8->rnti, ppWritePackedMsg, end) && - push8(preamble_rel8->preamble, ppWritePackedMsg, end) && - push16(preamble_rel8->timing_advance, ppWritePackedMsg, end)); -} -static uint8_t pack_preamble_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_preamble_pdu_rel9_t *preamble_rel9 = (nfapi_preamble_pdu_rel9_t *)tlv; - return ( push16(preamble_rel9->timing_advance_r9, ppWritePackedMsg, end) ); -} -static uint8_t pack_preamble_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_preamble_pdu_rel13_t *preamble_rel13 = (nfapi_preamble_pdu_rel13_t *)tlv; - return ( push8(preamble_rel13->rach_resource_type, ppWritePackedMsg, end) ); -} - -static uint8_t pack_rach_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_rach_indication_body_t *value = (nfapi_rach_indication_body_t *)tlv; - - if( push16(value->number_of_preambles, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_preambles; - - for(; i < total_number_of_pdus; ++i) { - nfapi_preamble_pdu_t *pdu = &(value->preamble_list[i]); - uint8_t *instance_length_p = *ppWritePackedMsg; - - if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - return 0; - - if(!(pack_tlv(NFAPI_PREAMBLE_REL8_TAG, &pdu->preamble_rel8, ppWritePackedMsg, end, pack_preamble_pdu_rel8_value) && - pack_tlv(NFAPI_PREAMBLE_REL9_TAG, &pdu->preamble_rel9, ppWritePackedMsg, end, pack_preamble_pdu_rel9_value) && - pack_tlv(NFAPI_PREAMBLE_REL13_TAG, &pdu->preamble_rel13, ppWritePackedMsg, end, pack_preamble_pdu_rel13_value))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - push16(instance_length, &instance_length_p, end); - } - - return 1; -} - -static uint8_t pack_rach_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_rach_indication_t *pNfapiMsg = (nfapi_rach_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_RACH_INDICATION_BODY_TAG, &pNfapiMsg->rach_indication_body, ppWritePackedMsg, end, pack_rach_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_srs_indication_fdd_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_srs_indication_fdd_rel8_t *srs_pdu_rel8 = (nfapi_srs_indication_fdd_rel8_t *)tlv; - return ( push16(srs_pdu_rel8->doppler_estimation, ppWritePackedMsg, end) && - push16(srs_pdu_rel8->timing_advance, ppWritePackedMsg, end) && - push8(srs_pdu_rel8->number_of_resource_blocks, ppWritePackedMsg, end) && - push8(srs_pdu_rel8->rb_start, ppWritePackedMsg, end) && - pusharray8(srs_pdu_rel8->snr, NFAPI_NUM_RB_MAX, srs_pdu_rel8->number_of_resource_blocks, ppWritePackedMsg, end)); -} - -static uint8_t pack_srs_indication_fdd_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_srs_indication_fdd_rel9_t *srs_pdu_rel9 = (nfapi_srs_indication_fdd_rel9_t *)tlv; - return ( push16(srs_pdu_rel9->timing_advance_r9, ppWritePackedMsg, end) ); -} - -static uint8_t pack_srs_indication_tdd_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_srs_indication_ttd_rel10_t *srs_pdu_rel10 = (nfapi_srs_indication_ttd_rel10_t *)tlv; - return ( push8(srs_pdu_rel10->uppts_symbol, ppWritePackedMsg, end) ); -} - -static uint8_t pack_srs_indication_fdd_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_srs_indication_fdd_rel11_t *srs_pdu_rel11 = (nfapi_srs_indication_fdd_rel11_t *)tlv; - return ( push16(srs_pdu_rel11->ul_rtoa, ppWritePackedMsg, end) ) ; -} - -static uint8_t pack_tdd_channel_measurement_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_tdd_channel_measurement_t *value = (nfapi_tdd_channel_measurement_t *)tlv; - - if(!(push8(value->num_prb_per_subband, ppWritePackedMsg, end) && - push8(value->number_of_subbands, ppWritePackedMsg, end) && - push8(value->num_atennas, ppWritePackedMsg, end))) - return 0; - - uint8_t idx = 0; - - for(idx = 0; idx < value->number_of_subbands; ++idx) { - if(!(push8(value->subands[idx].subband_index, ppWritePackedMsg, end) && - pusharray16(value->subands[idx].channel, NFAPI_MAX_NUM_PHYSICAL_ANTENNAS, value->num_atennas, ppWritePackedMsg, end))) - return 0; - } - - return 1; -} - -static uint8_t pack_srs_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_srs_indication_body_t *value = (nfapi_srs_indication_body_t *)tlv; - - if( push8(value->number_of_ues, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_ues; - - for(; i < total_number_of_pdus; ++i) { - nfapi_srs_indication_pdu_t *pdu = &(value->srs_pdu_list[i]); - uint8_t *instance_length_p = *ppWritePackedMsg; - - if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - return 0; - - if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, &pack_rx_ue_information_value) && - pack_tlv(NFAPI_SRS_INDICATION_FDD_REL8_TAG, &pdu->srs_indication_fdd_rel8, ppWritePackedMsg, end, &pack_srs_indication_fdd_rel8_value) && - pack_tlv(NFAPI_SRS_INDICATION_FDD_REL9_TAG, &pdu->srs_indication_fdd_rel9, ppWritePackedMsg, end, &pack_srs_indication_fdd_rel9_value) && - pack_tlv(NFAPI_SRS_INDICATION_TDD_REL10_TAG, &pdu->srs_indication_tdd_rel10, ppWritePackedMsg, end, &pack_srs_indication_tdd_rel10_value) && - pack_tlv(NFAPI_SRS_INDICATION_FDD_REL11_TAG, &pdu->srs_indication_fdd_rel11, ppWritePackedMsg, end, &pack_srs_indication_fdd_rel11_value) && - pack_tlv(NFAPI_TDD_CHANNEL_MEASUREMENT_TAG, &pdu->tdd_channel_measurement, ppWritePackedMsg, end, &pack_tdd_channel_measurement_value))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - push16(instance_length, &instance_length_p, end); - } - - return 1; -} - -static uint8_t pack_srs_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_srs_indication_t *pNfapiMsg = (nfapi_srs_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_SRS_INDICATION_BODY_TAG, &pNfapiMsg->srs_indication_body, ppWritePackedMsg, end, &pack_srs_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_sr_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_sr_indication_body_t *value = (nfapi_sr_indication_body_t *)tlv; - - if(push16(value->number_of_srs, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_srs; - - for(; i < total_number_of_pdus; ++i) { - nfapi_sr_indication_pdu_t *pdu = &(value->sr_pdu_list[i]); - uint8_t *instance_length_p = *ppWritePackedMsg; - - if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - return 0; - - if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && - pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - push16(instance_length, &instance_length_p, end); - } - - return 1; -} - -static uint8_t pack_sr_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_sr_indication_t *pNfapiMsg = (nfapi_sr_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_SR_INDICATION_BODY_TAG, &pNfapiMsg->sr_indication_body, ppWritePackedMsg, end, &pack_sr_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_cqi_indication_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_cqi_indication_rel8_t *cqi_pdu_rel8 = (nfapi_cqi_indication_rel8_t *)tlv; - return ( push16(cqi_pdu_rel8->length, ppWritePackedMsg, end) && - push16(cqi_pdu_rel8->data_offset, ppWritePackedMsg, end) && - push8(cqi_pdu_rel8->ul_cqi, ppWritePackedMsg, end) && - push8(cqi_pdu_rel8->ri, ppWritePackedMsg, end) && - push16(cqi_pdu_rel8->timing_advance, ppWritePackedMsg, end)); -} - -static uint8_t pack_cqi_indication_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_cqi_indication_rel9_t *cqi_pdu_rel9 = (nfapi_cqi_indication_rel9_t *)tlv; - return ( push16(cqi_pdu_rel9->length, ppWritePackedMsg, end) && - push16(cqi_pdu_rel9->data_offset, ppWritePackedMsg, end) && - push8(cqi_pdu_rel9->ul_cqi, ppWritePackedMsg, end) && - push8(cqi_pdu_rel9->number_of_cc_reported, ppWritePackedMsg, end) && - pusharray8(cqi_pdu_rel9->ri, NFAPI_CC_MAX, cqi_pdu_rel9->number_of_cc_reported, ppWritePackedMsg, end) && - push16(cqi_pdu_rel9->timing_advance, ppWritePackedMsg, end) && - push16(cqi_pdu_rel9->timing_advance_r9, ppWritePackedMsg, end)); -} - -static uint8_t pack_cqi_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_cqi_indication_body_t *value = (nfapi_cqi_indication_body_t *)tlv; - - if( push16(value->number_of_cqis, ppWritePackedMsg, end) == 0) - return 0; - - // need to calculate the data offset's. This very bittle due the hardcoding - // of the sizes. can not use the sizeof as we have an array for the Rel9 - // info - uint16_t i = 0; - uint16_t offset = 2; // taking into account the number_of_cqis - uint16_t total_number_of_pdus = value->number_of_cqis; - - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); - offset += 2; // for the instance length - - if(pdu->rx_ue_information.tl.tag == NFAPI_RX_UE_INFORMATION_TAG) { - offset += 4 + 6; // sizeof(nfapi_rx_ue_information) - sizeof(nfapi_tl_t) - } - - if(pdu->cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { - offset += 4 + 8; - } - - if(pdu->cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { - offset += 4 + 10 + pdu->cqi_indication_rel9.number_of_cc_reported; - } - - if(pdu->ul_cqi_information.tl.tag == NFAPI_UL_CQI_INFORMATION_TAG) { - offset += 4 + 2; - } - } - - // Now update the structure to include the offset - for(i =0; i < total_number_of_pdus; ++i) { - nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); - - if(pdu->cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { - if(pdu->cqi_indication_rel8.data_offset == 1) { - pdu->cqi_indication_rel8.data_offset = offset; - offset += pdu->cqi_indication_rel8.length; - } - } - - if(pdu->cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { - if(pdu->cqi_indication_rel9.data_offset == 1) { - pdu->cqi_indication_rel9.data_offset = offset; - offset += pdu->cqi_indication_rel9.length; - } - } - } - - // Write out the cqi information - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); - uint8_t *instance_length_p = *ppWritePackedMsg; - - if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - return 0; - - if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end,pack_rx_ue_information_value) && - pack_tlv(NFAPI_CQI_INDICATION_REL8_TAG, &pdu->cqi_indication_rel8, ppWritePackedMsg, end, pack_cqi_indication_rel8_value) && - pack_tlv(NFAPI_CQI_INDICATION_REL9_TAG, &pdu->cqi_indication_rel9, ppWritePackedMsg, end, pack_cqi_indication_rel9_value) && - pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - push16(instance_length, &instance_length_p, end); - } - - // Write out the cqi raw data - for(i = 0; i < total_number_of_pdus; ++i) { - uint16_t length = 0; - nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); - - if(pdu->cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { - length = pdu->cqi_indication_rel8.length; - } - - if(pdu->cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { - length = pdu->cqi_indication_rel9.length; - } - - if( pusharray8(value->cqi_raw_pdu_list[i].pdu, NFAPI_CQI_RAW_MAX_LEN, length, ppWritePackedMsg, end) == 0) - return 0; - } - - return 1; -} - -static uint8_t pack_cqi_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_cqi_indication_t *pNfapiMsg = (nfapi_cqi_indication_t *)msg; - //Fixme: allocate some mem to fix pure bug, need to find out proper size - pNfapiMsg->vendor_extension=NULL;//(nfapi_vendor_extension_tlv_t)malloc( sizeof(* pNfapiMsg->vendor_extension)); - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_CQI_INDICATION_BODY_TAG, &pNfapiMsg->cqi_indication_body, ppWritePackedMsg, end, pack_cqi_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_lbt_pdsch_req_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_lbt_pdsch_req_pdu_rel13_t *value = (nfapi_lbt_pdsch_req_pdu_rel13_t *)tlv; - return ( push32(value->handle, ppWritePackedMsg, end) && - push32(value->mp_cca, ppWritePackedMsg, end) && - push32(value->n_cca, ppWritePackedMsg, end) && - push32(value->offset, ppWritePackedMsg, end) && - push32(value->lte_txop_sf, ppWritePackedMsg, end) && - push16(value->txop_sfn_sf_end, ppWritePackedMsg, end) && - push32(value->lbt_mode, ppWritePackedMsg, end)); -} - -static uint8_t pack_lbt_drs_req_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_lbt_drs_req_pdu_rel13_t *value = (nfapi_lbt_drs_req_pdu_rel13_t *)tlv; - return ( push32(value->handle, ppWritePackedMsg, end) && - push32(value->offset, ppWritePackedMsg, end) && - push16(value->sfn_sf_end, ppWritePackedMsg, end) && - push32(value->lbt_mode, ppWritePackedMsg, end)); -} - -static uint8_t pack_lbt_dl_config_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_lbt_dl_config_request_body_t *value = (nfapi_lbt_dl_config_request_body_t *)tlv; - - if( push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_pdus; - - for(; i < total_number_of_pdus; ++i) { - nfapi_lbt_dl_config_request_pdu_t *pdu = &(value->lbt_dl_config_req_pdu_list[i]); - - if( push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) - return 0; - - // Put a 0 size in and then determine the size after the pdu - // has been writen and write the calculated size - uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; - pdu->pdu_size = 0; - - if( push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) - return 0; - - switch(pdu->pdu_type) { - case NFAPI_LBT_DL_CONFIG_REQUEST_PDSCH_PDU_TYPE: { - if( pack_tlv(NFAPI_LBT_PDSCH_REQ_PDU_REL13_TAG, &pdu->lbt_pdsch_req_pdu.lbt_pdsch_req_pdu_rel13, ppWritePackedMsg, end, pack_lbt_pdsch_req_pdu_rel13_value) == 0) - return 0; - } - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST_DRS_PDU_TYPE: { - if(pack_tlv(NFAPI_LBT_DRS_REQ_PDU_REL13_TAG, &pdu->lbt_drs_req_pdu.lbt_drs_req_pdu_rel13, ppWritePackedMsg, end, pack_lbt_drs_req_pdu_rel13_value) == 0) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL_CONFIG.request invalid pdu type %d \n", pdu->pdu_type ); - } - break; - }; - - // add 1 for the pdu_type. The delta will include the pdu_size - pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); - - push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); - } - - return 1; -} - -static uint8_t pack_lbt_pdsch_rsp_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_lbt_pdsch_rsp_pdu_rel13_t *value = (nfapi_lbt_pdsch_rsp_pdu_rel13_t *)tlv; - return ( push32(value->handle, ppWritePackedMsg, end) && - push32(value->result, ppWritePackedMsg, end) && - push32(value->lte_txop_symbols, ppWritePackedMsg, end) && - push32(value->initial_partial_sf, ppWritePackedMsg, end)); -} - -static uint8_t pack_lbt_drs_rsp_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_lbt_drs_rsp_pdu_rel13_t *value = (nfapi_lbt_drs_rsp_pdu_rel13_t *)tlv; - return ( push32(value->handle, ppWritePackedMsg, end) && - push32(value->result, ppWritePackedMsg, end)); -} - -static uint8_t pack_lbt_dl_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_lbt_dl_config_request_t *pNfapiMsg = (nfapi_lbt_dl_config_request_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_LBT_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->lbt_dl_config_request_body, ppWritePackedMsg, end, &pack_lbt_dl_config_request_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_lbt_dl_config_indication_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_lbt_dl_indication_body_t *value = (nfapi_lbt_dl_indication_body_t *)tlv; - - if( push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_pdus = value->number_of_pdus; - - for(; i < total_number_of_pdus; ++i) { - nfapi_lbt_dl_indication_pdu_t *pdu = &(value->lbt_indication_pdu_list[i]); - - if( push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) - return 0; - - // Put a 0 size in and then determine the size after the pdu - // has been writen and write the calculated size - uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; - pdu->pdu_size = 0; - - if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) - return 0; - - switch(pdu->pdu_type) { - case NFAPI_LBT_DL_RSP_PDSCH_PDU_TYPE: { - if( pack_tlv(NFAPI_LBT_PDSCH_RSP_PDU_REL13_TAG, &pdu->lbt_pdsch_rsp_pdu.lbt_pdsch_rsp_pdu_rel13, ppWritePackedMsg, end, pack_lbt_pdsch_rsp_pdu_rel13_value) == 0) - return 0; - } - break; - - case NFAPI_LBT_DL_RSP_DRS_PDU_TYPE: { - if( pack_tlv(NFAPI_LBT_DRS_RSP_PDU_REL13_TAG, &pdu->lbt_drs_rsp_pdu.lbt_drs_rsp_pdu_rel13, ppWritePackedMsg, end, pack_lbt_drs_rsp_pdu_rel13_value) == 0) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL.indication body invalid pdu type %d \n", pdu->pdu_type ); - } - break; - }; - - // add 1 for the pdu_type. The delta will include the pdu_size - pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); - - push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); - } - - return 1; -} - -static uint8_t pack_lbt_dl_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_lbt_dl_indication_t *pNfapiMsg = (nfapi_lbt_dl_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_LBT_DL_INDICATION_BODY_TAG, &pNfapiMsg->lbt_dl_indication_body, ppWritePackedMsg, end, &pack_lbt_dl_config_indication_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nb_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nb_harq_indication_fdd_rel13_t *nb_harq_indication_fdd_rel13 = (nfapi_nb_harq_indication_fdd_rel13_t *)tlv; - return ( push8(nb_harq_indication_fdd_rel13->harq_tb1, ppWritePackedMsg, end) ); -} - -static uint8_t pack_nb_harq_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nb_harq_indication_body_t *value = (nfapi_nb_harq_indication_body_t *)tlv; - - if( push16(value->number_of_harqs, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_harqs = value->number_of_harqs; - - for(; i < total_number_of_harqs; ++i) { - nfapi_nb_harq_indication_pdu_t *pdu = &(value->nb_harq_pdu_list[i]); - uint8_t *instance_length_p = *ppWritePackedMsg; - - if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - return 0; - - if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && - pack_tlv(NFAPI_NB_HARQ_INDICATION_FDD_REL13_TAG, &pdu->nb_harq_indication_fdd_rel13, ppWritePackedMsg, end, pack_nb_harq_indication_fdd_rel13_value) && - pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - push16(instance_length, &instance_length_p, end); - } - - return 1; -} - - -static uint8_t pack_nb_harq_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nb_harq_indication_t *pNfapiMsg = (nfapi_nb_harq_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_NB_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->nb_harq_indication_body, ppWritePackedMsg, end, &pack_nb_harq_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nrach_indication_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nrach_indication_pdu_rel13_t *nrach_indication_fdd_rel13 = (nfapi_nrach_indication_pdu_rel13_t *)tlv; - return ( push16(nrach_indication_fdd_rel13->rnti, ppWritePackedMsg, end) && - push8(nrach_indication_fdd_rel13->initial_sc, ppWritePackedMsg, end) && - push16(nrach_indication_fdd_rel13->timing_advance, ppWritePackedMsg, end) && - push8(nrach_indication_fdd_rel13->nrach_ce_level, ppWritePackedMsg, end)); -} - - -static uint8_t pack_nrach_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { - nfapi_nrach_indication_body_t *value = (nfapi_nrach_indication_body_t *)tlv; - - if( push8(value->number_of_initial_scs_detected, ppWritePackedMsg, end) == 0) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_initial_scs_detected = value->number_of_initial_scs_detected; - - for(; i < total_number_of_initial_scs_detected; ++i) { - nfapi_nrach_indication_pdu_t *pdu = &(value->nrach_pdu_list[i]); - - //uint8_t* instance_length_p = *ppWritePackedMsg; - //if(!push16(pdu->instance_length, ppWritePackedMsg, end)) - // return 0; - - if(!(pack_tlv(NFAPI_NRACH_INDICATION_REL13_TAG, &pdu->nrach_indication_rel13, ppWritePackedMsg, end, pack_nrach_indication_rel13_value))) - return 0; - - // calculate the instance length subtracting the size of the instance - // length feild - //uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; - //push16(instance_length, &instance_length_p, end); - } - - return 1; -} - -static uint8_t pack_nrach_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nrach_indication_t *pNfapiMsg = (nfapi_nrach_indication_t *)msg; - return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && - pack_tlv(NFAPI_NRACH_INDICATION_BODY_TAG, &pNfapiMsg->nrach_indication_body, ppWritePackedMsg, end, &pack_nrach_indication_body_value) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nr_dl_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nr_dl_node_sync_t *pNfapiMsg = (nfapi_nr_dl_node_sync_t *)msg; - return ( push32(pNfapiMsg->t1, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->delta_sfn_slot, ppWritePackedMsg, end) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_dl_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_dl_node_sync_t *pNfapiMsg = (nfapi_dl_node_sync_t *)msg; - return ( push32(pNfapiMsg->t1, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->delta_sfn_sf, ppWritePackedMsg, end) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nr_ul_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nr_ul_node_sync_t *pNfapiMsg = (nfapi_nr_ul_node_sync_t *)msg; - return (push32(pNfapiMsg->t1, ppWritePackedMsg, end) && - push32(pNfapiMsg->t2, ppWritePackedMsg, end) && - push32(pNfapiMsg->t3, ppWritePackedMsg, end) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_ul_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_ul_node_sync_t *pNfapiMsg = (nfapi_ul_node_sync_t *)msg; - return (push32(pNfapiMsg->t1, ppWritePackedMsg, end) && - push32(pNfapiMsg->t2, ppWritePackedMsg, end) && - push32(pNfapiMsg->t3, ppWritePackedMsg, end) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_timing_info(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_timing_info_t *pNfapiMsg = (nfapi_timing_info_t *)msg; - return (push32(pNfapiMsg->last_sfn_sf, ppWritePackedMsg, end) && - push32(pNfapiMsg->time_since_last_timing_info, ppWritePackedMsg, end) && - push32(pNfapiMsg->dl_config_jitter, ppWritePackedMsg, end) && - push32(pNfapiMsg->tx_request_jitter, ppWritePackedMsg, end) && - push32(pNfapiMsg->ul_config_jitter, ppWritePackedMsg, end) && - push32(pNfapiMsg->hi_dci0_jitter, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->dl_config_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->tx_request_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->ul_config_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->hi_dci0_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->dl_config_earliest_arrival, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->tx_request_earliest_arrival, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->ul_config_earliest_arrival, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->hi_dci0_earliest_arrival, ppWritePackedMsg, end) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - -static uint8_t pack_nr_timing_info(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nr_timing_info_t *pNfapiMsg = (nfapi_nr_timing_info_t *)msg; - return (push32(pNfapiMsg->last_sfn, ppWritePackedMsg, end) && - push32(pNfapiMsg->last_slot, ppWritePackedMsg, end) && - push32(pNfapiMsg->time_since_last_timing_info, ppWritePackedMsg, end) && - push32(pNfapiMsg->dl_tti_jitter, ppWritePackedMsg, end) && - push32(pNfapiMsg->tx_data_request_jitter, ppWritePackedMsg, end) && - push32(pNfapiMsg->ul_tti_jitter, ppWritePackedMsg, end) && - push32(pNfapiMsg->ul_dci_jitter, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->dl_tti_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->tx_data_request_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->ul_tti_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->ul_dci_latest_delay, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->dl_tti_earliest_arrival, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->tx_data_request_earliest_arrival, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->ul_tti_earliest_arrival, ppWritePackedMsg, end) && - pushs32(pNfapiMsg->ul_dci_earliest_arrival, ppWritePackedMsg, end) && - pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); -} - - - -// Main pack function - public - -int nfapi_nr_p7_message_pack(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen, nfapi_p7_codec_config_t *config) { - nfapi_p7_message_header_t *pMessageHeader = pMessageBuf; - uint8_t *pWritePackedMessage = pPackedBuf; - uint8_t *pPackedLengthField = &pWritePackedMessage[4]; - uint8_t *end = pPackedBuf + packedBufLen; - - if (pMessageBuf == NULL || pPackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack supplied pointers are null\n"); - return -1; - } - - /* - printf("\n P7 MESSAGE SENT: \n"); - for(int i=0; i< packedBufLen; i++){ - printf("%d", *(uint8_t *)(pMessageBuf + i)); - } - printf("\n"); - */ - // process the header - if(!(push16(pMessageHeader->phy_id, &pWritePackedMessage, end) && - push16(pMessageHeader->message_id, &pWritePackedMessage, end) && - push16(0/*pMessageHeader->message_length*/, &pWritePackedMessage, end) && - push16(pMessageHeader->m_segment_sequence, &pWritePackedMessage, end) && - push32(0/*pMessageHeader->checksum*/, &pWritePackedMessage, end) && - push32(pMessageHeader->transmit_timestamp, &pWritePackedMessage, end))) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack header failed\n"); - return -1; - } - - if (pMessageHeader->message_id != NFAPI_TIMING_INFO) { - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() message_id:0x%04x phy_id:%u m_segment_sequence:%u timestamp:%u\n", __FUNCTION__, pMessageHeader->message_id, pMessageHeader->phy_id, pMessageHeader->m_segment_sequence, pMessageHeader->transmit_timestamp); - } - - // look for the specific message - uint8_t result = 0; - - switch (pMessageHeader->message_id) { - case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST: - result = pack_dl_tti_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST: - result = pack_ul_tti_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST: - result = pack_tx_data_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST: - result = pack_ul_dci_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_UE_RELEASE_REQUEST: - result =pack_ue_release_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_UE_RELEASE_RESPONSE: - result =pack_ue_release_response(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_HARQ_INDICATION: - result = pack_harq_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_CRC_INDICATION: - result = pack_crc_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RX_ULSCH_INDICATION: - //printf("RX ULSCH\n"); - result = pack_rx_ulsch_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RACH_INDICATION: - result = pack_rach_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_SRS_INDICATION: - result = pack_srs_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RX_SR_INDICATION: - result = pack_sr_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RX_CQI_INDICATION: - result = pack_cqi_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST: - result = pack_lbt_dl_config_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_LBT_DL_INDICATION: - result = pack_lbt_dl_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NB_HARQ_INDICATION: - result = pack_nb_harq_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NRACH_INDICATION: - result = pack_nrach_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC: - result = pack_nr_dl_node_sync(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC: - result = pack_nr_ul_node_sync(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_TIMING_INFO: - result = pack_nr_timing_info(pMessageHeader, &pWritePackedMessage, end, config); - break; - - default: { - if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->pack_p7_vendor_extension) { - result = (config->pack_p7_vendor_extension)(pMessageHeader, &pWritePackedMessage, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, pMessageHeader->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); - } - } - break; - } - - if(result == 0) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack failed to pack message\n"); - return -1; - } - - // check for a valid message length - uintptr_t msgHead = (uintptr_t)pPackedBuf; - uintptr_t msgEnd = (uintptr_t)pWritePackedMessage; - uint32_t packedMsgLen = msgEnd - msgHead; - uint16_t packedMsgLen16; - - if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); - return -1; - } else { - packedMsgLen16 = (uint16_t)packedMsgLen; - } - - // Update the message length in the header - pMessageHeader->message_length = packedMsgLen16; - - if(!push16(packedMsgLen16, &pPackedLengthField, end)) - return -1; - - if(1) { - //quick test - if(pMessageHeader->message_length != packedMsgLen) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "nfapi packedMsgLen(%d) != message_length(%d) id %d\n", packedMsgLen, pMessageHeader->message_length, pMessageHeader->message_id); - } - } - - return (packedMsgLen); -} - -int nfapi_p7_message_pack(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen, nfapi_p7_codec_config_t *config) { - nfapi_p7_message_header_t *pMessageHeader = pMessageBuf; - uint8_t *pWritePackedMessage = pPackedBuf; - uint8_t *pPackedLengthField = &pWritePackedMessage[4]; - uint8_t *end = pPackedBuf + packedBufLen; - - if (pMessageBuf == NULL || pPackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack supplied pointers are null\n"); - return -1; - } - - /* - printf("\n P7 MESSAGE SENT: \n"); - for(int i=0; i< packedBufLen; i++){ - printf("%d", *(uint8_t *)(pMessageBuf + i)); - } - printf("\n"); - */ - // process the header - if(!(push16(pMessageHeader->phy_id, &pWritePackedMessage, end) && - push16(pMessageHeader->message_id, &pWritePackedMessage, end) && - push16(0/*pMessageHeader->message_length*/, &pWritePackedMessage, end) && - push16(pMessageHeader->m_segment_sequence, &pWritePackedMessage, end) && - push32(0/*pMessageHeader->checksum*/, &pWritePackedMessage, end) && - push32(pMessageHeader->transmit_timestamp, &pWritePackedMessage, end))) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack header failed\n"); - return -1; - } - - if (pMessageHeader->message_id != NFAPI_TIMING_INFO) { - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() message_id:0x%04x phy_id:%u m_segment_sequence:%u timestamp:%u\n", __FUNCTION__, pMessageHeader->message_id, pMessageHeader->phy_id, pMessageHeader->m_segment_sequence, pMessageHeader->transmit_timestamp); - } - - // look for the specific message - uint8_t result = 0; - - switch (pMessageHeader->message_id) { - case NFAPI_DL_CONFIG_REQUEST: - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() NFAPI_DL_CONFIG_REQUEST\n", __FUNCTION__); - result = pack_dl_config_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_UL_CONFIG_REQUEST: - result = pack_ul_config_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_TX_REQUEST: - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() NFAPI_TX_REQUEST\n", __FUNCTION__); - result = pack_tx_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_HI_DCI0_REQUEST: - result = pack_hi_dci0_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_UE_RELEASE_REQUEST: - result =pack_ue_release_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_UE_RELEASE_RESPONSE: - result =pack_ue_release_response(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_HARQ_INDICATION: - result = pack_harq_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_CRC_INDICATION: - result = pack_crc_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RX_ULSCH_INDICATION: - //printf("RX ULSCH\n"); - result = pack_rx_ulsch_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RACH_INDICATION: - result = pack_rach_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_SRS_INDICATION: - result = pack_srs_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RX_SR_INDICATION: - result = pack_sr_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_RX_CQI_INDICATION: - result = pack_cqi_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST: - result = pack_lbt_dl_config_request(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_LBT_DL_INDICATION: - result = pack_lbt_dl_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NB_HARQ_INDICATION: - result = pack_nb_harq_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_NRACH_INDICATION: - result = pack_nrach_indication(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_DL_NODE_SYNC: - result = pack_dl_node_sync(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_UL_NODE_SYNC: - result = pack_ul_node_sync(pMessageHeader, &pWritePackedMessage, end, config); - break; - - case NFAPI_TIMING_INFO: - result = pack_timing_info(pMessageHeader, &pWritePackedMessage, end, config); - break; - - default: { - if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->pack_p7_vendor_extension) { - result = (config->pack_p7_vendor_extension)(pMessageHeader, &pWritePackedMessage, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, pMessageHeader->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); - } - } - break; - } - - if(result == 0) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack failed to pack message\n"); - return -1; - } - - // check for a valid message length - uintptr_t msgHead = (uintptr_t)pPackedBuf; - uintptr_t msgEnd = (uintptr_t)pWritePackedMessage; - uint32_t packedMsgLen = msgEnd - msgHead; - uint16_t packedMsgLen16; - - if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); - return -1; - } else { - packedMsgLen16 = (uint16_t)packedMsgLen; - } - - // Update the message length in the header - pMessageHeader->message_length = packedMsgLen16; - - if(!push16(packedMsgLen16, &pPackedLengthField, end)) - return -1; - - if(1) { - //quick test - if(pMessageHeader->message_length != packedMsgLen) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "nfapi packedMsgLen(%d) != message_length(%d) id %d\n", packedMsgLen, pMessageHeader->message_length, pMessageHeader->message_id); - } - } - - return (packedMsgLen); -} - -// Unpack routines -// NR: -static uint8_t unpack_dl_tti_csi_rs_pdu_rel15_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *value = (nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *)tlv; - return( - pull16(ppReadPackedMsg, &value->bwp_size, end) && - pull16(ppReadPackedMsg, &value->bwp_start, end) && - pull8(ppReadPackedMsg, &value->subcarrier_spacing, end) && - pull8(ppReadPackedMsg, &value->cyclic_prefix, end) && - pull16(ppReadPackedMsg, &value->start_rb, end) && - pull16(ppReadPackedMsg, &value->nr_of_rbs, end) && - pull8(ppReadPackedMsg, &value->csi_type, end) && - pull8(ppReadPackedMsg, &value->row, end) && - pull16(ppReadPackedMsg, &value->freq_domain, end) && - pull8(ppReadPackedMsg, &value->symb_l0, end) && - pull8(ppReadPackedMsg, &value->symb_l1, end) && - pull8(ppReadPackedMsg, &value->cdm_type, end) && - pull8(ppReadPackedMsg, &value->freq_density, end) && - pull16(ppReadPackedMsg, &value->scramb_id, end) && - pull8(ppReadPackedMsg, &value->power_control_offset, end) && - pull8(ppReadPackedMsg, &value->power_control_offset_ss, end) - ); -} - - -static uint8_t unpack_dl_tti_pdcch_pdu_rel15_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *value = (nfapi_nr_dl_tti_pdcch_pdu_rel15_t *)tlv; - - for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) { - if(!pull16(ppReadPackedMsg, &value->dci_pdu[i].RNTI, end) && - pull16(ppReadPackedMsg, &value->dci_pdu[i].ScramblingId, end) && - pull16(ppReadPackedMsg, &value->dci_pdu[i].ScramblingRNTI, end) && - pull8(ppReadPackedMsg, &value->dci_pdu[i].CceIndex, end) && - pull8(ppReadPackedMsg, &value->dci_pdu[i].AggregationLevel, end) && - pull8(ppReadPackedMsg, &value->dci_pdu[i].beta_PDCCH_1_0, end) && - pull8(ppReadPackedMsg, &value->dci_pdu[i].powerControlOffsetSS, end) && - pull16(ppReadPackedMsg, &value->dci_pdu[i].PayloadSizeBits, end) && - pullarray8(ppReadPackedMsg, value->dci_pdu[i].Payload, DCI_PAYLOAD_BYTE_LEN, value->dci_pdu[i].PayloadSizeBits, end)); - - return 0; - } - - // TODO: resolve the packaging of array (currently sending a single element) - return( - pull16(ppReadPackedMsg, &value->BWPSize, end) && - pull16(ppReadPackedMsg, &value->BWPStart, end) && - pull8(ppReadPackedMsg, &value->SubcarrierSpacing, end) && - pull8(ppReadPackedMsg, &value->CyclicPrefix, end) && - pull8(ppReadPackedMsg, &value->StartSymbolIndex, end) && - pull8(ppReadPackedMsg, &value->DurationSymbols, end) && - pullarray8(ppReadPackedMsg, value->FreqDomainResource, 6, 6, end) && - pull8(ppReadPackedMsg, &value->CceRegMappingType, end) && - pull8(ppReadPackedMsg, &value->RegBundleSize, end) && - pull8(ppReadPackedMsg, &value->InterleaverSize, end) && - pull8(ppReadPackedMsg, &value->CoreSetType, end) && - pull16(ppReadPackedMsg, &value->ShiftIndex, end) && - pull8(ppReadPackedMsg, &value->precoderGranularity, end) && - pull16(ppReadPackedMsg, &value->numDlDci, end)); -} - - -static uint8_t unpack_dl_tti_pdsch_pdu_rel15_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *value = (nfapi_nr_dl_tti_pdsch_pdu_rel15_t *)tlv; - // TODO: resolve the packaging of array (currently sending a single element) - return( - pull16(ppReadPackedMsg, &value->pduBitmap, end) && - pull16(ppReadPackedMsg, &value->rnti, end) && - pull16(ppReadPackedMsg, &value->pduIndex, end) && - pull16(ppReadPackedMsg, &value->BWPSize, end) && - pull16(ppReadPackedMsg, &value->BWPStart, end) && - pull8(ppReadPackedMsg, &value->SubcarrierSpacing, end) && - pull8(ppReadPackedMsg, &value->CyclicPrefix, end) && - pull8(ppReadPackedMsg, &value->NrOfCodewords, end) && - pullarray16(ppReadPackedMsg, value->targetCodeRate, 2, 1, end) && - pullarray8(ppReadPackedMsg, value->qamModOrder, 2, 1, end) && - pullarray8(ppReadPackedMsg, value->mcsIndex, 2, 1, end) && - pullarray8(ppReadPackedMsg, value->mcsTable, 2, 1, end) && - pullarray8(ppReadPackedMsg, value->rvIndex, 2, 1, end) && - pullarray32(ppReadPackedMsg, value->TBSize, 2, 1, end) && - pull16(ppReadPackedMsg, &value->dataScramblingId, end) && - pull8(ppReadPackedMsg, &value->nrOfLayers, end) && - pull8(ppReadPackedMsg, &value->transmissionScheme, end) && - pull8(ppReadPackedMsg, &value->refPoint, end) && - pull16(ppReadPackedMsg, &value->dlDmrsSymbPos, end) && - pull8(ppReadPackedMsg, &value->dmrsConfigType, end) && - pull16(ppReadPackedMsg, &value->dlDmrsScramblingId, end) && - pull8(ppReadPackedMsg, &value->SCID, end) && - pull8(ppReadPackedMsg, &value->numDmrsCdmGrpsNoData, end) && - pull16(ppReadPackedMsg, &value->dmrsPorts, end) && - pull8(ppReadPackedMsg, &value->resourceAlloc, end) && - pull16(ppReadPackedMsg, &value->rbStart, end) && - pull16(ppReadPackedMsg, &value->rbSize, end) && - pull8(ppReadPackedMsg, &value->VRBtoPRBMapping, end) && - pull8(ppReadPackedMsg, &value->StartSymbolIndex, end) && - pull8(ppReadPackedMsg, &value->NrOfSymbols, end) && - pull8(ppReadPackedMsg, &value->PTRSPortIndex, end) && - pull8(ppReadPackedMsg, &value->PTRSTimeDensity, end) && - pull8(ppReadPackedMsg, &value->PTRSFreqDensity, end) && - pull8(ppReadPackedMsg, &value->PTRSReOffset, end) - ); -} - - -static uint8_t unpack_dl_tti_ssb_pdu_rel15_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_dl_tti_ssb_pdu_rel15_t *value = (nfapi_nr_dl_tti_ssb_pdu_rel15_t *)tlv; - return( - pull16(ppReadPackedMsg, &value->PhysCellId, end) && - pull8(ppReadPackedMsg, &value->BetaPss, end) && - pull8(ppReadPackedMsg, &value->SsbBlockIndex, end) && - pull8(ppReadPackedMsg, &value->SsbSubcarrierOffset, end) && - pull16(ppReadPackedMsg, &value->ssbOffsetPointA, end) && - pull8(ppReadPackedMsg, &value->bchPayloadFlag, end) && - pull32(ppReadPackedMsg, &value->bchPayload, end) - // TODO: pack precoding_and_beamforming too - ); -} - - -// LTE: -static uint8_t unpack_dl_config_dci_dl_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel8_t *dci_dl_pdu_rel8 = (nfapi_dl_config_dci_dl_pdu_rel8_t *)tlv; - return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->dci_format, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->cce_idx, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->aggregation_level, end) && - pull16(ppReadPackedMsg, &dci_dl_pdu_rel8->rnti, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->resource_allocation_type, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->virtual_resource_block_assignment_flag, end) && - pull32(ppReadPackedMsg, &dci_dl_pdu_rel8->resource_block_coding, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->mcs_1, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->redundancy_version_1, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->new_data_indicator_1, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->transport_block_to_codeword_swap_flag, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->mcs_2, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->redundancy_version_2, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->new_data_indicator_2, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->harq_process, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->tpmi, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->pmi, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->precoding_information, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->tpc, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->downlink_assignment_index, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->ngap, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->transport_block_size_index, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->downlink_power_offset, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->allocate_prach_flag, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->preamble_index, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->prach_mask_index, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->rnti_type, end) && - pull16(ppReadPackedMsg, &dci_dl_pdu_rel8->transmission_power, end)); -} - -static uint8_t unpack_dl_config_dci_dl_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel9_t *dci_dl_pdu_rel9 = (nfapi_dl_config_dci_dl_pdu_rel9_t *)tlv; - return ( pull8(ppReadPackedMsg, &dci_dl_pdu_rel9->mcch_flag, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel9->mcch_change_notification, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel9->scrambling_identity, end)); -} - -static uint8_t unpack_dl_config_dci_dl_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel10_t *dci_dl_pdu_rel10 = (nfapi_dl_config_dci_dl_pdu_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->cross_carrier_scheduling_flag, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->carrier_indicator, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->srs_flag, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->srs_request, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->antenna_ports_scrambling_and_layers, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->total_dci_length_including_padding, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->n_dl_rb, end)); -} - -static uint8_t unpack_dl_config_dci_dl_pdu_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel11_t *dci_dl_pdu_rel11 = (nfapi_dl_config_dci_dl_pdu_rel11_t *)tlv; - return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel11->harq_ack_resource_offset, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel11->pdsch_re_mapping_quasi_co_location_indicator, end)); -} - -static uint8_t unpack_dl_config_dci_dl_pdu_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel12_t *dci_dl_pdu_rel12 = (nfapi_dl_config_dci_dl_pdu_rel12_t *)tlv; - return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel12->primary_cell_type, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel12->ul_dl_configuration_flag, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel12->number_ul_dl_configurations, end) && - pullarray8(ppReadPackedMsg, dci_dl_pdu_rel12->ul_dl_configuration_indication, NFAPI_MAX_UL_DL_CONFIGURATIONS, dci_dl_pdu_rel12->number_ul_dl_configurations, end)); -} - -static uint8_t unpack_tpm_value(uint8_t **ppReadPackedMsg, nfapi_dl_config_dci_dl_tpm_t *value, uint8_t *end) { - if(!(pull8(ppReadPackedMsg, &value->num_prb_per_subband, end) && - pull8(ppReadPackedMsg, &value->number_of_subbands, end) && - pull8(ppReadPackedMsg, &value->num_antennas, end))) - return 0; - - uint8_t idx = 0; - - for(idx = 0; idx < value->number_of_subbands; ++idx) { - nfapi_dl_config_dci_dl_tpm_subband_info_t *subband_info = &(value->subband_info[idx]); - - if(!(pull8(ppReadPackedMsg, &subband_info->subband_index, end) && - pull8(ppReadPackedMsg, &subband_info->scheduled_ues, end))) - return 0; - - uint8_t antenna_idx = 0; - uint8_t scheduled_ue_idx = 0; - - for(antenna_idx = 0; antenna_idx < value->num_antennas; ++antenna_idx) { - for(scheduled_ue_idx = 0; scheduled_ue_idx < subband_info->scheduled_ues; ++scheduled_ue_idx) { - if(!pull16(ppReadPackedMsg, &(subband_info->precoding_value[antenna_idx][scheduled_ue_idx]), end)) - return 0; - } - } - } - - return 1; -} - - -static uint8_t unpack_dl_config_dci_dl_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dci_dl_pdu_rel13_t *dci_dl_pdu_rel13 = (nfapi_dl_config_dci_dl_pdu_rel13_t *)tlv; - // If the length is greater than 5 then the TPM struct flag and possiably the TPM structure have been - // added - uint8_t tpm_struct_flag_present = dci_dl_pdu_rel13->tl.length > 5; - dci_dl_pdu_rel13->tpm_struct_flag = 0; - return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->laa_end_partial_sf_flag, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->laa_end_partial_sf_configuration, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->initial_lbt_sf, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->codebook_size_determination, end) && - pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->drms_table_flag, end) && - ( (tpm_struct_flag_present == 1) ? pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->tpm_struct_flag, end) : 1) && - ( (tpm_struct_flag_present == 1 && dci_dl_pdu_rel13->tpm_struct_flag == 1) ? unpack_tpm_value(ppReadPackedMsg, &dci_dl_pdu_rel13->tpm, end) : 1)); -} - -static uint8_t unpack_dl_config_bch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_bch_pdu_rel8_t *bch_pdu_rel8 = (nfapi_dl_config_bch_pdu_rel8_t *)tlv; - return ( pull16(ppReadPackedMsg, &bch_pdu_rel8->length, end) && - pull16(ppReadPackedMsg, (uint16_t *)&bch_pdu_rel8->pdu_index, end) && - pull16(ppReadPackedMsg, &bch_pdu_rel8->transmission_power, end)); -} - -static uint8_t unpack_dl_config_mch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_mch_pdu_rel8_t *mch_pdu_rel8 = (nfapi_dl_config_mch_pdu_rel8_t *)tlv; - return (pull16(ppReadPackedMsg, &mch_pdu_rel8->length, end) && - pull16(ppReadPackedMsg, (uint16_t *)&mch_pdu_rel8->pdu_index, end) && - pull16(ppReadPackedMsg, &mch_pdu_rel8->rnti, end) && - pull8(ppReadPackedMsg, &mch_pdu_rel8->resource_allocation_type, end) && - pull32(ppReadPackedMsg, &mch_pdu_rel8->resource_block_coding, end) && - pull8(ppReadPackedMsg, &mch_pdu_rel8->modulation, end) && - pull16(ppReadPackedMsg, &mch_pdu_rel8->transmission_power, end) && - pull16(ppReadPackedMsg, &mch_pdu_rel8->mbsfn_area_id, end)); -} - -static uint8_t unpack_dl_config_dlsch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel8_t *dlsch_pdu_rel8 = (nfapi_dl_config_dlsch_pdu_rel8_t *)tlv; - - if (!(pull16(ppReadPackedMsg, &dlsch_pdu_rel8->length, end) && - pull16(ppReadPackedMsg, (uint16_t *)&dlsch_pdu_rel8->pdu_index, end) && - pull16(ppReadPackedMsg, &dlsch_pdu_rel8->rnti, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->resource_allocation_type, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->virtual_resource_block_assignment_flag, end) && - pull32(ppReadPackedMsg, &dlsch_pdu_rel8->resource_block_coding, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->modulation, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->redundancy_version, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transport_blocks, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transport_block_to_codeword_swap_flag, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transmission_scheme, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->number_of_layers, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->number_of_subbands, end) && - pullarray8(ppReadPackedMsg, dlsch_pdu_rel8->codebook_index, NFAPI_MAX_NUM_SUBBANDS, dlsch_pdu_rel8->number_of_subbands, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->ue_category_capacity, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->pa, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->delta_power_offset_index, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->ngap, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->nprb, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transmission_mode, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->num_bf_prb_per_subband, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->num_bf_vector, end))) - return 0; - - uint16_t j = 0; - - for(j = 0; j < dlsch_pdu_rel8->num_bf_vector; ++j) { - if(!(pull8(ppReadPackedMsg, &dlsch_pdu_rel8->bf_vector[j].subband_index, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel8->bf_vector[j].num_antennas, end) && - pullarray16(ppReadPackedMsg, dlsch_pdu_rel8->bf_vector[j].bf_value, NFAPI_MAX_NUM_ANTENNAS, dlsch_pdu_rel8->bf_vector[j].num_antennas, end))) - return 0; - } - - return 1; -} -static uint8_t unpack_dl_config_dlsch_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel9_t *dlsch_pdu_rel9 = (nfapi_dl_config_dlsch_pdu_rel9_t *)tlv; - return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel9->nscid, end) ); -} -static uint8_t unpack_dl_config_dlsch_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel10_t *dlsch_pdu_rel10 = (nfapi_dl_config_dlsch_pdu_rel10_t *)tlv; - return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_flag, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_resource_config_r10, end) && - pull16(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_zero_tx_power_resource_config_bitmap_r10, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_number_nzp_configuration, end) && - pullarray8(ppReadPackedMsg, dlsch_pdu_rel10->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, dlsch_pdu_rel10->csi_rs_number_nzp_configuration, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel10->pdsch_start, end)) ; -} -static uint8_t unpack_dl_config_dlsch_pdu_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel11_t *dlsch_pdu_rel11 = (nfapi_dl_config_dlsch_pdu_rel11_t *)tlv; - return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel11->drms_config_flag, end) && - pull16(ppReadPackedMsg, &dlsch_pdu_rel11->drms_scrambling, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel11->csi_config_flag, end) && - pull16(ppReadPackedMsg, &dlsch_pdu_rel11->csi_scrambling, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel11->pdsch_re_mapping_flag, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel11->pdsch_re_mapping_atenna_ports, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel11->pdsch_re_mapping_freq_shift, end)); -} -static uint8_t unpack_dl_config_dlsch_pdu_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel12_t *dlsch_pdu_rel12 = (nfapi_dl_config_dlsch_pdu_rel12_t *)tlv; - return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel12->altcqi_table_r12, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel12->maxlayers, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel12->n_dl_harq, end)); -} -static uint8_t unpack_dl_config_dlsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_dlsch_pdu_rel13_t *dlsch_pdu_rel13 = (nfapi_dl_config_dlsch_pdu_rel13_t *)tlv; - return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel13->dwpts_symbols, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel13->initial_lbt_sf, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel13->ue_type, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel13->pdsch_payload_type, end) && - pull16(ppReadPackedMsg, &dlsch_pdu_rel13->initial_transmission_sf_io, end) && - pull8(ppReadPackedMsg, &dlsch_pdu_rel13->drms_table_flag, end)); -} - -static uint8_t unpack_dl_config_pch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_pch_pdu_rel8_t *pch_pdu_rel8 = (nfapi_dl_config_pch_pdu_rel8_t *)tlv; - return ( pull16(ppReadPackedMsg, &pch_pdu_rel8->length, end) && - pull16(ppReadPackedMsg, (uint16_t *)&pch_pdu_rel8->pdu_index, end) && - pull16(ppReadPackedMsg, &pch_pdu_rel8->p_rnti, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->resource_allocation_type, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->virtual_resource_block_assignment_flag, end) && - pull32(ppReadPackedMsg, &pch_pdu_rel8->resource_block_coding, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->mcs, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->redundancy_version, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->number_of_transport_blocks, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->transport_block_to_codeword_swap_flag, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->transmission_scheme, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->number_of_layers, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->codebook_index, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->ue_category_capacity, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->pa, end) && - pull16(ppReadPackedMsg, &pch_pdu_rel8->transmission_power, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->nprb, end) && - pull8(ppReadPackedMsg, &pch_pdu_rel8->ngap, end)); -} -static uint8_t unpack_dl_config_pch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_pch_pdu_rel13_t *pch_pdu_rel13 = (nfapi_dl_config_pch_pdu_rel13_t *)tlv; - return ( pull8(ppReadPackedMsg, &pch_pdu_rel13->ue_mode, end) && - pull16(ppReadPackedMsg, &pch_pdu_rel13->initial_transmission_sf_io, end)); -} - -static uint8_t unpack_dl_config_prs_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_prs_pdu_rel9_t *prs_pdu_rel9 = (nfapi_dl_config_prs_pdu_rel9_t *)tlv; - return ( pull16(ppReadPackedMsg, &prs_pdu_rel9->transmission_power, end) && - pull8(ppReadPackedMsg, &prs_pdu_rel9->prs_bandwidth, end) && - pull8(ppReadPackedMsg, &prs_pdu_rel9->prs_cyclic_prefix_type, end) && - pull8(ppReadPackedMsg, &prs_pdu_rel9->prs_muting, end)); -} - -static uint8_t unpack_dl_config_csi_rs_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_csi_rs_pdu_rel10_t *csi_rs_pdu_rel10 = (nfapi_dl_config_csi_rs_pdu_rel10_t *)tlv; - return ( pull8(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_antenna_port_count_r10, end) && - pull8(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_resource_config_r10, end) && - pull16(ppReadPackedMsg, &csi_rs_pdu_rel10->transmission_power, end) && - pull16(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_zero_tx_power_resource_config_bitmap_r10, end) && - pull8(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_number_of_nzp_configuration, end) && - pullarray8(ppReadPackedMsg, csi_rs_pdu_rel10->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, csi_rs_pdu_rel10->csi_rs_number_of_nzp_configuration, end)); -} - -static uint8_t unpack_dl_config_csi_rs_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_csi_rs_pdu_rel13_t *csi_rs_pdu_rel13 = (nfapi_dl_config_csi_rs_pdu_rel13_t *)tlv; - - if (!(pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->csi_rs_class, end) && - pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->cdm_type, end) && - pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->num_bf_vector, end))) - return 0; - - uint16_t idx =0; - - for(idx = 0; idx < csi_rs_pdu_rel13->num_bf_vector; ++idx) { - if(!(pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->bf_vector[idx].csi_rs_resource_index, end))) - return 0; - - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : HOW TO DECODE BF VALUE \n"); - //pullarray16(ppReadPackedMsg, &csi_rs_pdu_rel13->bf_vector[idx].bf_vector, ??); - } - - return 1; -} - -static uint8_t unpack_dl_config_epdcch_params_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_epdcch_parameters_rel11_t *epdcch_params_rel11 = (nfapi_dl_config_epdcch_parameters_rel11_t *)tlv; - return (pull8(ppReadPackedMsg, &epdcch_params_rel11->epdcch_resource_assignment_flag, end) && - pull16(ppReadPackedMsg, &epdcch_params_rel11->epdcch_id, end) && - pull8(ppReadPackedMsg, &epdcch_params_rel11->epdcch_start_symbol, end) && - pull8(ppReadPackedMsg, &epdcch_params_rel11->epdcch_num_prb, end) && - pullarray8(ppReadPackedMsg, epdcch_params_rel11->epdcch_prb_index, NFAPI_MAX_EPDCCH_PRB, epdcch_params_rel11->epdcch_num_prb, end) && - pull8(ppReadPackedMsg, &epdcch_params_rel11->bf_vector.subband_index, end) && - pull8(ppReadPackedMsg, &epdcch_params_rel11->bf_vector.num_antennas, end) && - pullarray16(ppReadPackedMsg, epdcch_params_rel11->bf_vector.bf_value, NFAPI_MAX_NUM_ANTENNAS, epdcch_params_rel11->bf_vector.num_antennas, end)); -} - -static uint8_t unpack_dl_config_epdcch_params_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_epdcch_parameters_rel13_t *epdcch_params_rel13 = (nfapi_dl_config_epdcch_parameters_rel13_t *)tlv; - return ( pull8(ppReadPackedMsg, &epdcch_params_rel13->dwpts_symbols, end) && - pull8(ppReadPackedMsg, &epdcch_params_rel13->initial_lbt_sf, end)); -} - -static uint8_t unpack_dl_config_mpdcch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_mpdcch_pdu_rel13_t *mpdcch_params_rel13 = (nfapi_dl_config_mpdcch_pdu_rel13_t *)tlv; - return ( pull8(ppReadPackedMsg, &mpdcch_params_rel13->mpdcch_narrow_band, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->number_of_prb_pairs, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->resource_block_assignment, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->mpdcch_tansmission_type, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->start_symbol, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->ecce_index, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->aggregation_level, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->rnti_type, end) && - pull16(ppReadPackedMsg, &mpdcch_params_rel13->rnti, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->ce_mode, end) && - pull16(ppReadPackedMsg, &mpdcch_params_rel13->drms_scrambling_init, end) && - pull16(ppReadPackedMsg, &mpdcch_params_rel13->initial_transmission_sf_io, end) && - pull16(ppReadPackedMsg, &mpdcch_params_rel13->transmission_power, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->dci_format, end) && - pull16(ppReadPackedMsg, &mpdcch_params_rel13->resource_block_coding, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->mcs, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->pdsch_reptition_levels, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->redundancy_version, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->new_data_indicator, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->harq_process, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->tpmi_length, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->tpmi, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->pmi_flag, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->pmi, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->harq_resource_offset, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->dci_subframe_repetition_number, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->tpc, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->downlink_assignment_index_length, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->downlink_assignment_index, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->allocate_prach_flag, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->preamble_index, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->prach_mask_index, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->starting_ce_level, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->srs_request, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->antenna_ports_and_scrambling_identity_flag, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->antenna_ports_and_scrambling_identity, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->frequency_hopping_enabled_flag, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->paging_direct_indication_differentiation_flag, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->direct_indication, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->total_dci_length_including_padding, end) && - pull8(ppReadPackedMsg, &mpdcch_params_rel13->number_of_tx_antenna_ports, end) && - pullarray16(ppReadPackedMsg, mpdcch_params_rel13->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, mpdcch_params_rel13->number_of_tx_antenna_ports, end)); -} - - -static uint8_t unpack_dl_config_nbch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_nbch_pdu_rel13_t *nbch_params_rel13 = (nfapi_dl_config_nbch_pdu_rel13_t *)tlv; - return ( pull16(ppReadPackedMsg, &nbch_params_rel13->length, end) && - pull16(ppReadPackedMsg, (uint16_t *)&nbch_params_rel13->pdu_index, end) && - pull16(ppReadPackedMsg, &nbch_params_rel13->transmission_power, end) && - pull16(ppReadPackedMsg, &nbch_params_rel13->hyper_sfn_2_lsbs, end)); -} - -static uint8_t unpack_dl_config_npdcch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_npdcch_pdu_rel13_t *npdcch_params_rel13 = (nfapi_dl_config_npdcch_pdu_rel13_t *)tlv; - return ( pull16(ppReadPackedMsg, &npdcch_params_rel13->length, end) && - pull16(ppReadPackedMsg, (uint16_t *)&npdcch_params_rel13->pdu_index, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->ncce_index, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->aggregation_level, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->start_symbol, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->rnti_type, end) && - pull16(ppReadPackedMsg, &npdcch_params_rel13->rnti, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->scrambling_reinitialization_batch_index, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->nrs_antenna_ports_assumed_by_the_ue, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->dci_format, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->scheduling_delay, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->resource_assignment, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->repetition_number, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->mcs, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->new_data_indicator, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->harq_ack_resource, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->npdcch_order_indication, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->starting_number_of_nprach_repetitions, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->subcarrier_indication_of_nprach, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->paging_direct_indication_differentation_flag, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->direct_indication, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->dci_subframe_repetition_number, end) && - pull8(ppReadPackedMsg, &npdcch_params_rel13->total_dci_length_including_padding, end)); -} - -static uint8_t unpack_dl_config_ndlsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_dl_config_ndlsch_pdu_rel13_t *ndlsch_params_rel13 = (nfapi_dl_config_ndlsch_pdu_rel13_t *)tlv; - return ( pull16(ppReadPackedMsg, &ndlsch_params_rel13->length, end) && - pull16(ppReadPackedMsg, (uint16_t *)&ndlsch_params_rel13->pdu_index, end) && - pull8(ppReadPackedMsg, &ndlsch_params_rel13->start_symbol, end) && - pull8(ppReadPackedMsg, &ndlsch_params_rel13->rnti_type, end) && - pull16(ppReadPackedMsg, &ndlsch_params_rel13->rnti, end) && - pull16(ppReadPackedMsg, &ndlsch_params_rel13->resource_assignment, end) && - pull16(ppReadPackedMsg, &ndlsch_params_rel13->repetition_number, end) && - pull8(ppReadPackedMsg, &ndlsch_params_rel13->modulation, end) && - pull8(ppReadPackedMsg, &ndlsch_params_rel13->number_of_subframes_for_resource_assignment, end) && - pull8(ppReadPackedMsg, &ndlsch_params_rel13->scrambling_sequence_initialization_cinit, end) && - pull16(ppReadPackedMsg, &ndlsch_params_rel13->sf_idx, end) && - pull8(ppReadPackedMsg, &ndlsch_params_rel13->nrs_antenna_ports_assumed_by_the_ue, end)); -} - - -static uint8_t unpack_dl_tti_request_body_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { - nfapi_nr_dl_tti_request_pdu_t *value = (nfapi_nr_dl_tti_request_pdu_t *)msg; - - if(!(pull32(ppReadPackedMsg, &value->PDUSize, end) && - pull16(ppReadPackedMsg, &value->PDUType, end) )) - return 0; - - // first match the pdu type, then call the respective function - switch(value->PDUType) { - case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: { - if(!(unpack_dl_tti_csi_rs_pdu_rel15_value(&value->csi_rs_pdu.csi_rs_pdu_rel15,ppReadPackedMsg,end))) - return 0; - } - break; - - case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: { - if(!(unpack_dl_tti_pdcch_pdu_rel15_value(&value->pdcch_pdu.pdcch_pdu_rel15,ppReadPackedMsg,end))) - return 0; - } - break; - - case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: { - if(!(unpack_dl_tti_pdsch_pdu_rel15_value(&value->pdsch_pdu.pdsch_pdu_rel15,ppReadPackedMsg,end))) - return 0; - } - break; - - case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: { - if(!(unpack_dl_tti_ssb_pdu_rel15_value(&value->ssb_pdu.ssb_pdu_rel15,ppReadPackedMsg,end))) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid DL_TTI pdu type %d \n", value->PDUType ); - } - break; - } - - return 1; -} - - - - -static uint8_t unpack_dl_config_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_dl_config_request_body_t *value = (nfapi_dl_config_request_body_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &value->number_pdcch_ofdm_symbols, end) && - pull8(ppReadPackedMsg, &value->number_dci, end) && - pull16(ppReadPackedMsg, &value->number_pdu, end) && - pull8(ppReadPackedMsg, &value->number_pdsch_rnti, end) && - pull16(ppReadPackedMsg, &value->transmission_power_pcfich, end))) - return 0; - - if(value->number_pdu > NFAPI_DL_CONFIG_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of dl config pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_pdu, NFAPI_DL_CONFIG_MAX_PDU); - return 0; - } - - if(value->number_pdu) { - value->dl_config_pdu_list = (nfapi_dl_config_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_dl_config_request_pdu_t) * value->number_pdu, config); - - if(value->dl_config_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate dl config pdu list (count:%d)\n", __FUNCTION__, value->number_pdu); - return 0; - } - } else { - value->dl_config_pdu_list = 0; - } - - uint16_t i; - uint16_t total_number_of_pdus = value->number_pdu; - - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_dl_config_request_pdu_t *pdu = &(value->dl_config_pdu_list[i]); - - if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && - pull8(ppReadPackedMsg, &pdu->pdu_size, end))) - return 0; - - uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; - - if(packedPduEnd > end) { - // pdu end of beyond buffer end - return 0; - } - - switch(pdu->pdu_type) { - case NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL8_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel8, &unpack_dl_config_dci_dl_pdu_rel8_value}, - { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL9_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel9, &unpack_dl_config_dci_dl_pdu_rel9_value}, - { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL10_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel10, &unpack_dl_config_dci_dl_pdu_rel10_value}, - { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL11_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel11, &unpack_dl_config_dci_dl_pdu_rel11_value}, - { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL12_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel12, &unpack_dl_config_dci_dl_pdu_rel12_value}, - { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL13_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel13, &unpack_dl_config_dci_dl_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_BCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_BCH_PDU_REL8_TAG, &pdu->bch_pdu.bch_pdu_rel8, &unpack_dl_config_bch_pdu_rel8_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_MCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_MCH_PDU_REL8_TAG, &pdu->mch_pdu.mch_pdu_rel8, &unpack_dl_config_mch_pdu_rel8_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_DLSCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL8_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel8, &unpack_dl_config_dlsch_pdu_rel8_value}, - { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL9_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel9, &unpack_dl_config_dlsch_pdu_rel9_value}, - { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL10_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel10, &unpack_dl_config_dlsch_pdu_rel10_value}, - { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL11_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel11, &unpack_dl_config_dlsch_pdu_rel11_value}, - { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL12_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel12, &unpack_dl_config_dlsch_pdu_rel12_value}, - { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL13_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel13, &unpack_dl_config_dlsch_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_PCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL8_TAG, &pdu->pch_pdu.pch_pdu_rel8, &unpack_dl_config_pch_pdu_rel8_value}, - { NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL13_TAG, &pdu->pch_pdu.pch_pdu_rel13, &unpack_dl_config_pch_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_PRS_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_PRS_PDU_REL9_TAG, &pdu->prs_pdu.prs_pdu_rel9, &unpack_dl_config_prs_pdu_rel9_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_CSI_RS_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL10_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel10, &unpack_dl_config_csi_rs_pdu_rel10_value}, - { NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL13_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel13, &unpack_dl_config_csi_rs_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_EPDCCH_DL_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL8_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel8, &unpack_dl_config_dci_dl_pdu_rel8_value}, - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL9_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel9, &unpack_dl_config_dci_dl_pdu_rel9_value}, - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL10_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel10, &unpack_dl_config_dci_dl_pdu_rel10_value}, - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL11_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel11, &unpack_dl_config_dci_dl_pdu_rel11_value}, - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL12_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel12, &unpack_dl_config_dci_dl_pdu_rel12_value}, - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL13_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel13, &unpack_dl_config_dci_dl_pdu_rel13_value}, - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL11_TAG, &pdu->epdcch_pdu.epdcch_params_rel11, &unpack_dl_config_epdcch_params_rel11_value}, - { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL13_TAG, &pdu->epdcch_pdu.epdcch_params_rel13, &unpack_dl_config_epdcch_params_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_MPDCCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_MPDCCH_PDU_REL13_TAG, &pdu->mpdcch_pdu.mpdcch_pdu_rel13, &unpack_dl_config_mpdcch_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_NBCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_NBCH_PDU_REL13_TAG, &pdu->nbch_pdu.nbch_pdu_rel13, &unpack_dl_config_nbch_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_NPDCCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_NPDCCH_PDU_REL13_TAG, &pdu->npdcch_pdu.npdcch_pdu_rel13, &unpack_dl_config_npdcch_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_DL_CONFIG_NDLSCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_NDLSCH_PDU_REL13_TAG, &pdu->ndlsch_pdu.ndlsch_pdu_rel13, &unpack_dl_config_ndlsch_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - default: - // Need to log an error - break; - } - } - - return 1; -} - - -static uint8_t unpack_dl_tti_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nr_dl_tti_request_t *pNfapiMsg = (nfapi_nr_dl_tti_request_t *)msg; - - if (!(pull16(ppReadPackedMsg,&pNfapiMsg->SFN, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->dl_tti_request_body.nGroup, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->dl_tti_request_body.nPDUs, end) && - pullarray8(ppReadPackedMsg,pNfapiMsg->dl_tti_request_body.nUe,256,pNfapiMsg->dl_tti_request_body.nGroup, end) - //pusharray8(pNfapiMsg->PduIdx[0] ,256,256, ppWritePackedMsg, end) - )) - return 0; - - int arr[12]; - - for(int i=0; idl_tti_request_body.nGroup; i++) { - for(int j=0; jdl_tti_request_body.nUe[i]; j++) { - arr[j] = pNfapiMsg->dl_tti_request_body.PduIdx[i][j]; - } - - if(!(pullarrays32(ppReadPackedMsg,arr,12,pNfapiMsg->dl_tti_request_body.nUe[i], end))) - return 0; - } - - for(int i=0; idl_tti_request_body.nPDUs; i++) { - if(!unpack_dl_tti_request_body_value(ppReadPackedMsg, end, &pNfapiMsg->dl_tti_request_body.dl_tti_pdu_list[i])) - return 0; - } - - return 1; -} - - -static uint8_t unpack_ul_tti_request_prach_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_prach_pdu_t *prach_pdu = (nfapi_nr_prach_pdu_t *)tlv; - return( - pull16(ppReadPackedMsg, &prach_pdu->phys_cell_id, end) && - pull8(ppReadPackedMsg, &prach_pdu->num_prach_ocas, end) && - pull8(ppReadPackedMsg, &prach_pdu->prach_format, end) && - pull8(ppReadPackedMsg, &prach_pdu->num_ra, end) && - pull8(ppReadPackedMsg, &prach_pdu->prach_start_symbol, end) && - pull16(ppReadPackedMsg, &prach_pdu->num_cs, end) - // TODO: ignoring beamforming tlv for now - ); -} - - -static uint8_t unpack_ul_tti_request_pucch_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_pucch_pdu_t *pucch_pdu = (nfapi_nr_pucch_pdu_t *)tlv; - return( - pull16(ppReadPackedMsg, &pucch_pdu->rnti, end) && - pull32(ppReadPackedMsg, &pucch_pdu->handle, end) && - pull16(ppReadPackedMsg, &pucch_pdu->bwp_size, end) && - pull16(ppReadPackedMsg, &pucch_pdu->bwp_start, end) && - pull8(ppReadPackedMsg, &pucch_pdu->subcarrier_spacing, end) && - pull8(ppReadPackedMsg, &pucch_pdu->cyclic_prefix, end) && - pull8(ppReadPackedMsg, &pucch_pdu->format_type, end) && - pull8(ppReadPackedMsg, &pucch_pdu->multi_slot_tx_indicator, end) && - pull16(ppReadPackedMsg, &pucch_pdu->prb_start, end) && - pull16(ppReadPackedMsg, &pucch_pdu->prb_size, end) && - pull8(ppReadPackedMsg, &pucch_pdu->start_symbol_index, end) && - pull8(ppReadPackedMsg, &pucch_pdu->nr_of_symbols, end) && - pull8(ppReadPackedMsg, &pucch_pdu->freq_hop_flag, end) && - pull16(ppReadPackedMsg, &pucch_pdu->second_hop_prb, end) && - pull8(ppReadPackedMsg, &pucch_pdu->group_hop_flag, end) && - pull8(ppReadPackedMsg, &pucch_pdu->sequence_hop_flag, end) && - pull16(ppReadPackedMsg, &pucch_pdu->hopping_id, end) && - pull16(ppReadPackedMsg, &pucch_pdu->initial_cyclic_shift, end) && - pull16(ppReadPackedMsg, &pucch_pdu->data_scrambling_id, end) && - pull8(ppReadPackedMsg, &pucch_pdu->time_domain_occ_idx, end) && - pull8(ppReadPackedMsg, &pucch_pdu->pre_dft_occ_idx, end) && - pull8(ppReadPackedMsg, &pucch_pdu->pre_dft_occ_len, end) && - pull8(ppReadPackedMsg, &pucch_pdu->add_dmrs_flag, end) && - pull16(ppReadPackedMsg, &pucch_pdu->dmrs_scrambling_id, end) && - pull8(ppReadPackedMsg, &pucch_pdu->dmrs_cyclic_shift, end) && - pull8(ppReadPackedMsg, &pucch_pdu->sr_flag, end) && - pull8(ppReadPackedMsg, &pucch_pdu->bit_len_harq, end) && - pull16(ppReadPackedMsg, &pucch_pdu->bit_len_csi_part1, end) && - pull16(ppReadPackedMsg, &pucch_pdu->bit_len_csi_part2, end) - // TODO: ignoring beamforming tlv for now - ); -} - - -static uint8_t unpack_ul_tti_request_pusch_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_pusch_pdu_t *pusch_pdu = (nfapi_nr_pusch_pdu_t *)tlv; - - if (!( - pull16(ppReadPackedMsg, &pusch_pdu->pdu_bit_map, end) && - pull16(ppReadPackedMsg, &pusch_pdu->rnti, end) && - pull32(ppReadPackedMsg, &pusch_pdu->handle, end) && - pull16(ppReadPackedMsg, &pusch_pdu->bwp_size, end) && - pull16(ppReadPackedMsg, &pusch_pdu->bwp_start, end) && - pull8(ppReadPackedMsg, &pusch_pdu->subcarrier_spacing, end) && - pull8(ppReadPackedMsg, &pusch_pdu->cyclic_prefix, end) && - pull16(ppReadPackedMsg, &pusch_pdu->target_code_rate, end) && - pull8(ppReadPackedMsg, &pusch_pdu->qam_mod_order, end) && - pull8(ppReadPackedMsg, &pusch_pdu->mcs_index, end) && - pull8(ppReadPackedMsg, &pusch_pdu->mcs_table, end) && - pull8(ppReadPackedMsg, &pusch_pdu->transform_precoding, end) && - pull16(ppReadPackedMsg, &pusch_pdu->data_scrambling_id, end) && - pull8(ppReadPackedMsg, &pusch_pdu->nrOfLayers, end) && - pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_symb_pos, end) && - pull8(ppReadPackedMsg, &pusch_pdu->dmrs_config_type, end) && - pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_scrambling_id, end) && - pull8(ppReadPackedMsg, &pusch_pdu->scid, end) && - pull8(ppReadPackedMsg, &pusch_pdu->num_dmrs_cdm_grps_no_data, end) && - pull16(ppReadPackedMsg, &pusch_pdu->dmrs_ports, end) && - pull8(ppReadPackedMsg, &pusch_pdu->resource_alloc, end) && - pull8(ppReadPackedMsg, &pusch_pdu->resource_alloc,end) && - pull16(ppReadPackedMsg, &pusch_pdu->dmrs_ports, end) && - pull16(ppReadPackedMsg, &pusch_pdu->rb_start, end) && - pull16(ppReadPackedMsg, &pusch_pdu->rb_size, end) && - pull8(ppReadPackedMsg, &pusch_pdu->vrb_to_prb_mapping, end) && - pull8(ppReadPackedMsg, &pusch_pdu->frequency_hopping, end) && - pull16(ppReadPackedMsg, &pusch_pdu->tx_direct_current_location, end) && - pull8(ppReadPackedMsg, &pusch_pdu->uplink_frequency_shift_7p5khz, end) && - pull8(ppReadPackedMsg, &pusch_pdu->start_symbol_index, end) && - pull8(ppReadPackedMsg, &pusch_pdu->nr_of_symbols, end) - // TODO: ignoring beamforming tlv for now - )) - return 0; - - //Pack Optional Data only included if indicated in pduBitmap - switch(pusch_pdu->pdu_bit_map) { - case PUSCH_PDU_BITMAP_PUSCH_DATA: { - // pack optional TLVs - return( - pull8(ppReadPackedMsg, &pusch_pdu->pusch_data.rv_index, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_data.harq_process_id, end) && - pull32(ppReadPackedMsg, &pusch_pdu->pusch_data.tb_size, end) && - pull16(ppReadPackedMsg, &pusch_pdu->pusch_data.num_cb, end) && - pullarray8(ppReadPackedMsg, pusch_pdu->pusch_data.cb_present_and_position,1,1,end) - ); - } - break; - - case PUSCH_PDU_BITMAP_PUSCH_UCI: { - return( - pull16(ppReadPackedMsg, &pusch_pdu->pusch_uci.harq_ack_bit_length, end) && - pull16(ppReadPackedMsg, &pusch_pdu->pusch_uci.csi_part1_bit_length, end) && - pull16(ppReadPackedMsg, &pusch_pdu->pusch_uci.csi_part2_bit_length, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.alpha_scaling, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.beta_offset_harq_ack, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.beta_offset_csi1, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.beta_offset_csi2, end) - ); - } - break; - - case PUSCH_PDU_BITMAP_PUSCH_PTRS: { - return( - pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.num_ptrs_ports, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_dmrs_port, end) && - + pull16(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_port_index, end) && - + pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_time_density, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_freq_density, end) && - pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ul_ptrs_power, end) - ); - } - break; - - case PUSCH_PDU_BITMAP_DFTS_OFDM: { - return( - pull8(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.low_papr_group_number, end) && - pull16(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.low_papr_sequence_number, end) && - pull8(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.ul_ptrs_sample_density, end) && - pull8(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.ul_ptrs_time_density_transform_precoding, end) - ); - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_INFO, "Invalid pdu bitmap %d \n", pusch_pdu->pdu_bit_map ); - } - } - - return 1; -} - - -static uint8_t unpack_ul_tti_request_srs_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nr_srs_pdu_t *srs_pdu = (nfapi_nr_srs_pdu_t *)tlv; - return( - pull16(ppReadPackedMsg, &srs_pdu->rnti, end) && - pull32(ppReadPackedMsg, &srs_pdu->handle, end) && - pull16(ppReadPackedMsg, &srs_pdu->bwp_size, end) && - pull16(ppReadPackedMsg, &srs_pdu->bwp_start, end) && - pull8(ppReadPackedMsg, &srs_pdu->subcarrier_spacing, end) && - pull8(ppReadPackedMsg, &srs_pdu->cyclic_prefix, end) && - pull8(ppReadPackedMsg, &srs_pdu->num_ant_ports, end) && - pull8(ppReadPackedMsg, &srs_pdu->num_symbols, end) && - pull8(ppReadPackedMsg, &srs_pdu->num_repetitions, end) && - pull8(ppReadPackedMsg, &srs_pdu->time_start_position, end) && - pull8(ppReadPackedMsg, &srs_pdu->config_index, end) && - pull16(ppReadPackedMsg, &srs_pdu->sequence_id, end) && - pull8(ppReadPackedMsg, &srs_pdu->bandwidth_index, end) && - pull8(ppReadPackedMsg, &srs_pdu->comb_size, end) && - pull8(ppReadPackedMsg, &srs_pdu->comb_offset, end) && - pull8(ppReadPackedMsg, &srs_pdu->cyclic_shift, end) && - pull8(ppReadPackedMsg, &srs_pdu->frequency_position, end) && - pull8(ppReadPackedMsg, &srs_pdu->frequency_shift, end) && - pull8(ppReadPackedMsg, &srs_pdu->frequency_hopping, end) && - pull8(ppReadPackedMsg, &srs_pdu->group_or_sequence_hopping, end) && - pull8(ppReadPackedMsg, &srs_pdu->resource_type, end) && - pull16(ppReadPackedMsg, &srs_pdu->t_srs, end) && - pull16(ppReadPackedMsg, &srs_pdu->t_offset, end) - // TODO: ignoring beamforming tlv for now - ); -} - - -static uint8_t unpack_ul_tti_pdu_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { - nfapi_nr_ul_tti_request_number_of_pdus_t *pNfapiMsg = (nfapi_nr_ul_tti_request_number_of_pdus_t *)msg; - - if(!(pull16(ppReadPackedMsg, &pNfapiMsg->pdu_size, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->pdu_type, end) )) - return 0; - - // first natch the pdu type, then call the respective function - switch(pNfapiMsg->pdu_type) { - case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: { - if(!unpack_ul_tti_request_prach_pdu(&pNfapiMsg->prach_pdu, ppReadPackedMsg, end)) - return 0; - } - break; - - case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE: { - if(!unpack_ul_tti_request_pucch_pdu(&pNfapiMsg->pucch_pdu, ppReadPackedMsg, end)) - return 0; - } - break; - - case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: { - if(!unpack_ul_tti_request_pusch_pdu(&pNfapiMsg->pusch_pdu, ppReadPackedMsg, end)) - return 0; - } - break; - - case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: { - if(!unpack_ul_tti_request_srs_pdu(&pNfapiMsg->srs_pdu, ppReadPackedMsg, end)) - return 0; - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid UL_TTI pdu type %d \n", pNfapiMsg->pdu_type ); - } - break; - } - - return 1; -} - - -static uint8_t unpack_ul_tti_groups_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { - nfapi_nr_ul_tti_request_number_of_groups_t *pNfapiMsg = (nfapi_nr_ul_tti_request_number_of_groups_t *)msg; - - if(!pull8(ppReadPackedMsg, &pNfapiMsg->n_ue, end)) - return 0; - - for (int i = 0; i < pNfapiMsg->n_ue; i++) { - if(!pull8(ppReadPackedMsg, &pNfapiMsg->ue_list[i].pdu_idx,end) ) - return 0; - } - - return 1; -} - - -static uint8_t unpack_ul_tti_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nr_ul_tti_request_t *pNfapiMsg = (nfapi_nr_ul_tti_request_t *)msg; - - if (!( - pull16(ppReadPackedMsg, &pNfapiMsg->SFN, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->n_pdus, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->n_group, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->rach_present, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->n_ulcch, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->n_ulsch, end) )) - return 0; - - for(int i=0; i< pNfapiMsg->n_pdus; i++) { - if (!unpack_ul_tti_pdu_list_value(ppReadPackedMsg, end, &pNfapiMsg->pdus_list[i])) - return 0; - } - - for(int i=0; i< pNfapiMsg->n_group; i++) { - if (!unpack_ul_tti_groups_list_value(ppReadPackedMsg, end, &pNfapiMsg->groups_list[i])) - return 0; - } - - return 1; -} - - - -static uint8_t unpack_dl_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_dl_config_request_t *pNfapiMsg = (nfapi_dl_config_request_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->dl_config_request_body, &unpack_dl_config_request_body_value}, - }; - return ( pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_ul_config_ulsch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel8_t *ulsch_pdu_rel8 = (nfapi_ul_config_ulsch_pdu_rel8_t *)tlv; - return (pull32(ppReadPackedMsg, &ulsch_pdu_rel8->handle, end) && - pull16(ppReadPackedMsg, &ulsch_pdu_rel8->size, end) && - pull16(ppReadPackedMsg, &ulsch_pdu_rel8->rnti, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->resource_block_start, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->number_of_resource_blocks, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->modulation_type, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->cyclic_shift_2_for_drms, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->frequency_hopping_enabled_flag, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->frequency_hopping_bits, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->new_data_indication, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->redundancy_version, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->harq_process_number, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->ul_tx_mode, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->current_tx_nb, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel8->n_srs, end )); -} - -static uint8_t unpack_ul_config_ulsch_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel10_t *ulsch_pdu_rel10 = (nfapi_ul_config_ulsch_pdu_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &ulsch_pdu_rel10->resource_allocation_type, end) && - pull32(ppReadPackedMsg, &ulsch_pdu_rel10->resource_block_coding, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel10->transport_blocks, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel10->transmission_scheme, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel10->number_of_layers, end) & - pull8(ppReadPackedMsg, &ulsch_pdu_rel10->codebook_index, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel10->disable_sequence_hopping_flag, end)); -} -static uint8_t unpack_ul_config_ulsch_pdu_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel11_t *ulsch_pdu_rel11 = (nfapi_ul_config_ulsch_pdu_rel11_t *)tlv; - return ( pull8(ppReadPackedMsg, &ulsch_pdu_rel11->virtual_cell_id_enabled_flag, end) && - pull16(ppReadPackedMsg, &ulsch_pdu_rel11->npusch_identity, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel11->dmrs_config_flag, end) && - pull16(ppReadPackedMsg, &ulsch_pdu_rel11->ndmrs_csh_identity, end)); -} -static uint8_t unpack_ul_config_ulsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_pdu_rel13_t *ulsch_pdu_rel13 = (nfapi_ul_config_ulsch_pdu_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &ulsch_pdu_rel13->ue_type, end) && - pull16(ppReadPackedMsg, &ulsch_pdu_rel13->total_number_of_repetitions, end) && - pull16(ppReadPackedMsg, &ulsch_pdu_rel13->repetition_number, end) && - pull16(ppReadPackedMsg, &ulsch_pdu_rel13->initial_transmission_sf_io, end) && - pull8(ppReadPackedMsg, &ulsch_pdu_rel13->empty_symbols_due_to_re_tunning, end)); -} -static uint8_t unpack_ul_config_cqi_ri_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_ri_information_rel8_t *cqi_ri_info_rel8 = (nfapi_ul_config_cqi_ri_information_rel8_t *)tlv; - return (pull8(ppReadPackedMsg, &cqi_ri_info_rel8->dl_cqi_pmi_size_rank_1, end) && - pull8(ppReadPackedMsg, &cqi_ri_info_rel8->dl_cqi_pmi_size_rank_greater_1, end) && - pull8(ppReadPackedMsg, &cqi_ri_info_rel8->ri_size, end) && - pull8(ppReadPackedMsg, &cqi_ri_info_rel8->delta_offset_cqi, end) && - pull8(ppReadPackedMsg, &cqi_ri_info_rel8->delta_offset_ri, end)); -} - -static uint8_t unpack_ul_config_cqi_ri_info_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_ri_information_rel9_t *cqi_ri_info_rel9 = (nfapi_ul_config_cqi_ri_information_rel9_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->report_type, end) && - pull8(ppReadPackedMsg, &cqi_ri_info_rel9->delta_offset_cqi, end) && - pull8(ppReadPackedMsg, &cqi_ri_info_rel9->delta_offset_ri, end))) - return 0; - - switch(cqi_ri_info_rel9->report_type) { - case NFAPI_CSI_REPORT_TYPE_PERIODIC: { - if(!(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size, end) && - pull8(ppReadPackedMsg, &cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.control_type, end))) - return 0; - } - break; - - case NFAPI_CSI_REPORT_TYPE_APERIODIC: { - if(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc, end) ==0) - return 0; - - uint8_t i; - - for(i = 0; i < cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc; ++i) { - if(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].ri_size, end) == 0) - return 0; - - uint8_t j; - - for(j = 0; j < 8; ++j) { - if(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].dl_cqi_pmi_size[j], end) == 0) - return 0; - } - } - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid report type %d \n", cqi_ri_info_rel9->report_type ); - return 0; - } - break; - }; - - return 1; -} - -// NOTE : This function is a little unconventional as we uese the side to -// determine the report type -static uint8_t unpack_ul_config_cqi_ri_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_ri_information_rel13_t *cqi_ri_info_rel13 = (nfapi_ul_config_cqi_ri_information_rel13_t *)tlv; - - if(cqi_ri_info_rel13->tl.length == 0) { - cqi_ri_info_rel13->report_type = NFAPI_CSI_REPORT_TYPE_APERIODIC; - } else { - cqi_ri_info_rel13->report_type = NFAPI_CSI_REPORT_TYPE_PERIODIC; - - if(pull16(ppReadPackedMsg, &cqi_ri_info_rel13->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size_2, end) == 0) - return 0; - } - - return 1; -} -static uint8_t unpack_ul_config_cqi_init_tx_params_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_initial_transmission_parameters_rel8_t *init_tx_params_rel8 = (nfapi_ul_config_initial_transmission_parameters_rel8_t *)tlv; - return (pull8(ppReadPackedMsg, &init_tx_params_rel8->n_srs_initial, end) && - pull8(ppReadPackedMsg, &init_tx_params_rel8->initial_number_of_resource_blocks, end)); -} -static uint8_t unpack_ul_config_ulsch_harq_info_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_harq_information_rel10_t *harq_info_rel10 = (nfapi_ul_config_ulsch_harq_information_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &harq_info_rel10->harq_size, end) && - pull8(ppReadPackedMsg, &harq_info_rel10->delta_offset_harq, end) && - pull8(ppReadPackedMsg, &harq_info_rel10->ack_nack_mode, end)); -} - -static uint8_t unpack_ul_config_ulsch_harq_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ulsch_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_ulsch_harq_information_rel13_t *)tlv; - return (pull16(ppReadPackedMsg, &harq_info_rel13->harq_size_2, end) && - pull8(ppReadPackedMsg, &harq_info_rel13->delta_offset_harq_2, end)); -} - -static uint8_t unpack_ul_config_ue_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ue_information_rel8_t *ue_info_rel8 = (nfapi_ul_config_ue_information_rel8_t *)tlv; - return (pull32(ppReadPackedMsg, &ue_info_rel8->handle, end) && - pull16(ppReadPackedMsg, (uint16_t *)&ue_info_rel8->rnti, end)); -} -static uint8_t unpack_ul_config_ue_info_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ue_information_rel11_t *ue_info_rel11 = (nfapi_ul_config_ue_information_rel11_t *)tlv; - return (pull8(ppReadPackedMsg, &ue_info_rel11->virtual_cell_id_enabled_flag, end) && - pull16(ppReadPackedMsg, &ue_info_rel11->npusch_identity, end)); -} -static uint8_t unpack_ul_config_ue_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_ue_information_rel13_t *ue_info_rel13 = (nfapi_ul_config_ue_information_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &ue_info_rel13->ue_type, end) && - pull8(ppReadPackedMsg, &ue_info_rel13->empty_symbols, end) && - pull16(ppReadPackedMsg, &ue_info_rel13->total_number_of_repetitions, end) && - pull16(ppReadPackedMsg, &ue_info_rel13->repetition_number, end)); -} - -static uint8_t unpack_ul_config_cqi_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_information_rel8_t *cqi_info_rel8 = (nfapi_ul_config_cqi_information_rel8_t *)tlv; - return ( pull16(ppReadPackedMsg, &cqi_info_rel8->pucch_index, end) && - pull8(ppReadPackedMsg, &cqi_info_rel8->dl_cqi_pmi_size, end)); -} -static uint8_t unpack_ul_config_cqi_info_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_information_rel10_t *cqi_info_rel10 = (nfapi_ul_config_cqi_information_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &cqi_info_rel10->number_of_pucch_resource, end) && - pull16(ppReadPackedMsg, &cqi_info_rel10->pucch_index_p1, end)); -} -static uint8_t unpack_ul_config_cqi_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_cqi_information_rel13_t *cqi_info_rel13 = (nfapi_ul_config_cqi_information_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &cqi_info_rel13->csi_mode, end) && - pull16(ppReadPackedMsg, &cqi_info_rel13->dl_cqi_pmi_size_2, end) && - pull8(ppReadPackedMsg, &cqi_info_rel13->starting_prb, end) && - pull8(ppReadPackedMsg, &cqi_info_rel13->n_prb, end) && - pull8(ppReadPackedMsg, &cqi_info_rel13->cdm_index, end) && - pull8(ppReadPackedMsg, &cqi_info_rel13->n_srs, end)); -} - -static uint8_t unpack_ul_config_sr_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_sr_information_rel8_t *sr_info_rel8 = (nfapi_ul_config_sr_information_rel8_t *)tlv; - return ( pull16(ppReadPackedMsg, &sr_info_rel8->pucch_index, end)); -} - -static uint8_t unpack_ul_config_sr_info_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_sr_information_rel10_t *sr_info_rel10 = (nfapi_ul_config_sr_information_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &sr_info_rel10->number_of_pucch_resources, end) && - pull16(ppReadPackedMsg, &sr_info_rel10->pucch_index_p1, end)); -} - -static uint8_t unpack_ul_config_harq_info_rel10_tdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel10_tdd_t *harq_info_tdd_rel10 = (nfapi_ul_config_harq_information_rel10_tdd_t *)tlv; - return (pull8(ppReadPackedMsg, &harq_info_tdd_rel10->harq_size, end) && - pull8(ppReadPackedMsg, &harq_info_tdd_rel10->ack_nack_mode, end) && - pull8(ppReadPackedMsg, &harq_info_tdd_rel10->number_of_pucch_resources, end) && - pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_0, end) && - pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_1, end) && - pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_2, end) && - pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_3, end)); -} - -static uint8_t unpack_ul_config_harq_info_rel8_fdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel8_fdd_t *harq_info_fdd_rel8 = (nfapi_ul_config_harq_information_rel8_fdd_t *)tlv; - return (pull16(ppReadPackedMsg, &harq_info_fdd_rel8->n_pucch_1_0, end) && - pull8(ppReadPackedMsg, &harq_info_fdd_rel8->harq_size, end)); -} - -static uint8_t unpack_ul_config_harq_info_rel9_fdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel9_fdd_t *harq_info_fdd_rel9 = (nfapi_ul_config_harq_information_rel9_fdd_t *)tlv; - return (pull8(ppReadPackedMsg, &harq_info_fdd_rel9->harq_size, end) && - pull8(ppReadPackedMsg, &harq_info_fdd_rel9->ack_nack_mode, end) && - pull8(ppReadPackedMsg, &harq_info_fdd_rel9->number_of_pucch_resources, end) && - pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_0, end) && - pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_1, end) && - pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_2, end) && - pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_3, end)); -} - -static uint8_t unpack_ul_config_harq_info_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel11_t *harq_info_rel11 = (nfapi_ul_config_harq_information_rel11_t *)tlv; - return (pull8(ppReadPackedMsg, &harq_info_rel11->num_ant_ports, end) && - pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_0, end) && - pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_1, end) && - pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_2, end) && - pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_3, end)); -} - -static uint8_t unpack_ul_config_harq_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_harq_information_rel13_t *)tlv; - return (pull16(ppReadPackedMsg, &harq_info_rel13->harq_size_2, end) && - pull8(ppReadPackedMsg, &harq_info_rel13->starting_prb, end) && - pull8(ppReadPackedMsg, &harq_info_rel13->n_prb, end) && - pull8(ppReadPackedMsg, &harq_info_rel13->cdm_index, end) && - pull8(ppReadPackedMsg, &harq_info_rel13->n_srs, end)); -} - - -static uint8_t unpack_ul_config_srs_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_srs_pdu_rel8_t *srs_pdu_rel8 = (nfapi_ul_config_srs_pdu_rel8_t *)tlv; - return (pull32(ppReadPackedMsg, &srs_pdu_rel8->handle, end) && - pull16(ppReadPackedMsg, &srs_pdu_rel8->size, end) && - pull16(ppReadPackedMsg, &srs_pdu_rel8->rnti, end) && - pull8(ppReadPackedMsg, &srs_pdu_rel8->srs_bandwidth, end) && - pull8(ppReadPackedMsg, &srs_pdu_rel8->frequency_domain_position, end) && - pull8(ppReadPackedMsg, &srs_pdu_rel8->srs_hopping_bandwidth, end) && - pull8(ppReadPackedMsg, &srs_pdu_rel8->transmission_comb, end) && - pull16(ppReadPackedMsg, &srs_pdu_rel8->i_srs, end) && - pull8(ppReadPackedMsg, &srs_pdu_rel8->sounding_reference_cyclic_shift, end)); -} - -static uint8_t unpack_ul_config_srs_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_srs_pdu_rel10_t *srs_pdu_rel10 = (nfapi_ul_config_srs_pdu_rel10_t *)tlv; - return pull8(ppReadPackedMsg, &srs_pdu_rel10->antenna_port, end); -} - -static uint8_t unpack_ul_config_srs_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_srs_pdu_rel13_t *srs_pdu_rel13 = (nfapi_ul_config_srs_pdu_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &srs_pdu_rel13->number_of_combs, end)); -} - -static uint8_t unpack_ul_nb_harq_info_rel13_fdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_nb_harq_information_rel13_fdd_t *nb_harq_info_fdd_rel13 = (nfapi_ul_config_nb_harq_information_rel13_fdd_t *)tlv; - return (pull8(ppReadPackedMsg, &nb_harq_info_fdd_rel13->harq_ack_resource, end)); -} - -static uint8_t unpack_ul_config_nulsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_nulsch_pdu_rel13_t *nulsch_pdu_rel13 = (nfapi_ul_config_nulsch_pdu_rel13_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &nulsch_pdu_rel13->nulsch_format, end) && - pull32(ppReadPackedMsg, &nulsch_pdu_rel13->handle, end) && - pull16(ppReadPackedMsg, &nulsch_pdu_rel13->size, end) && - pull16(ppReadPackedMsg, &nulsch_pdu_rel13->rnti, end) && - pull8(ppReadPackedMsg, &nulsch_pdu_rel13->subcarrier_indication, end) && - pull8(ppReadPackedMsg, &nulsch_pdu_rel13->resource_assignment, end) && - pull8(ppReadPackedMsg, &nulsch_pdu_rel13->mcs, end) && - pull8(ppReadPackedMsg, &nulsch_pdu_rel13->redudancy_version, end) && - pull8(ppReadPackedMsg, &nulsch_pdu_rel13->repetition_number, end) && - pull8(ppReadPackedMsg, &nulsch_pdu_rel13->new_data_indication, end) && - pull8(ppReadPackedMsg, &nulsch_pdu_rel13->n_srs, end) && - pull16(ppReadPackedMsg, &nulsch_pdu_rel13->scrambling_sequence_initialization_cinit, end) && - pull16(ppReadPackedMsg, &nulsch_pdu_rel13->sf_idx, end))) - return 0; - - unpack_tlv_t unpack_fns[] = { - { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL8_TAG, &nulsch_pdu_rel13->ue_information.ue_information_rel8, &unpack_ul_config_ue_info_rel8_value}, - { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL11_TAG, &nulsch_pdu_rel13->ue_information.ue_information_rel11, &unpack_ul_config_ue_info_rel11_value}, - { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL13_TAG, &nulsch_pdu_rel13->ue_information.ue_information_rel13, &unpack_ul_config_ue_info_rel13_value}, - { NFAPI_UL_CONFIG_REQUEST_NB_HARQ_INFORMATION_REL13_FDD_TAG, &nulsch_pdu_rel13->nb_harq_information.nb_harq_information_rel13_fdd, &unpack_ul_nb_harq_info_rel13_fdd_value}, - }; - return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, 0, 0); -} - -static uint8_t unpack_ul_config_nrach_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_config_nrach_pdu_rel13_t *nrach_pdu_rel13 = (nfapi_ul_config_nrach_pdu_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &nrach_pdu_rel13->nprach_config_0, end) && - pull8(ppReadPackedMsg, &nrach_pdu_rel13->nprach_config_1, end) && - pull8(ppReadPackedMsg, &nrach_pdu_rel13->nprach_config_2, end)); -} - - -static uint8_t unpack_ul_config_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { -#define UL_CONFIG_ULSCH_PDU_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL8_TAG, &_pdu.ulsch_pdu_rel8, &unpack_ul_config_ulsch_pdu_rel8_value}, \ - { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL10_TAG, &_pdu.ulsch_pdu_rel10, &unpack_ul_config_ulsch_pdu_rel10_value}, \ - { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL11_TAG, &_pdu.ulsch_pdu_rel11, &unpack_ul_config_ulsch_pdu_rel11_value}, \ - { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL13_TAG, &_pdu.ulsch_pdu_rel13, &unpack_ul_config_ulsch_pdu_rel13_value}, -#define UL_CONFIG_CQI_RI_INFO_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL8_TAG, &_pdu.cqi_ri_information_rel8, &unpack_ul_config_cqi_ri_info_rel8_value}, \ - { NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL9_TAG, &_pdu.cqi_ri_information_rel9, &unpack_ul_config_cqi_ri_info_rel9_value}, \ - { NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL13_TAG, &_pdu.cqi_ri_information_rel13, &unpack_ul_config_cqi_ri_info_rel13_value}, -#define UL_CONFIG_ULSCH_HARQ_INFO_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL10_TAG, &_pdu.harq_information_rel10, &unpack_ul_config_ulsch_harq_info_rel10_value},\ - { NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL13_TAG, &_pdu.harq_information_rel13, &unpack_ul_config_ulsch_harq_info_rel13_value}, -#define UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_INITIAL_TRANSMISSION_PARAMETERS_REL8_TAG, &_pdu.initial_transmission_parameters_rel8, &unpack_ul_config_cqi_init_tx_params_rel8_value}, -#define UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL8_TAG, &_pdu.ue_information_rel8, &unpack_ul_config_ue_info_rel8_value}, \ - { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL11_TAG, &_pdu.ue_information_rel11, &unpack_ul_config_ue_info_rel11_value}, \ - { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL13_TAG, &_pdu.ue_information_rel13, &unpack_ul_config_ue_info_rel13_value}, -#define UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL8_TAG, &_pdu.cqi_information_rel8, &unpack_ul_config_cqi_info_rel8_value}, \ - { NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL10_TAG, &_pdu.cqi_information_rel10, &unpack_ul_config_cqi_info_rel10_value}, \ - { NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL13_TAG, &_pdu.cqi_information_rel13, &unpack_ul_config_cqi_info_rel13_value}, -#define UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL8_TAG, &_pdu.sr_information_rel8, &unpack_ul_config_sr_info_rel8_value}, \ - { NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL10_TAG, &_pdu.sr_information_rel10, &unpack_ul_config_sr_info_rel10_value}, -#define UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL10_TDD_TAG, &_pdu.harq_information_rel10_tdd, &unpack_ul_config_harq_info_rel10_tdd_value}, \ - { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL8_FDD_TAG, &_pdu.harq_information_rel8_fdd, &unpack_ul_config_harq_info_rel8_fdd_value}, \ - { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL9_FDD_TAG, &_pdu.harq_information_rel9_fdd, &unpack_ul_config_harq_info_rel9_fdd_value}, \ - { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL11_TAG, &_pdu.harq_information_rel11, &unpack_ul_config_harq_info_rel11_value}, \ - { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL13_TAG, &_pdu.harq_information_rel13, &unpack_ul_config_harq_info_rel13_value}, -#define UL_CONFIG_SRS_PDU_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL8_TAG, &_pdu.srs_pdu_rel8, &unpack_ul_config_srs_pdu_rel8_value}, \ - { NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL10_TAG, &_pdu.srs_pdu_rel10, &unpack_ul_config_srs_pdu_rel10_value}, \ - { NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL13_TAG, &_pdu.srs_pdu_rel13, &unpack_ul_config_srs_pdu_rel13_value}, -#define UL_CONFIG_NULSCH_PDU_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_NULSCH_PDU_REL13_TAG, &_pdu.nulsch_pdu_rel13, &unpack_ul_config_nulsch_pdu_rel13_value}, -#define UL_CONFIG_NRACH_PDU_UNPACK_FNS(_pdu) \ - { NFAPI_UL_CONFIG_REQUEST_NRACH_PDU_REL13_TAG, &_pdu.nrach_pdu_rel13, &unpack_ul_config_nrach_pdu_rel13_value}, - nfapi_ul_config_request_body_t *value = (nfapi_ul_config_request_body_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &value->number_of_pdus, end) && - pull8(ppReadPackedMsg, &value->rach_prach_frequency_resources, end) && - pull8(ppReadPackedMsg, &value->srs_present, end))) - return 0; - - if(value->number_of_pdus > NFAPI_UL_CONFIG_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of ul config pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_UL_CONFIG_MAX_PDU); - return 0; - } - - if(value->number_of_pdus > 0) { - value->ul_config_pdu_list = (nfapi_ul_config_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_ul_config_request_pdu_t) * value->number_of_pdus, config); - - if(value->ul_config_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate ul config pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); - return 0; - } - } else { - value->ul_config_pdu_list = 0; - } - - uint16_t i; - uint16_t total_number_of_pdus = value->number_of_pdus; - - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_ul_config_request_pdu_t *pdu = &(value->ul_config_pdu_list[i]); - - if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && - pull8(ppReadPackedMsg, &pdu->pdu_size, end))) - return 0; - - uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; - - if(packedPduEnd > end) { - // pdu end is past buffer end - return 0; - } - - switch(pdu->pdu_type) { - case NFAPI_UL_CONFIG_ULSCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_pdu) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_cqi_ri_pdu.ulsch_pdu) - UL_CONFIG_CQI_RI_INFO_UNPACK_FNS(pdu->ulsch_cqi_ri_pdu.cqi_ri_information) - UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(pdu->ulsch_cqi_ri_pdu.initial_transmission_parameters) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_harq_pdu.ulsch_pdu) - UL_CONFIG_ULSCH_HARQ_INFO_UNPACK_FNS(pdu->ulsch_harq_pdu.harq_information) - UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(pdu->ulsch_harq_pdu.initial_transmission_parameters) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu) - UL_CONFIG_CQI_RI_INFO_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.cqi_ri_information) - UL_CONFIG_ULSCH_HARQ_INFO_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.harq_information) - UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.initial_transmission_parameters) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_pdu.ue_information) - UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_pdu.cqi_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_sr_pdu.ue_information) - UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_sr_pdu.sr_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_harq_pdu.ue_information) - UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_harq_pdu.harq_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_sr_harq_pdu.ue_information) - UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_sr_harq_pdu.sr_information) - UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_sr_harq_pdu.harq_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_harq_pdu.ue_information) - UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_harq_pdu.cqi_information) - UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_cqi_harq_pdu.harq_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_sr_pdu.ue_information) - UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_sr_pdu.cqi_information) - UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_cqi_sr_pdu.sr_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.ue_information) - UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.cqi_information) - UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.sr_information) - UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.harq_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_SRS_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_SRS_PDU_UNPACK_FNS(pdu->srs_pdu) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_HARQ_BUFFER_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->harq_buffer_pdu.ue_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_ULSCH_UCI_CSI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_uci_csi_pdu.ulsch_pdu) - UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->ulsch_uci_csi_pdu.csi_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_uci_harq_pdu.ulsch_pdu) - UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->ulsch_uci_harq_pdu.harq_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_csi_uci_harq_pdu.ulsch_pdu) - UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->ulsch_csi_uci_harq_pdu.csi_information) - UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->ulsch_csi_uci_harq_pdu.harq_information) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_NULSCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_NULSCH_PDU_UNPACK_FNS(pdu->nulsch_pdu) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_UL_CONFIG_NRACH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - UL_CONFIG_NRACH_PDU_UNPACK_FNS(pdu->nrach_pdu) - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - } - } - - return 1; -} - - -static uint8_t unpack_ul_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_ul_config_request_t *pNfapiMsg = (nfapi_ul_config_request_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_UL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->ul_config_request_body, &unpack_ul_config_request_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_hi_dci0_hi_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_hi_dci0_hi_pdu_rel8_t *hi_pdu_rel8 = (nfapi_hi_dci0_hi_pdu_rel8_t *)tlv; - return( pull8(ppReadPackedMsg, &hi_pdu_rel8->resource_block_start, end) && - pull8(ppReadPackedMsg, &hi_pdu_rel8->cyclic_shift_2_for_drms, end) && - pull8(ppReadPackedMsg, &hi_pdu_rel8->hi_value, end) && - pull8(ppReadPackedMsg, &hi_pdu_rel8->i_phich, end) && - pull16(ppReadPackedMsg, &hi_pdu_rel8->transmission_power, end)); -} - -static uint8_t unpack_hi_dci0_hi_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_hi_dci0_hi_pdu_rel10_t *hi_pdu_rel10 = (nfapi_hi_dci0_hi_pdu_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &hi_pdu_rel10->flag_tb2, end) && - pull8(ppReadPackedMsg, &hi_pdu_rel10->hi_value_2, end)); -} - -static uint8_t unpack_hi_dci0_dci_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_hi_dci0_dci_pdu_rel8_t *dci_pdu_rel8 = (nfapi_hi_dci0_dci_pdu_rel8_t *)tlv; - return (pull8(ppReadPackedMsg, &dci_pdu_rel8->dci_format, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->cce_index, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->aggregation_level, end) && - pull16(ppReadPackedMsg, &dci_pdu_rel8->rnti, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->resource_block_start, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->number_of_resource_block, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->mcs_1, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->cyclic_shift_2_for_drms, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->frequency_hopping_enabled_flag, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->frequency_hopping_bits, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->new_data_indication_1, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->ue_tx_antenna_seleciton, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->tpc, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->cqi_csi_request, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->ul_index, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel8->dl_assignment_index, end) && - pull32(ppReadPackedMsg, &dci_pdu_rel8->tpc_bitmap, end) && - pull16(ppReadPackedMsg, &dci_pdu_rel8->transmission_power, end)); -} - -static uint8_t unpack_hi_dci0_dci_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_hi_dci0_dci_pdu_rel10_t *dci_pdu_rel10 = (nfapi_hi_dci0_dci_pdu_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &dci_pdu_rel10->cross_carrier_scheduling_flag, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->carrier_indicator, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->size_of_cqi_csi_feild, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->srs_flag, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->srs_request, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->resource_allocation_flag, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->resource_allocation_type, end) && - pull32(ppReadPackedMsg, &dci_pdu_rel10->resource_block_coding, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->mcs_2, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->new_data_indication_2, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->number_of_antenna_ports, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->tpmi, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->total_dci_length_including_padding, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel10->n_ul_rb, end)); -} - -static uint8_t unpack_hi_dci0_dci_pdu_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_hi_dci0_dci_pdu_rel12_t *dci_pdu_rel12 = (nfapi_hi_dci0_dci_pdu_rel12_t *)tlv; - return ( pull8(ppReadPackedMsg, &dci_pdu_rel12->pscch_resource, end) && - pull8(ppReadPackedMsg, &dci_pdu_rel12->time_resource_pattern, end)); -} - -static uint8_t unpack_hi_dci0_mpdcch_dci_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *value = (nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &value->mpdcch_narrowband, end) && - pull8(ppReadPackedMsg, &value->number_of_prb_pairs, end) && - pull8(ppReadPackedMsg, &value->resource_block_assignment, end) && - pull8(ppReadPackedMsg, &value->mpdcch_transmission_type, end) && - pull8(ppReadPackedMsg, &value->start_symbol, end) && - pull8(ppReadPackedMsg, &value->ecce_index, end) && - pull8(ppReadPackedMsg, &value->aggreagation_level, end) && - pull8(ppReadPackedMsg, &value->rnti_type, end) && - pull16(ppReadPackedMsg, &value->rnti, end) && - pull8(ppReadPackedMsg, &value->ce_mode, end) && - pull16(ppReadPackedMsg, &value->drms_scrambling_init, end) && - pull16(ppReadPackedMsg, &value->initial_transmission_sf_io, end) && - pull16(ppReadPackedMsg, &value->transmission_power, end) && - pull8(ppReadPackedMsg, &value->dci_format, end) && - pull8(ppReadPackedMsg, &value->resource_block_start, end) && - pull8(ppReadPackedMsg, &value->number_of_resource_blocks, end) && - pull8(ppReadPackedMsg, &value->mcs, end) && - pull8(ppReadPackedMsg, &value->pusch_repetition_levels, end) && - pull8(ppReadPackedMsg, &value->frequency_hopping_flag, end) && - pull8(ppReadPackedMsg, &value->new_data_indication, end) && - pull8(ppReadPackedMsg, &value->harq_process, end) && - pull8(ppReadPackedMsg, &value->redudency_version, end) && - pull8(ppReadPackedMsg, &value->tpc, end) && - pull8(ppReadPackedMsg, &value->csi_request, end) && - pull8(ppReadPackedMsg, &value->ul_inex, end) && - pull8(ppReadPackedMsg, &value->dai_presence_flag, end) && - pull8(ppReadPackedMsg, &value->dl_assignment_index, end) && - pull8(ppReadPackedMsg, &value->srs_request, end) && - pull8(ppReadPackedMsg, &value->dci_subframe_repetition_number, end) && - pull32(ppReadPackedMsg, &value->tcp_bitmap, end) && - pull8(ppReadPackedMsg, &value->total_dci_length_include_padding, end) && - pull8(ppReadPackedMsg, &value->number_of_tx_antenna_ports, end) && - pullarray16(ppReadPackedMsg, value->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, value->number_of_tx_antenna_ports, end)); -} - -static uint8_t unpack_hi_dci0_npdcch_dci_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *value = (nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &value->ncce_index, end) && - pull8(ppReadPackedMsg, &value->aggregation_level, end) && - pull8(ppReadPackedMsg, &value->start_symbol, end) && - pull16(ppReadPackedMsg, &value->rnti, end) && - pull8(ppReadPackedMsg, &value->scrambling_reinitialization_batch_index, end) && - pull8(ppReadPackedMsg, &value->nrs_antenna_ports_assumed_by_the_ue, end) && - pull8(ppReadPackedMsg, &value->subcarrier_indication, end) && - pull8(ppReadPackedMsg, &value->resource_assignment, end) && - pull8(ppReadPackedMsg, &value->scheduling_delay, end) && - pull8(ppReadPackedMsg, &value->mcs, end) && - pull8(ppReadPackedMsg, &value->redudancy_version, end) && - pull8(ppReadPackedMsg, &value->repetition_number, end) && - pull8(ppReadPackedMsg, &value->new_data_indicator, end) && - pull8(ppReadPackedMsg, &value->dci_subframe_repetition_number, end)); -} - -static uint8_t unpack_hi_dci0_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_hi_dci0_request_body_t *value = (nfapi_hi_dci0_request_body_t *)tlv; - - if(!(pull16(ppReadPackedMsg, &value->sfnsf, end) && - pull8(ppReadPackedMsg, &value->number_of_dci, end) && - pull8(ppReadPackedMsg, &value->number_of_hi, end))) - return 0; - - uint8_t totalNumPdus = value->number_of_hi + value->number_of_dci; - - if(totalNumPdus > NFAPI_HI_DCI0_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of dci0 pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, totalNumPdus, NFAPI_HI_DCI0_MAX_PDU); - return 0; - } - - if(totalNumPdus > 0) { - value->hi_dci0_pdu_list = (nfapi_hi_dci0_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_hi_dci0_request_pdu_t) * totalNumPdus, config); - - if(value->hi_dci0_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate hi dci0 pdu list (count:%d)\n", __FUNCTION__, totalNumPdus); - return 0; - } - } else { - value->hi_dci0_pdu_list = 0; - } - - uint8_t i; - - for(i = 0; i < totalNumPdus; ++i) { - nfapi_hi_dci0_request_pdu_t *pdu = &(value->hi_dci0_pdu_list[i]); - - if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && - pull8(ppReadPackedMsg, &pdu->pdu_size, end))) - return 0; - - uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; - - if(packedPduEnd > end) { - // pdu end if past buffer end - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s pdu size to big %d %d\n", __FUNCTION__, packedPduEnd, end); - return 0; - } - - switch(pdu->pdu_type) { - case NFAPI_HI_DCI0_HI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_HI_DCI0_REQUEST_HI_PDU_REL8_TAG, &pdu->hi_pdu.hi_pdu_rel8, &unpack_hi_dci0_hi_pdu_rel8_value}, - { NFAPI_HI_DCI0_REQUEST_HI_PDU_REL10_TAG, &pdu->hi_pdu.hi_pdu_rel10, &unpack_hi_dci0_hi_pdu_rel10_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_HI_DCI0_DCI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL8_TAG, &pdu->dci_pdu.dci_pdu_rel8, &unpack_hi_dci0_dci_pdu_rel8_value}, - { NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL10_TAG, &pdu->dci_pdu.dci_pdu_rel10, &unpack_hi_dci0_dci_pdu_rel10_value}, - { NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL12_TAG, &pdu->dci_pdu.dci_pdu_rel12, &unpack_hi_dci0_dci_pdu_rel12_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_HI_DCI0_EPDCCH_DCI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL8_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel8, &unpack_hi_dci0_dci_pdu_rel8_value}, - { NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL10_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel10, &unpack_hi_dci0_dci_pdu_rel10_value}, - { NFAPI_HI_DCI0_REQUEST_EPDCCH_PARAMETERS_REL11_TAG, &pdu->epdcch_dci_pdu.epdcch_parameters_rel11, &unpack_dl_config_epdcch_params_rel11_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_HI_DCI0_MPDCCH_DCI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_HI_DCI0_REQUEST_MPDCCH_DCI_PDU_REL13_TAG, &pdu->mpdcch_dci_pdu.mpdcch_dci_pdu_rel13, &unpack_hi_dci0_mpdcch_dci_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_HI_DCI0_NPDCCH_DCI_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_HI_DCI0_REQUEST_NPDCCH_DCI_PDU_REL13_TAG, &pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13, &unpack_hi_dci0_npdcch_dci_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); - } - break; - }; - } - - return 1; -} -//unpack_ul_dci_pdu_list_value - -static uint8_t unpack_ul_dci_pdu_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { - nfapi_nr_ul_dci_request_pdus_t *value = (nfapi_nr_ul_dci_request_pdus_t *)msg; - - for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) { - if(!pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].RNTI, end) && - pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingId, end) && - pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingRNTI, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].CceIndex, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].AggregationLevel, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].beta_PDCCH_1_0, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].powerControlOffsetSS, end) && - pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, end) && - pullarray8(ppReadPackedMsg, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].Payload, DCI_PAYLOAD_BYTE_LEN, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, end)); - - return 0; - } - - return (pull16(ppReadPackedMsg, &value->PDUType, end) && - pull16(ppReadPackedMsg, &value->PDUSize, end) && - pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.BWPSize, end) && - pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.BWPStart, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.SubcarrierSpacing, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.CyclicPrefix, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.StartSymbolIndex, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.DurationSymbols, end) && - pullarray8(ppReadPackedMsg, value->pdcch_pdu.pdcch_pdu_rel15.FreqDomainResource, 6, 6, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.CceRegMappingType, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.RegBundleSize, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.InterleaverSize, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.CoreSetType, end) && - pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.ShiftIndex, end) && - pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.precoderGranularity, end) && - pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.numDlDci, end)); -} - -static uint8_t unpack_ul_dci_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nr_ul_dci_request_t *pNfapiMsg = (nfapi_nr_ul_dci_request_t *)msg; - - if (!(pull16(ppReadPackedMsg, &pNfapiMsg->SFN, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && - pull8(ppReadPackedMsg, &pNfapiMsg->numPdus, end) - )) - return 0; - - for(int i=0; i< pNfapiMsg->numPdus; i++) { - if (!unpack_ul_dci_pdu_list_value(ppReadPackedMsg, end, &pNfapiMsg->ul_dci_pdu_list[i])) - return 0; - } - - return 1; -} - -static uint8_t unpack_hi_dci0_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_hi_dci0_request_t *pNfapiMsg = (nfapi_hi_dci0_request_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_HI_DCI0_REQUEST_BODY_TAG, &pNfapiMsg->hi_dci0_request_body, &unpack_hi_dci0_request_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} -static uint8_t unpack_tx_data_pdu_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { - nfapi_nr_pdu_t *pNfapiMsg = (nfapi_nr_pdu_t *)msg; - - if(!(pull32(ppReadPackedMsg, &pNfapiMsg->num_TLV, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->PDU_index, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->PDU_length, end) - )) - return 0; - - uint16_t i = 0; - uint16_t total_number_of_tlvs = pNfapiMsg->num_TLV; - - for(; i < total_number_of_tlvs; ++i) { - if (!(pull16(ppReadPackedMsg, &pNfapiMsg->TLVs[i].length, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->TLVs[i].tag, end))) - return 0; - - switch(pNfapiMsg->TLVs[i].tag) { - case 0: { - if(!pullarray32(ppReadPackedMsg, pNfapiMsg->TLVs[i].value.direct, 16384, pNfapiMsg->TLVs[i].length, end)) - return 0; - - break; - } - - case 1: { - if(!pullarray32(ppReadPackedMsg, pNfapiMsg->TLVs[i].value.ptr, pNfapiMsg->TLVs[i].length, pNfapiMsg->TLVs[i].length, end)) - return 0; - - break; - } - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid tag value %d \n", pNfapiMsg->TLVs[i].tag ); - break; - } - } - } - - return 1; -} - -static uint8_t unpack_tx_data_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nr_tx_data_request_t *pNfapiMsg = (nfapi_nr_tx_data_request_t *)msg; - - if(!(pull16(ppReadPackedMsg, &pNfapiMsg->SFN, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && - pull16(ppReadPackedMsg, &pNfapiMsg->Number_of_PDUs, end))) - return 0; - - for(int i=0; i< pNfapiMsg->Number_of_PDUs; i++) { - if (!unpack_tx_data_pdu_list_value(ppReadPackedMsg, end, &pNfapiMsg->pdu_list[i])) - return 0; - } - - return 1; -} - -static uint8_t unpack_tx_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - uint8_t proceed = 1; - nfapi_tx_request_t *pNfapiMsg = (nfapi_tx_request_t *)msg; - - if(pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) == 0) - return 0; - - while (((uint8_t *)(*ppReadPackedMsg) < end) && proceed) { - nfapi_tl_t generic_tl; - - if(unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) - return 0; - - switch(generic_tl.tag) { - case NFAPI_TX_REQUEST_BODY_TAG: { - pNfapiMsg->tx_request_body.tl = generic_tl; - - if( pull16(ppReadPackedMsg, &pNfapiMsg->tx_request_body.number_of_pdus, end) == 0) - return 0; - - if(pNfapiMsg->tx_request_body.number_of_pdus > NFAPI_TX_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of tx pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, pNfapiMsg->tx_request_body.number_of_pdus, NFAPI_TX_MAX_PDU); - return 0; - } - - if(pNfapiMsg->tx_request_body.number_of_pdus > 0) { - pNfapiMsg->tx_request_body.tx_pdu_list = (nfapi_tx_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_tx_request_pdu_t) * pNfapiMsg->tx_request_body.number_of_pdus, config); - - if(pNfapiMsg->tx_request_body.tx_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate tx pdu list (count:%d)\n", __FUNCTION__, pNfapiMsg->tx_request_body.number_of_pdus); - return 0; - } - } else { - pNfapiMsg->tx_request_body.tx_pdu_list = 0; - } - - uint16_t i; - uint16_t totalNumPdus = pNfapiMsg->tx_request_body.number_of_pdus; - - for(i = 0; i < totalNumPdus; ++i) { - nfapi_tx_request_pdu_t *pdu = &(pNfapiMsg->tx_request_body.tx_pdu_list[i]); - - if (pdu) { - uint16_t length = 0; - uint16_t index = 0; - - if(!(pull16(ppReadPackedMsg, &length, end) && - pull16(ppReadPackedMsg, &index, end))) - return 0; - - pdu->pdu_length = length; - pdu->pdu_index = index; - // TODO : May need to rethink this bit - pdu->num_segments = 1; - pdu->segments[0].segment_length = pdu->pdu_length; - pdu->segments[0].segment_data = nfapi_p7_allocate(pdu->pdu_length, config); - - if(pdu->segments[0].segment_data) { - if(!pullarray8(ppReadPackedMsg, pdu->segments[0].segment_data, pdu->segments[0].segment_length, pdu->segments[0].segment_length, end)) - return 0; - - if (pdu->segments[0].segment_length == 3) { - NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() BCH? segment_data:%x %x %x\n", __FUNCTION__, - pdu->segments[0].segment_data[0], - pdu->segments[0].segment_data[1], - pdu->segments[0].segment_data[2] - ); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "unpack_tx_request: Failed to allocate pdu (len:%d) %d/%d %d\n", pdu->pdu_length, totalNumPdus, i, pdu->pdu_index); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "NULL pdu\n"); - } - } - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "unpack_tx_request FIXME : Invalid pdu type %d \n", generic_tl.tag ); - } - break; - }; - } - - return 1; -} - -static uint8_t unpack_ue_release_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - uint8_t proceed = 1; - nfapi_ue_release_request_t *pNfapiMsg = (nfapi_ue_release_request_t *)msg; - - if(pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) == 0) - return 0; - - while (((uint8_t *)(*ppReadPackedMsg) < end) && proceed) { - nfapi_tl_t generic_tl; - - if(unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) - return 0; - - switch(generic_tl.tag) { - case NFAPI_UE_RELEASE_BODY_TAG: { - pNfapiMsg->ue_release_request_body.tl = generic_tl; - - if( pull16(ppReadPackedMsg, &pNfapiMsg->ue_release_request_body.number_of_TLVs, end) == 0) - return 0; - - if(pNfapiMsg->ue_release_request_body.number_of_TLVs > NFAPI_RELEASE_MAX_RNTI) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of relese rnti's exceed maxium (count:%d max:%d)\n", __FUNCTION__, pNfapiMsg->ue_release_request_body.number_of_TLVs, NFAPI_RELEASE_MAX_RNTI); - return 0; - } else { - uint8_t j; - uint16_t num = pNfapiMsg->ue_release_request_body.number_of_TLVs; - - for(j = 0; j < num; ++j) { - if(pull16(ppReadPackedMsg, &pNfapiMsg->ue_release_request_body.ue_release_request_TLVs_list[j].rnti, end) == 0) { - return 0; - } - } - } - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "unpack_ue_release_request FIXME : Invalid type %d \n", generic_tl.tag ); - } - break; - }; - } - - return 1; -} - -static uint8_t unpack_harq_indication_tdd_harq_data_bundling(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_harq_data_bundling_t *value = (nfapi_harq_indication_tdd_harq_data_bundling_t *)tlv; - return (pull8(ppReadPackedMsg, &value->value_0, end) && - pull8(ppReadPackedMsg, &value->value_1, end)); -} - -static uint8_t unpack_harq_indication_tdd_harq_data_multiplexing(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_harq_data_multiplexing_t *value = (nfapi_harq_indication_tdd_harq_data_multiplexing_t *)tlv; - return (pull8(ppReadPackedMsg, &value->value_0, end) && - pull8(ppReadPackedMsg, &value->value_1, end) && - pull8(ppReadPackedMsg, &value->value_2, end) && - pull8(ppReadPackedMsg, &value->value_3, end)); -} -static uint8_t unpack_harq_indication_tdd_harq_data_special_bundling(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_harq_data_special_bundling_t *value = (nfapi_harq_indication_tdd_harq_data_special_bundling_t *)tlv; - return ( pull8(ppReadPackedMsg, &value->value_0, end)); -} -static uint8_t unpack_harq_indication_tdd_harq_data(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_harq_data_t *value = (nfapi_harq_indication_tdd_harq_data_t *)tlv; - return (pull8(ppReadPackedMsg, &value->value_0, end)); -} - -static uint8_t unpack_harq_indication_tdd_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_rel8_t *value = (nfapi_harq_indication_tdd_rel8_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &value->mode, end) && - pull8(ppReadPackedMsg, &value->number_of_ack_nack, end))) - return 0; - - uint8_t result = 0; - - switch(value->mode) { - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: - result = unpack_harq_indication_tdd_harq_data_bundling(&value->harq_data.bundling, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: - result = unpack_harq_indication_tdd_harq_data_multiplexing(&value->harq_data.multiplex, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: - result = unpack_harq_indication_tdd_harq_data_special_bundling(&value->harq_data.special_bundling, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: - result = 1; - break; - - default: - // TODO add error message - return 0; - break; - } - - return result; -} - -static uint8_t unpack_harq_indication_tdd_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_rel9_t *value = (nfapi_harq_indication_tdd_rel9_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &value->mode, end) && - pull8(ppReadPackedMsg, &value->number_of_ack_nack, end))) - return 0; - - if(value->number_of_ack_nack > NFAPI_MAX_NUMBER_ACK_NACK_TDD) { - // TODO : add error message - return 0; - } - - uint16_t idx = 0; - - for(idx = 0; idx < value->number_of_ack_nack; ++idx) { - uint8_t result = 0; - - switch(value->mode) { - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].bundling, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].multiplex, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: - result = unpack_harq_indication_tdd_harq_data_special_bundling(&value->harq_data[idx].special_bundling, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].channel_selection, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_3, ppReadPackedMsg, end); - break; - - default: - // TODO add error message - return 0; - break; - } - - if(result == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_harq_indication_tdd_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_tdd_rel13_t *value = (nfapi_harq_indication_tdd_rel13_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &value->mode, end) && - pull16(ppReadPackedMsg, &value->number_of_ack_nack, end))) - return 0; - - if(value->number_of_ack_nack > NFAPI_MAX_NUMBER_ACK_NACK_TDD) { - // TODO : add error message - return 0; - } - - uint16_t idx = 0; - - for(idx = 0; idx < value->number_of_ack_nack; ++idx) { - uint8_t result = 0; - - switch(value->mode) { - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].bundling, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].multiplex, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: - result = unpack_harq_indication_tdd_harq_data_special_bundling(&value->harq_data[idx].special_bundling, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].channel_selection, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_3, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_4: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_4, ppReadPackedMsg, end); - break; - - case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_5: - result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_5, ppReadPackedMsg, end); - break; - - default: - // TODO add error message - return 0; - break; - } - - if(result == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_harq_indication_fdd_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_fdd_rel8_t *value = (nfapi_harq_indication_fdd_rel8_t *)tlv; - return (pull8(ppReadPackedMsg, &value->harq_tb1, end) && - pull8(ppReadPackedMsg, &value->harq_tb2, end)); -} - -static uint8_t unpack_harq_indication_fdd_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_fdd_rel9_t *value = (nfapi_harq_indication_fdd_rel9_t *)tlv; - return (pull8(ppReadPackedMsg, &value->mode, end) && - pull8(ppReadPackedMsg, &value->number_of_ack_nack, end) && - pullarray8(ppReadPackedMsg, value->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL9_MAX, value->number_of_ack_nack, end)); -} - -static uint8_t unpack_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_harq_indication_fdd_rel13_t *value = (nfapi_harq_indication_fdd_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &value->mode, end) && - pull16(ppReadPackedMsg, &value->number_of_ack_nack, end) && - pullarray8(ppReadPackedMsg, value->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL13_MAX, value->number_of_ack_nack, end)); -} - -static uint8_t unpack_ul_cqi_information_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_ul_cqi_information_t *value = (nfapi_ul_cqi_information_t *)tlv; - return (pull8(ppReadPackedMsg, &value->ul_cqi, end) && - pull8(ppReadPackedMsg, &value->channel, end)); -} - - - -static uint8_t unpack_harq_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_harq_indication_body_t *value = (nfapi_harq_indication_body_t *)tlv; - uint8_t *harqBodyEnd = *ppReadPackedMsg + value->tl.length; - - if(harqBodyEnd > end) - return 0; - - if(pull16(ppReadPackedMsg, &value->number_of_harqs, end) == 0) - return 0; - - if(value->number_of_harqs > NFAPI_HARQ_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of harq ind pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_harqs, NFAPI_HARQ_IND_MAX_PDU); - return 0; - } - - value->harq_pdu_list = (nfapi_harq_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_harq_indication_pdu_t) * value->number_of_harqs, config); - - if(value->harq_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate harq ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_harqs); - return 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_harqs; ++i) { - nfapi_harq_indication_pdu_t *pdu = &(value->harq_pdu_list[i]); - - if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) - return 0; - - uint8_t *harqPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; - unpack_tlv_t unpack_fns[] = { - { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, - { NFAPI_HARQ_INDICATION_TDD_REL8_TAG, &pdu->harq_indication_tdd_rel8, &unpack_harq_indication_tdd_rel8_value}, - { NFAPI_HARQ_INDICATION_TDD_REL9_TAG, &pdu->harq_indication_tdd_rel9, &unpack_harq_indication_tdd_rel9_value}, - { NFAPI_HARQ_INDICATION_TDD_REL13_TAG, &pdu->harq_indication_tdd_rel13, &unpack_harq_indication_tdd_rel13_value}, - { NFAPI_HARQ_INDICATION_FDD_REL8_TAG, &pdu->harq_indication_fdd_rel8, &unpack_harq_indication_fdd_rel8_value}, - { NFAPI_HARQ_INDICATION_FDD_REL9_TAG, &pdu->harq_indication_fdd_rel9, &unpack_harq_indication_fdd_rel9_value}, - { NFAPI_HARQ_INDICATION_FDD_REL13_TAG, &pdu->harq_indication_fdd_rel13, &unpack_harq_indication_fdd_rel13_value}, - { NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, &unpack_ul_cqi_information_value} - }; - - if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, harqPduInstanceEnd, 0, 0) == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_harq_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_harq_indication_t *pNfapiMsg = (nfapi_harq_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->harq_indication_body, &unpack_harq_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_crc_indication_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_crc_indication_rel8_t *crc_pdu_rel8 = (nfapi_crc_indication_rel8_t *)tlv; - return ( pull8(ppReadPackedMsg, &crc_pdu_rel8->crc_flag, end) ); -} - -static uint8_t unpack_crc_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_crc_indication_body_t *value = (nfapi_crc_indication_body_t *)tlv; - uint8_t *crcBodyEnd = *ppReadPackedMsg + value->tl.length; - - if(crcBodyEnd > end) - return 0; - - if(pull16(ppReadPackedMsg, &value->number_of_crcs, end) == 0) - return 0; - - if(value->number_of_crcs > NFAPI_CRC_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of crc ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_crcs, NFAPI_CRC_IND_MAX_PDU); - return 0; - } - - if(value->number_of_crcs > 0) { - value->crc_pdu_list = (nfapi_crc_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_crc_indication_pdu_t) * value->number_of_crcs, config); - - if(value->crc_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate crc ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_crcs); - return 0; - } - } else { - value->crc_pdu_list = 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_crcs; ++i) { - nfapi_crc_indication_pdu_t *pdu = &(value->crc_pdu_list[i]); - - if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) - return 0; - - uint8_t *crcPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; - unpack_tlv_t unpack_fns[] = { - { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, - { NFAPI_CRC_INDICATION_REL8_TAG, &pdu->crc_indication_rel8, unpack_crc_indication_rel8_value }, - }; - - if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, crcPduInstanceEnd, 0, 0) == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_crc_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_crc_indication_t *pNfapiMsg = (nfapi_crc_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_CRC_INDICATION_BODY_TAG, &pNfapiMsg->crc_indication_body, &unpack_crc_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_rx_indication_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_rx_indication_rel8_t *value = (nfapi_rx_indication_rel8_t *)tlv; - return (pull16(ppReadPackedMsg, &value->length, end) && - pull16(ppReadPackedMsg, &value->offset, end) && - pull8(ppReadPackedMsg, &value->ul_cqi, end) && - pull16(ppReadPackedMsg, &value->timing_advance, end)); -} -static uint8_t unpack_rx_indication_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_rx_indication_rel9_t *value = (nfapi_rx_indication_rel9_t *)tlv; - return (pull16(ppReadPackedMsg, &value->timing_advance_r9, end)); -} - -static uint8_t unpack_rx_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_rx_indication_body_t *value = (nfapi_rx_indication_body_t *)tlv; - // the rxBodyEnd points to the end of the cqi PDU's - uint8_t *rxBodyEnd = *ppReadPackedMsg + value->tl.length; - uint8_t *rxPduEnd = rxBodyEnd; - uint8_t *numberOfPdusAddress = *ppReadPackedMsg; - - if(rxBodyEnd > end) { - // pdu end is past buffer end - return 0; - } - - if(pull16(ppReadPackedMsg, &value->number_of_pdus, end) == 0) - return 0; - - if(value->number_of_pdus > NFAPI_RX_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of rx ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_RX_IND_MAX_PDU); - return 0; - } - - if(value->number_of_pdus > 0) { - value->rx_pdu_list = (nfapi_rx_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_rx_indication_pdu_t) * value->number_of_pdus, config); - - if(value->rx_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate rx ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); - return 0; - } - } else { - value->rx_pdu_list = 0; - } - - uint8_t i = 0; - nfapi_rx_indication_pdu_t *pdu = 0; - - while((uint8_t *)(*ppReadPackedMsg) < rxBodyEnd && (uint8_t *)(*ppReadPackedMsg) < rxPduEnd) { - nfapi_tl_t generic_tl; - - if( unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) - return 0; - - switch(generic_tl.tag) { - case NFAPI_RX_UE_INFORMATION_TAG: { - pdu = &(value->rx_pdu_list[i++]); - pdu->rx_ue_information.tl = generic_tl; - - if(unpack_rx_ue_information_value(&pdu->rx_ue_information, ppReadPackedMsg, end) == 0) - return 0; - } - break; - - case NFAPI_RX_INDICATION_REL8_TAG: { - if(pdu != 0) { - pdu->rx_indication_rel8.tl = generic_tl; - - if(unpack_rx_indication_rel8_value(&pdu->rx_indication_rel8, ppReadPackedMsg, end) == 0) - return 0; - - if(pdu->rx_indication_rel8.offset > 0) { - // Need to check that the data is within the tlv - if(numberOfPdusAddress + pdu->rx_indication_rel8.offset + pdu->rx_indication_rel8.length <= rxBodyEnd) { - // If this the first pdu set the rxPduEnd - if(numberOfPdusAddress + pdu->rx_indication_rel8.offset < rxPduEnd) { - rxPduEnd = numberOfPdusAddress + pdu->rx_indication_rel8.offset; - - if(rxPduEnd > end) { - // pdu end is past buffer end - return 0; - } - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME: the rx data is outside of the tlv\n"); - } - } - } - } - break; - - case NFAPI_RX_INDICATION_REL9_TAG: { - if(pdu != 0) { - pdu->rx_indication_rel9.tl = generic_tl; - - if(unpack_rx_indication_rel9_value(&pdu->rx_indication_rel9, ppReadPackedMsg, end) == 0) - return 0; - } - } - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "RX_ULSCH.indication Invalid pdu type %d \n", generic_tl.tag ); - } - break; - } - } - - uint8_t idx = 0; - - for(idx = 0; idx < value->number_of_pdus; ++idx) { - if(value->rx_pdu_list[idx].rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { - uint32_t length = value->rx_pdu_list[idx].rx_indication_rel8.length; - value->rx_pdu_list[idx].data = nfapi_p7_allocate(length, config); - - if(pullarray8(ppReadPackedMsg, value->rx_pdu_list[idx].data, length, length, end) == 0) { - return 0; - } - } - } - - return 1; -} - -static uint8_t unpack_rx_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_rx_indication_t *pNfapiMsg = (nfapi_rx_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_RX_INDICATION_BODY_TAG, &pNfapiMsg->rx_indication_body, &unpack_rx_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_preamble_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_preamble_pdu_rel8_t *preamble_pdu_rel8 = (nfapi_preamble_pdu_rel8_t *)tlv; - return (pull16(ppReadPackedMsg, &preamble_pdu_rel8->rnti, end) && - pull8(ppReadPackedMsg, &preamble_pdu_rel8->preamble, end) && - pull16(ppReadPackedMsg, &preamble_pdu_rel8->timing_advance, end)); -} - -static uint8_t unpack_preamble_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_preamble_pdu_rel9_t *preamble_pdu_rel9 = (nfapi_preamble_pdu_rel9_t *)tlv; - return pull16(ppReadPackedMsg, &preamble_pdu_rel9->timing_advance_r9, end); -} - -static uint8_t unpack_preamble_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_preamble_pdu_rel13_t *preamble_pdu_rel13 = (nfapi_preamble_pdu_rel13_t *)tlv; - return pull8(ppReadPackedMsg, &preamble_pdu_rel13->rach_resource_type, end); -} - -static uint8_t unpack_rach_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_rach_indication_body_t *value = (nfapi_rach_indication_body_t *)tlv; - uint8_t *rachBodyEnd = *ppReadPackedMsg + value->tl.length; - - if(rachBodyEnd > end) - return 0; - - if(pull16(ppReadPackedMsg, &value->number_of_preambles, end) == 0) - return 0; - - if(value->number_of_preambles > NFAPI_PREAMBLE_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of preamble du's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_preambles, NFAPI_PREAMBLE_MAX_PDU); - return 0; - } - - if(value->number_of_preambles > 0) { - value->preamble_list = (nfapi_preamble_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_preamble_pdu_t) * value->number_of_preambles, config); - - if(value->preamble_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate preamble pdu list (count:%d)\n", __FUNCTION__, value->number_of_preambles); - return 0; - } - } else { - value->preamble_list = 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_preambles; ++i) { - nfapi_preamble_pdu_t *pdu = &(value->preamble_list[i]); - - if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) - return 0; - - uint8_t *preamblePduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; - unpack_tlv_t unpack_fns[] = { - { NFAPI_PREAMBLE_REL8_TAG, &pdu->preamble_rel8, unpack_preamble_pdu_rel8_value }, - { NFAPI_PREAMBLE_REL9_TAG, &pdu->preamble_rel9, unpack_preamble_pdu_rel9_value }, - { NFAPI_PREAMBLE_REL13_TAG, &pdu->preamble_rel13, unpack_preamble_pdu_rel13_value }, - }; - - if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, preamblePduInstanceEnd, 0, 0) == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_rach_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_rach_indication_t *pNfapiMsg = (nfapi_rach_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_RACH_INDICATION_BODY_TAG, &pNfapiMsg->rach_indication_body, &unpack_rach_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_srs_indication_fdd_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_srs_indication_fdd_rel8_t *srs_pdu_fdd_rel8 = (nfapi_srs_indication_fdd_rel8_t *)tlv; - - if(!(pull16(ppReadPackedMsg, &srs_pdu_fdd_rel8->doppler_estimation, end) && - pull16(ppReadPackedMsg, &srs_pdu_fdd_rel8->timing_advance, end) && - pull8(ppReadPackedMsg, &srs_pdu_fdd_rel8->number_of_resource_blocks, end) && - pull8(ppReadPackedMsg, &srs_pdu_fdd_rel8->rb_start, end) && - pullarray8(ppReadPackedMsg, srs_pdu_fdd_rel8->snr, NFAPI_NUM_RB_MAX, srs_pdu_fdd_rel8->number_of_resource_blocks, end))) - return 0; - - return 1; -} - -static uint8_t unpack_srs_indication_fdd_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_srs_indication_fdd_rel9_t *srs_pdu_fdd_rel9 = (nfapi_srs_indication_fdd_rel9_t *)tlv; - return (pull16(ppReadPackedMsg, &srs_pdu_fdd_rel9->timing_advance_r9, end)); -} - -static uint8_t unpack_srs_indication_tdd_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_srs_indication_ttd_rel10_t *srs_pdu_tdd_rel10 = (nfapi_srs_indication_ttd_rel10_t *)tlv; - return (pull8(ppReadPackedMsg, &srs_pdu_tdd_rel10->uppts_symbol, end)); -} - -static uint8_t unpack_srs_indication_fdd_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_srs_indication_fdd_rel11_t *srs_pdu_fdd_rel11 = (nfapi_srs_indication_fdd_rel11_t *)tlv; - return ( pull16(ppReadPackedMsg, &srs_pdu_fdd_rel11->ul_rtoa, end)); -} - -static uint8_t unpack_tdd_channel_measurement_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_tdd_channel_measurement_t *value = (nfapi_tdd_channel_measurement_t *)tlv; - - if(!(pull8(ppReadPackedMsg, &value->num_prb_per_subband, end) && - pull8(ppReadPackedMsg, &value->number_of_subbands, end) && - pull8(ppReadPackedMsg, &value->num_atennas, end))) - return 0; - - if(value->number_of_subbands > NFAPI_MAX_NUM_SUBBANDS) { - // todo : add error - return 0; - } - - if(value->num_atennas > NFAPI_MAX_NUM_PHYSICAL_ANTENNAS) { - // todo : add error - return 0; - } - - uint8_t idx = 0; - - for(idx = 0; idx < value->number_of_subbands; ++idx) { - if(!(pull8(ppReadPackedMsg, &value->subands[idx].subband_index, end) && - pullarray16(ppReadPackedMsg, value->subands[idx].channel, NFAPI_MAX_NUM_PHYSICAL_ANTENNAS, value->num_atennas, end))) - return 0; - } - - return 1; -} - - -static uint8_t unpack_srs_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_srs_indication_body_t *value = (nfapi_srs_indication_body_t *)tlv; - uint8_t *srsBodyEnd = *ppReadPackedMsg + value->tl.length; - - if(srsBodyEnd > end) - return 0; - - if(pull8(ppReadPackedMsg, &value->number_of_ues, end) == 0) - return 0; - - if(value->number_of_ues > NFAPI_SRS_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of srs ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_ues, NFAPI_SRS_IND_MAX_PDU); - return 0; - } - - if(value->number_of_ues > 0) { - value->srs_pdu_list = (nfapi_srs_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_srs_indication_pdu_t) * value->number_of_ues, config); - - if(value->srs_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate srs ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_ues); - return 0; - } - } else { - value->srs_pdu_list = 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_ues; ++i) { - nfapi_srs_indication_pdu_t *pdu = &(value->srs_pdu_list[i]); - - if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) - return 0; - - uint8_t *srsPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; - unpack_tlv_t unpack_fns[] = { - { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, - { NFAPI_SRS_INDICATION_FDD_REL8_TAG, &pdu->srs_indication_fdd_rel8, unpack_srs_indication_fdd_rel8_value}, - { NFAPI_SRS_INDICATION_FDD_REL9_TAG, &pdu->srs_indication_fdd_rel9, unpack_srs_indication_fdd_rel9_value}, - { NFAPI_SRS_INDICATION_TDD_REL10_TAG, &pdu->srs_indication_tdd_rel10, unpack_srs_indication_tdd_rel10_value}, - { NFAPI_SRS_INDICATION_FDD_REL11_TAG, &pdu->srs_indication_fdd_rel11, unpack_srs_indication_fdd_rel11_value}, - { NFAPI_TDD_CHANNEL_MEASUREMENT_TAG, &pdu->tdd_channel_measurement, unpack_tdd_channel_measurement_value}, - }; - - if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, srsPduInstanceEnd, 0, 0) == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_srs_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_srs_indication_t *pNfapiMsg = (nfapi_srs_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_SRS_INDICATION_BODY_TAG, &pNfapiMsg->srs_indication_body, &unpack_srs_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_sr_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_sr_indication_body_t *value = (nfapi_sr_indication_body_t *)tlv; - uint8_t *srBodyEnd = *ppReadPackedMsg + value->tl.length; - - if(srBodyEnd > end) - return 0; - - if(pull16(ppReadPackedMsg, &value->number_of_srs, end) == 0) - return 0; - - if(value->number_of_srs > NFAPI_SR_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of sr ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_srs, NFAPI_SR_IND_MAX_PDU); - return 0; - } - - if(value->number_of_srs > 0) { - value->sr_pdu_list = (nfapi_sr_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_sr_indication_pdu_t) * value->number_of_srs, config); - - if(value->sr_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate sr ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_srs); - return 0; - } - } else { - value->sr_pdu_list = 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_srs; ++i) { - nfapi_sr_indication_pdu_t *pdu = &(value->sr_pdu_list[i]); - - if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) - return 0; - - uint8_t *srPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; - unpack_tlv_t unpack_fns[] = { - { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, - { NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, unpack_ul_cqi_information_value }, - }; - - if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, srPduInstanceEnd, 0, 0) == 0) - return 0; - } - - return 1; -} - -static int unpack_sr_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_sr_indication_t *pNfapiMsg = (nfapi_sr_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_SR_INDICATION_BODY_TAG, &pNfapiMsg->sr_indication_body, &unpack_sr_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} -static uint8_t unpack_cqi_indication_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_cqi_indication_rel8_t *cqi_pdu_rel8 = (nfapi_cqi_indication_rel8_t *)tlv; - return (pull16(ppReadPackedMsg, &cqi_pdu_rel8->length, end) && - pull16(ppReadPackedMsg, &cqi_pdu_rel8->data_offset, end) && - pull8(ppReadPackedMsg, &cqi_pdu_rel8->ul_cqi, end) && - pull8(ppReadPackedMsg, &cqi_pdu_rel8->ri, end) && - pull16(ppReadPackedMsg, &cqi_pdu_rel8->timing_advance, end)); -} - -static uint8_t unpack_cqi_indication_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_cqi_indication_rel9_t *cqi_pdu_rel9 = (nfapi_cqi_indication_rel9_t *)tlv; - - if(!(pull16(ppReadPackedMsg, &cqi_pdu_rel9->length, end) && - pull16(ppReadPackedMsg, &cqi_pdu_rel9->data_offset, end) && - pull8(ppReadPackedMsg, &cqi_pdu_rel9->ul_cqi, end) && - pull8(ppReadPackedMsg, &cqi_pdu_rel9->number_of_cc_reported, end))) - return 0; - - if(cqi_pdu_rel9->number_of_cc_reported > NFAPI_CC_MAX) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : out of bound array\n"); - return 0; - } - - if(!(pullarray8(ppReadPackedMsg, cqi_pdu_rel9->ri, NFAPI_CC_MAX, cqi_pdu_rel9->number_of_cc_reported, end) && - pull16(ppReadPackedMsg, &cqi_pdu_rel9->timing_advance, end) && - pull16(ppReadPackedMsg, &cqi_pdu_rel9->timing_advance_r9, end))) - return 0; - - return 1; -} - -static uint8_t unpack_cqi_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_cqi_indication_body_t *value = (nfapi_cqi_indication_body_t *)tlv; - // the cqiBodyEnd points to the end of the cqi PDU's - uint8_t *cqiBodyEnd = *ppReadPackedMsg + value->tl.length; - - //uint8_t* cqiPduEnd = cqiBodyEnd; - //uint8_t* numberOfPdusAddress = *ppReadPackedMsg; - - if(cqiBodyEnd > end) - return 0; - - if(pull16(ppReadPackedMsg, &value->number_of_cqis, end) == 0) - return 0; - - if(value->number_of_cqis > NFAPI_CQI_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of cqi ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_cqis, NFAPI_CQI_IND_MAX_PDU); - return -1; - } - - if(value->number_of_cqis > 0) { - value->cqi_pdu_list = (nfapi_cqi_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_cqi_indication_pdu_t) * value->number_of_cqis, config); - - if(value->cqi_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate cqi ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_cqis); - return 0; - } - } else { - value->cqi_pdu_list = 0; - } - - if(value->number_of_cqis > 0) { - value->cqi_raw_pdu_list = (nfapi_cqi_indication_raw_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_cqi_indication_raw_pdu_t) * value->number_of_cqis, config); - - if(value->cqi_raw_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate raw cqi ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_cqis); - return 0; - } - } else { - value->cqi_raw_pdu_list = 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_cqis; ++i) { - nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); - memset(pdu, 0, sizeof(nfapi_cqi_indication_pdu_t)); - - if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) - return 0; - - uint8_t *cqiPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; - - while((uint8_t *)(*ppReadPackedMsg) < cqiPduInstanceEnd) { - nfapi_tl_t generic_tl; - - if(unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) - return 0; - - switch(generic_tl.tag) { - case NFAPI_RX_UE_INFORMATION_TAG: - pdu->rx_ue_information.tl = generic_tl; - - if(unpack_rx_ue_information_value(&pdu->rx_ue_information, ppReadPackedMsg, end) == 0) - return 0; - - break; - - case NFAPI_CQI_INDICATION_REL8_TAG: - pdu->cqi_indication_rel8.tl = generic_tl; - - if(unpack_cqi_indication_rel8_value(&pdu->cqi_indication_rel8, ppReadPackedMsg, end) == 0) - return 0; - - break; - - case NFAPI_CQI_INDICATION_REL9_TAG: - pdu->cqi_indication_rel9.tl = generic_tl; - - if(unpack_cqi_indication_rel9_value(&pdu->cqi_indication_rel9, ppReadPackedMsg, end) == 0) - return 0; - - break; - - case NFAPI_UL_CQI_INFORMATION_TAG: - pdu->ul_cqi_information.tl = generic_tl; - - if(unpack_ul_cqi_information_value(&pdu->ul_cqi_information, ppReadPackedMsg, end) == 0) - return 0; - - break; - - default: { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "RX_CQI.indication Invalid pdu type %d \n", generic_tl.tag ); - } - break; - }; - } - } - - uint8_t idx = 0; - - for(idx = 0; idx < value->number_of_cqis; ++idx) { - if(value->cqi_pdu_list[idx].cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { - if(pullarray8(ppReadPackedMsg, &(value->cqi_raw_pdu_list[idx].pdu[0]), NFAPI_CQI_RAW_MAX_LEN, value->cqi_pdu_list[idx].cqi_indication_rel8.length, end) == 0) - return 0; - } else if(value->cqi_pdu_list[idx].cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { - if(pullarray8(ppReadPackedMsg, &(value->cqi_raw_pdu_list[idx].pdu[0]), NFAPI_CQI_RAW_MAX_LEN, value->cqi_pdu_list[idx].cqi_indication_rel9.length, end) == 0) - return 0; - } - } - - return 1; -} - -static uint8_t unpack_cqi_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_cqi_indication_t *pNfapiMsg = (nfapi_cqi_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_CQI_INDICATION_BODY_TAG, &pNfapiMsg->cqi_indication_body, &unpack_cqi_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} -static uint8_t unpack_lbt_pdsch_req_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_lbt_pdsch_req_pdu_rel13_t *value = (nfapi_lbt_pdsch_req_pdu_rel13_t *)tlv; - return (pull32(ppReadPackedMsg, &value->handle, end) && - pull32(ppReadPackedMsg, &value->mp_cca, end) && - pull32(ppReadPackedMsg, &value->n_cca, end) && - pull32(ppReadPackedMsg, &value->offset, end) && - pull32(ppReadPackedMsg, &value->lte_txop_sf, end) && - pull16(ppReadPackedMsg, &value->txop_sfn_sf_end, end) && - pull32(ppReadPackedMsg, &value->lbt_mode, end)); -} - -static uint8_t unpack_lbt_drs_req_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_lbt_drs_req_pdu_rel13_t *value = (nfapi_lbt_drs_req_pdu_rel13_t *)tlv; - return (pull32(ppReadPackedMsg, &value->handle, end) && - pull32(ppReadPackedMsg, &value->offset, end) && - pull16(ppReadPackedMsg, &value->sfn_sf_end, end) && - pull32(ppReadPackedMsg, &value->lbt_mode, end)); -} - - -static uint8_t unpack_lbt_config_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_lbt_dl_config_request_body_t *value = (nfapi_lbt_dl_config_request_body_t *)tlv; - - if(pull16(ppReadPackedMsg, &value->number_of_pdus, end) == 0) - return 0; - - if(value->number_of_pdus > NFAPI_LBT_DL_CONFIG_REQ_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of lbt dl config pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_LBT_DL_CONFIG_REQ_MAX_PDU); - return 0; - } - - if(value->number_of_pdus) { - value->lbt_dl_config_req_pdu_list = (nfapi_lbt_dl_config_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_lbt_dl_config_request_pdu_t) * value->number_of_pdus, config); - - if(value->lbt_dl_config_req_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate lbt dl config pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); - return 0; - } - } else { - value->lbt_dl_config_req_pdu_list = 0; - } - - uint16_t i; - uint16_t total_number_of_pdus = value->number_of_pdus; - - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_lbt_dl_config_request_pdu_t *pdu = &(value->lbt_dl_config_req_pdu_list[i]); - - if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && - pull8(ppReadPackedMsg, &pdu->pdu_size, end))) - return 0; - - uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; - - if(packedPduEnd > end) - return 0; - - switch(pdu->pdu_type) { - case NFAPI_LBT_DL_CONFIG_REQUEST_PDSCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_LBT_PDSCH_REQ_PDU_REL13_TAG, &pdu->lbt_pdsch_req_pdu.lbt_pdsch_req_pdu_rel13, &unpack_lbt_pdsch_req_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST_DRS_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_LBT_DRS_REQ_PDU_REL13_TAG, &pdu->lbt_drs_req_pdu.lbt_drs_req_pdu_rel13, &unpack_lbt_drs_req_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - default: - NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL_CONFIG.request body invalid pdu type %d\n", pdu->pdu_type); - return 0; - } - } - - return 1; -} -static uint8_t unpack_lbt_dl_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_lbt_dl_config_request_t *pNfapiMsg = (nfapi_lbt_dl_config_request_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_LBT_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->lbt_dl_config_request_body, &unpack_lbt_config_request_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_lbt_pdsch_rsp_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_lbt_pdsch_rsp_pdu_rel13_t *value = (nfapi_lbt_pdsch_rsp_pdu_rel13_t *)tlv; - return (pull32(ppReadPackedMsg, &value->handle, end) && - pull32(ppReadPackedMsg, &value->result, end) && - pull32(ppReadPackedMsg, &value->lte_txop_symbols, end) && - pull32(ppReadPackedMsg, &value->initial_partial_sf, end)); -} -static uint8_t unpack_lbt_drs_rsp_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_lbt_drs_rsp_pdu_rel13_t *value = (nfapi_lbt_drs_rsp_pdu_rel13_t *)tlv; - return (pull32(ppReadPackedMsg, &value->handle, end) && - pull32(ppReadPackedMsg, &value->result, end)); -} - -static uint8_t unpack_lbt_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_lbt_dl_indication_body_t *value = (nfapi_lbt_dl_indication_body_t *)tlv; - - if(pull16(ppReadPackedMsg, &value->number_of_pdus, end) == 0) - return 0; - - if(value->number_of_pdus > NFAPI_LBT_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of lbt dl ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_LBT_IND_MAX_PDU); - return 0; - } - - if(value->number_of_pdus > 0) { - value->lbt_indication_pdu_list = (nfapi_lbt_dl_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_lbt_dl_indication_pdu_t) * value->number_of_pdus, config); - - if(value->lbt_indication_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate lbt dl ind config pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); - return 0; - } - } else { - value->lbt_indication_pdu_list = 0; - } - - uint16_t i; - uint16_t total_number_of_pdus = value->number_of_pdus; - - for(i = 0; i < total_number_of_pdus; ++i) { - nfapi_lbt_dl_indication_pdu_t *pdu = &(value->lbt_indication_pdu_list[i]); - - if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && - pull8(ppReadPackedMsg, &pdu->pdu_size, end))) - return 0; - - uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; - - if(packedPduEnd > end) - return 0; - - switch(pdu->pdu_type) { - case NFAPI_LBT_DL_RSP_PDSCH_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_LBT_PDSCH_RSP_PDU_REL13_TAG, &pdu->lbt_pdsch_rsp_pdu.lbt_pdsch_rsp_pdu_rel13, &unpack_lbt_pdsch_rsp_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - case NFAPI_LBT_DL_RSP_DRS_PDU_TYPE: { - unpack_tlv_t unpack_fns[] = { - { NFAPI_LBT_DRS_RSP_PDU_REL13_TAG, &pdu->lbt_drs_rsp_pdu.lbt_drs_rsp_pdu_rel13, &unpack_lbt_drs_rsp_pdu_rel13_value}, - }; - unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); - } - break; - - default: - NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL.indication body invalid pdu type %d\n", pdu->pdu_type); - return 0; - } - } - - return 1; -} -static uint8_t unpack_lbt_dl_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_lbt_dl_indication_t *pNfapiMsg = (nfapi_lbt_dl_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_LBT_DL_INDICATION_BODY_TAG, &pNfapiMsg->lbt_dl_indication_body, &unpack_lbt_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_nb_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nb_harq_indication_fdd_rel13_t *value = (nfapi_nb_harq_indication_fdd_rel13_t *)tlv; - return (pull8(ppReadPackedMsg, &value->harq_tb1, end)); -} - - -static uint8_t unpack_nb_harq_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nb_harq_indication_body_t *value = (nfapi_nb_harq_indication_body_t *)tlv; - uint8_t *nbharqBodyEnd = *ppReadPackedMsg + value->tl.length; - - if(nbharqBodyEnd > end) - return 0; - - if(pull16(ppReadPackedMsg, &value->number_of_harqs, end) == 0) - return 0; - - if(value->number_of_harqs > NFAPI_HARQ_IND_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of harq ind pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_harqs, NFAPI_HARQ_IND_MAX_PDU); - return 0; - } - - value->nb_harq_pdu_list = (nfapi_nb_harq_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_nb_harq_indication_pdu_t) * value->number_of_harqs, config); - - if(value->nb_harq_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate harq ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_harqs); - return 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_harqs; ++i) { - nfapi_nb_harq_indication_pdu_t *pdu = &(value->nb_harq_pdu_list[i]); - - if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) - return 0; - - uint8_t *harqPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; - unpack_tlv_t unpack_fns[] = { - { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, - { NFAPI_NB_HARQ_INDICATION_FDD_REL13_TAG, &pdu->nb_harq_indication_fdd_rel13, &unpack_nb_harq_indication_fdd_rel13_value}, - { NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, &unpack_ul_cqi_information_value} - }; - - if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, harqPduInstanceEnd, 0, 0) == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_nb_harq_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nb_harq_indication_t *pNfapiMsg = (nfapi_nb_harq_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_NB_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->nb_harq_indication_body, &unpack_nb_harq_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_nrach_indication_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { - nfapi_nrach_indication_pdu_rel13_t *value = (nfapi_nrach_indication_pdu_rel13_t *)tlv; - return (pull16(ppReadPackedMsg, &value->rnti, end) && - pull8(ppReadPackedMsg, &value->initial_sc, end) && - pull16(ppReadPackedMsg, &value->timing_advance, end) && - pull8(ppReadPackedMsg, &value->nrach_ce_level, end)); -} - -static uint8_t unpack_ue_release_resp(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_ue_release_response_t *pNfapiMsg = (nfapi_ue_release_response_t *)msg; - - if(pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) == 0) { - return 0; - } else { - NFAPI_TRACE(NFAPI_TRACE_INFO, "ue_release_response:error_code = %d\n", pNfapiMsg->error_code); - } - - return 1; -} - -static uint8_t unpack_nrach_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { - nfapi_nrach_indication_body_t *value = (nfapi_nrach_indication_body_t *)tlv; - uint8_t *nrachBodyEnd = *ppReadPackedMsg + value->tl.length; - - if(nrachBodyEnd > end) - return 0; - - if(pull8(ppReadPackedMsg, &value->number_of_initial_scs_detected, end) == 0) - return 0; - - if(value->number_of_initial_scs_detected > NFAPI_PREAMBLE_MAX_PDU) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of detected scs ind pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_initial_scs_detected, NFAPI_PREAMBLE_MAX_PDU); - return 0; - } - - value->nrach_pdu_list = (nfapi_nrach_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_nrach_indication_pdu_t) * value->number_of_initial_scs_detected, config); - - if(value->nrach_pdu_list == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate nrach ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_initial_scs_detected); - return 0; - } - - uint8_t i = 0; - - for(i = 0; i < value->number_of_initial_scs_detected; ++i) { - nfapi_nrach_indication_pdu_t *pdu = &(value->nrach_pdu_list[i]); - uint8_t *nrachPduInstanceEnd = *ppReadPackedMsg + 4 + 6; - unpack_tlv_t unpack_fns[] = { - { NFAPI_NRACH_INDICATION_REL13_TAG, &pdu->nrach_indication_rel13, &unpack_nrach_indication_rel13_value}, - }; - - if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, nrachPduInstanceEnd, 0, 0) == 0) - return 0; - } - - return 1; -} - -static uint8_t unpack_nrach_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nrach_indication_t *pNfapiMsg = (nfapi_nrach_indication_t *)msg; - unpack_p7_tlv_t unpack_fns[] = { - { NFAPI_NRACH_INDICATION_BODY_TAG, &pNfapiMsg->nrach_indication_body, &unpack_nrach_indication_body_value}, - }; - return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && - unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_nr_dl_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nr_dl_node_sync_t *pNfapiMsg = (nfapi_nr_dl_node_sync_t *)msg; - return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->delta_sfn_slot, end) && - unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_dl_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_dl_node_sync_t *pNfapiMsg = (nfapi_dl_node_sync_t *)msg; - return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->delta_sfn_sf, end) && - unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_nr_ul_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nr_ul_node_sync_t *pNfapiMsg = (nfapi_nr_ul_node_sync_t *)msg; - return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->t2, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->t3, end) && - unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_ul_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_ul_node_sync_t *pNfapiMsg = (nfapi_ul_node_sync_t *)msg; - return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->t2, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->t3, end) && - unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - -static uint8_t unpack_timing_info(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_timing_info_t *pNfapiMsg = (nfapi_timing_info_t *)msg; - return (pull32(ppReadPackedMsg, &pNfapiMsg->last_sfn_sf, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->time_since_last_timing_info, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->dl_config_jitter, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->tx_request_jitter, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->ul_config_jitter, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->hi_dci0_jitter, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->dl_config_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->tx_request_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->ul_config_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->hi_dci0_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->dl_config_earliest_arrival, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->tx_request_earliest_arrival, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->ul_config_earliest_arrival, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->hi_dci0_earliest_arrival, end) && - unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - - -static uint8_t unpack_nr_timing_info(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { - nfapi_nr_timing_info_t *pNfapiMsg = (nfapi_nr_timing_info_t *)msg; - return (pull32(ppReadPackedMsg, &pNfapiMsg->last_sfn, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->last_slot, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->time_since_last_timing_info, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->dl_tti_jitter, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_jitter, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->ul_tti_jitter, end) && - pull32(ppReadPackedMsg, &pNfapiMsg->ul_dci_jitter, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->dl_tti_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->ul_tti_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->ul_dci_latest_delay, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->dl_tti_earliest_arrival, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_earliest_arrival, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->ul_tti_earliest_arrival, end) && - pulls32(ppReadPackedMsg, &pNfapiMsg->ul_dci_earliest_arrival, end) && - unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); -} - - - -// unpack length check - -static int check_unpack_length(nfapi_message_id_e msgId, uint32_t unpackedBufLen) { - int retLen = 0; - - switch (msgId) { - case NFAPI_DL_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_dl_config_request_t)) - retLen = sizeof(nfapi_dl_config_request_t); - - break; - - case NFAPI_UL_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_ul_config_request_t)) - retLen = sizeof(nfapi_ul_config_request_t); - - break; - - case NFAPI_SUBFRAME_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_subframe_indication_t)) - retLen = sizeof(nfapi_subframe_indication_t); - - break; - - case NFAPI_HI_DCI0_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_hi_dci0_request_t)) - retLen = sizeof(nfapi_hi_dci0_request_t); - - break; - - case NFAPI_TX_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_tx_request_t)) - retLen = sizeof(nfapi_tx_request_t); - - break; - - case NFAPI_HARQ_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_harq_indication_t)) - retLen = sizeof(nfapi_harq_indication_t); - - break; - - case NFAPI_CRC_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_crc_indication_t)) - retLen = sizeof(nfapi_crc_indication_t); - - break; - - case NFAPI_RX_ULSCH_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_rx_indication_t)) - retLen = sizeof(nfapi_rx_indication_t); - - break; - - case NFAPI_RACH_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_rach_indication_t)) - retLen = sizeof(nfapi_rach_indication_t); - - break; - - case NFAPI_SRS_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_srs_indication_t)) - retLen = sizeof(nfapi_srs_indication_t); - - break; - - case NFAPI_RX_SR_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_sr_indication_t)) - retLen = sizeof(nfapi_sr_indication_t); - - break; - - case NFAPI_RX_CQI_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_cqi_indication_t)) - retLen = sizeof(nfapi_cqi_indication_t); - - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_lbt_dl_config_request_t)) - retLen = sizeof(nfapi_lbt_dl_config_request_t); - - break; - - case NFAPI_LBT_DL_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_lbt_dl_indication_t)) - retLen = sizeof(nfapi_lbt_dl_indication_t); - - break; - - case NFAPI_NB_HARQ_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_nb_harq_indication_t)) - retLen = sizeof(nfapi_nb_harq_indication_t); - - break; - - case NFAPI_NRACH_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_nrach_indication_t)) - retLen = sizeof(nfapi_nrach_indication_t); - - break; - - case NFAPI_DL_NODE_SYNC: - if (unpackedBufLen >= sizeof(nfapi_dl_node_sync_t)) - retLen = sizeof(nfapi_dl_node_sync_t); - - break; - - case NFAPI_UL_NODE_SYNC: - if (unpackedBufLen >= sizeof(nfapi_ul_node_sync_t)) - retLen = sizeof(nfapi_ul_node_sync_t); - - break; - - case NFAPI_TIMING_INFO: - if (unpackedBufLen >= sizeof(nfapi_timing_info_t)) - retLen = sizeof(nfapi_timing_info_t); - - break; - - case NFAPI_UE_RELEASE_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_ue_release_request_t)) - retLen = sizeof(nfapi_ue_release_request_t); - - break; - - case NFAPI_UE_RELEASE_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_ue_release_response_t)) - retLen = sizeof(nfapi_ue_release_response_t); - - break; - - default: - NFAPI_TRACE(NFAPI_TRACE_ERROR, "Unknown message ID %d\n", msgId); - break; - } - - return retLen; -} - -static int check_nr_unpack_length(nfapi_message_id_e msgId, uint32_t unpackedBufLen) { - int retLen = 0; - - switch (msgId) { - case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_dl_tti_request_t)) - retLen = sizeof(nfapi_nr_dl_tti_request_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_ul_tti_request_t)) - retLen = sizeof(nfapi_nr_ul_tti_request_t); - - break; - - case NFAPI_SUBFRAME_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_subframe_indication_t)) - retLen = sizeof(nfapi_subframe_indication_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_ul_dci_request_t)) - retLen = sizeof(nfapi_nr_ul_dci_request_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_nr_tx_data_request_t)) - retLen = sizeof(nfapi_nr_tx_data_request_t); - - break; - - case NFAPI_HARQ_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_harq_indication_t)) - retLen = sizeof(nfapi_harq_indication_t); - - break; - - case NFAPI_CRC_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_crc_indication_t)) - retLen = sizeof(nfapi_crc_indication_t); - - break; - - case NFAPI_RX_ULSCH_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_rx_indication_t)) - retLen = sizeof(nfapi_rx_indication_t); - - break; - - case NFAPI_RACH_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_rach_indication_t)) - retLen = sizeof(nfapi_rach_indication_t); - - break; - - case NFAPI_SRS_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_srs_indication_t)) - retLen = sizeof(nfapi_srs_indication_t); - - break; - - case NFAPI_RX_SR_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_sr_indication_t)) - retLen = sizeof(nfapi_sr_indication_t); - - break; - - case NFAPI_RX_CQI_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_cqi_indication_t)) - retLen = sizeof(nfapi_cqi_indication_t); - - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_lbt_dl_config_request_t)) - retLen = sizeof(nfapi_lbt_dl_config_request_t); - - break; - - case NFAPI_LBT_DL_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_lbt_dl_indication_t)) - retLen = sizeof(nfapi_lbt_dl_indication_t); - - break; - - case NFAPI_NB_HARQ_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_nb_harq_indication_t)) - retLen = sizeof(nfapi_nb_harq_indication_t); - - break; - - case NFAPI_NRACH_INDICATION: - if (unpackedBufLen >= sizeof(nfapi_nrach_indication_t)) - retLen = sizeof(nfapi_nrach_indication_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC: - if (unpackedBufLen >= sizeof(nfapi_nr_dl_node_sync_t)) - retLen = sizeof(nfapi_nr_dl_node_sync_t); - - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC: - if (unpackedBufLen >= sizeof(nfapi_nr_ul_node_sync_t)) - retLen = sizeof(nfapi_nr_ul_node_sync_t); - - break; - - case NFAPI_TIMING_INFO: - if (unpackedBufLen >= sizeof(nfapi_timing_info_t)) - retLen = sizeof(nfapi_timing_info_t); - - break; - - case NFAPI_UE_RELEASE_REQUEST: - if (unpackedBufLen >= sizeof(nfapi_ue_release_request_t)) - retLen = sizeof(nfapi_ue_release_request_t); - - break; - - case NFAPI_UE_RELEASE_RESPONSE: - if (unpackedBufLen >= sizeof(nfapi_ue_release_response_t)) - retLen = sizeof(nfapi_ue_release_response_t); - - break; - - default: - NFAPI_TRACE(NFAPI_TRACE_ERROR, "Unknown message ID %d\n", msgId); - break; - } - - return retLen; -} - - - -// Main unpack functions - public - -int nfapi_p7_message_header_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p7_codec_config_t *config) { - nfapi_p7_message_header_t *pMessageHeader = pUnpackedBuf; - uint8_t *pReadPackedMessage = pMessageBuf; - uint8_t *end = pMessageBuf + messageBufLen; - - if (pMessageBuf == NULL || pUnpackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 header unpack supplied pointers are null\n"); - return -1; - } - - if (messageBufLen < NFAPI_P7_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p7_message_header_t)) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 header unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); - return -1; - } - - // process the header - if(!(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && - pull16(&pReadPackedMessage, &pMessageHeader->m_segment_sequence, end) && - pull32(&pReadPackedMessage, &pMessageHeader->checksum, end) && - pull32(&pReadPackedMessage, &pMessageHeader->transmit_timestamp, end))) - return -1; - - return 0; -} - -int nfapi_p7_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p7_codec_config_t *config) { - int result = 0; - nfapi_p7_message_header_t *pMessageHeader = (nfapi_p7_message_header_t *)pUnpackedBuf; - uint8_t *pReadPackedMessage = pMessageBuf; - uint8_t *end = pMessageBuf + messageBufLen; - - if (pMessageBuf == NULL || pUnpackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied pointers are null\n"); - return -1; - } - - if (messageBufLen < NFAPI_P7_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p7_message_header_t)) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); - return -1; - } - - /* - uint8_t *ptr = pMessageBuf; - printf("\n Read P7 message unpack: "); - while(ptr < end){ - printf(" %d ", *ptr); - ptr++; - } - printf("\n"); - */ - // clean the supplied buffer for - tag value blanking - (void)memset(pUnpackedBuf, 0, unpackedBufLen); - - // process the header - if(!(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && - pull16(&pReadPackedMessage, &pMessageHeader->m_segment_sequence, end) && - pull32(&pReadPackedMessage, &pMessageHeader->checksum, end) && - pull32(&pReadPackedMessage, &pMessageHeader->transmit_timestamp, end))) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack header failed\n"); - return -1; - } - - if((uint8_t *)(pMessageBuf + pMessageHeader->message_length) > end) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack message length is greater than the message buffer \n"); - return -1; - } - - /* - if(check_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) - { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack unpack buffer is not large enough \n"); - return -1; - } - */ - - // look for the specific message - switch (pMessageHeader->message_id) { - case NFAPI_DL_CONFIG_REQUEST: - if (check_unpack_length(NFAPI_DL_CONFIG_REQUEST, unpackedBufLen)) - result = unpack_dl_config_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_UL_CONFIG_REQUEST: - if (check_unpack_length(NFAPI_UL_CONFIG_REQUEST, unpackedBufLen)) - result = unpack_ul_config_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_TX_REQUEST: - if (check_unpack_length(NFAPI_TX_REQUEST, unpackedBufLen)) - result = unpack_tx_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_HI_DCI0_REQUEST: - if (check_unpack_length(NFAPI_HI_DCI0_REQUEST, unpackedBufLen)) - result = unpack_hi_dci0_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_UE_RELEASE_REQUEST: - if (check_unpack_length(NFAPI_UE_RELEASE_REQUEST, unpackedBufLen)) - result = unpack_ue_release_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_HARQ_INDICATION: - if (check_unpack_length(NFAPI_HARQ_INDICATION, unpackedBufLen)) - result = unpack_harq_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_CRC_INDICATION: - if (check_unpack_length(NFAPI_CRC_INDICATION, unpackedBufLen)) - result = unpack_crc_indication(&pReadPackedMessage,end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RX_ULSCH_INDICATION: - if (check_unpack_length(NFAPI_RX_ULSCH_INDICATION, unpackedBufLen)) - result = unpack_rx_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RACH_INDICATION: - if (check_unpack_length(NFAPI_RACH_INDICATION, unpackedBufLen)) - result = unpack_rach_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_SRS_INDICATION: - if (check_unpack_length(NFAPI_SRS_INDICATION, unpackedBufLen)) - result = unpack_srs_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RX_SR_INDICATION: - if (check_unpack_length(NFAPI_RX_SR_INDICATION, unpackedBufLen)) - result = unpack_sr_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RX_CQI_INDICATION: - if (check_unpack_length(NFAPI_RX_CQI_INDICATION, unpackedBufLen)) - result = unpack_cqi_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST: - if (check_unpack_length(NFAPI_LBT_DL_CONFIG_REQUEST, unpackedBufLen)) - result = unpack_lbt_dl_config_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_LBT_DL_INDICATION: - if (check_unpack_length(NFAPI_LBT_DL_INDICATION, unpackedBufLen)) - result = unpack_lbt_dl_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NB_HARQ_INDICATION: - if (check_unpack_length(NFAPI_NB_HARQ_INDICATION, unpackedBufLen)) - result = unpack_nb_harq_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NRACH_INDICATION: - if (check_unpack_length(NFAPI_NRACH_INDICATION, unpackedBufLen)) - result = unpack_nrach_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_DL_NODE_SYNC: - if (check_unpack_length(NFAPI_DL_NODE_SYNC, unpackedBufLen)) - result = unpack_dl_node_sync(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_UL_NODE_SYNC: - if (check_unpack_length(NFAPI_UL_NODE_SYNC, unpackedBufLen)) - result = unpack_ul_node_sync(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_TIMING_INFO: - if (check_unpack_length(NFAPI_TIMING_INFO, unpackedBufLen)) - result = unpack_timing_info(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_UE_RELEASE_RESPONSE: - if (check_unpack_length(NFAPI_UE_RELEASE_RESPONSE, unpackedBufLen)) - result = unpack_ue_release_resp(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - default: - if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->unpack_p7_vendor_extension) { - result = (config->unpack_p7_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); - } - - break; - } - - if(result == 0) - return -1; - else - return 0; -} - -int nfapi_nr_p7_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p7_codec_config_t *config) { - int result = 0; - nfapi_p7_message_header_t *pMessageHeader = (nfapi_p7_message_header_t *)pUnpackedBuf; - uint8_t *pReadPackedMessage = pMessageBuf; - uint8_t *end = pMessageBuf + messageBufLen; - - if (pMessageBuf == NULL || pUnpackedBuf == NULL) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied pointers are null\n"); - return -1; - } - - if (messageBufLen < NFAPI_P7_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p7_message_header_t)) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); - return -1; - } - - /* - uint8_t *ptr = pMessageBuf; - printf("\n Read P7 message unpack: "); - while(ptr < end){ - printf(" %d ", *ptr); - ptr++; - } - printf("\n"); - */ - // clean the supplied buffer for - tag value blanking - (void)memset(pUnpackedBuf, 0, unpackedBufLen); - - // process the header - if(!(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && - pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && - pull16(&pReadPackedMessage, &pMessageHeader->m_segment_sequence, end) && - pull32(&pReadPackedMessage, &pMessageHeader->checksum, end) && - pull32(&pReadPackedMessage, &pMessageHeader->transmit_timestamp, end))) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack header failed\n"); - return -1; - } - - if((uint8_t *)(pMessageBuf + pMessageHeader->message_length) > end) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack message length is greater than the message buffer \n"); - return -1; - } - - /* - if(check_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) - { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack unpack buffer is not large enough \n"); - return -1; - } - */ - - // look for the specific message - switch (pMessageHeader->message_id) { - case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST: - if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST, unpackedBufLen)) - result = unpack_dl_tti_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST: - if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST, unpackedBufLen)) - result = unpack_ul_tti_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST: - if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST, unpackedBufLen)) - result = unpack_tx_data_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST: - if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST, unpackedBufLen)) - result = unpack_ul_dci_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_UE_RELEASE_REQUEST: - if (check_nr_unpack_length(NFAPI_UE_RELEASE_REQUEST, unpackedBufLen)) - result = unpack_ue_release_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_HARQ_INDICATION: - if (check_nr_unpack_length(NFAPI_HARQ_INDICATION, unpackedBufLen)) - result = unpack_harq_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_CRC_INDICATION: - if (check_nr_unpack_length(NFAPI_CRC_INDICATION, unpackedBufLen)) - result = unpack_crc_indication(&pReadPackedMessage,end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RX_ULSCH_INDICATION: - if (check_nr_unpack_length(NFAPI_RX_ULSCH_INDICATION, unpackedBufLen)) - result = unpack_rx_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RACH_INDICATION: - if (check_nr_unpack_length(NFAPI_RACH_INDICATION, unpackedBufLen)) - result = unpack_rach_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_SRS_INDICATION: - if (check_nr_unpack_length(NFAPI_SRS_INDICATION, unpackedBufLen)) - result = unpack_srs_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RX_SR_INDICATION: - if (check_nr_unpack_length(NFAPI_RX_SR_INDICATION, unpackedBufLen)) - result = unpack_sr_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_RX_CQI_INDICATION: - if (check_nr_unpack_length(NFAPI_RX_CQI_INDICATION, unpackedBufLen)) - result = unpack_cqi_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_LBT_DL_CONFIG_REQUEST: - if (check_nr_unpack_length(NFAPI_LBT_DL_CONFIG_REQUEST, unpackedBufLen)) - result = unpack_lbt_dl_config_request(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_LBT_DL_INDICATION: - if (check_nr_unpack_length(NFAPI_LBT_DL_INDICATION, unpackedBufLen)) - result = unpack_lbt_dl_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NB_HARQ_INDICATION: - if (check_nr_unpack_length(NFAPI_NB_HARQ_INDICATION, unpackedBufLen)) - result = unpack_nb_harq_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NRACH_INDICATION: - if (check_nr_unpack_length(NFAPI_NRACH_INDICATION, unpackedBufLen)) - result = unpack_nrach_indication(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC: - if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC, unpackedBufLen)) - result = unpack_nr_dl_node_sync(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC: - if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC, unpackedBufLen)) - result = unpack_nr_ul_node_sync(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_TIMING_INFO: - if (check_nr_unpack_length(NFAPI_TIMING_INFO, unpackedBufLen)) - result = unpack_nr_timing_info(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - case NFAPI_UE_RELEASE_RESPONSE: - if (check_nr_unpack_length(NFAPI_UE_RELEASE_RESPONSE, unpackedBufLen)) - result = unpack_ue_release_resp(&pReadPackedMessage, end, pMessageHeader, config); - else - return -1; - - break; - - default: - if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && - pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { - if(config && config->unpack_p7_vendor_extension) { - result = (config->unpack_p7_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); - } - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); - } - - break; - } - - if(result == 0) - return -1; - else - return 0; -} - - +/* + * Copyright 2017 Cisco Systems, Inc. + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "nfapi_nr_interface_scf.h" + +extern int nfapi_unpack_p7_vendor_extension(nfapi_p7_message_header_t *header, uint8_t **ppReadPackedMsg, void *user_data); +extern int nfapi_pack_p7_vendor_extension(nfapi_p7_message_header_t *header, uint8_t **ppWritePackedMsg, void *user_data); + +uint32_t nfapi_calculate_checksum(uint8_t *buffer, uint16_t len) { + uint32_t chksum = 0; + // calcaulte upto the checksum + chksum = crc32(chksum, buffer, 8); + // skip the checksum + uint8_t zeros[4] = {0, 0, 0, 0}; + chksum = crc32(chksum, zeros, 4); + // continu with the rest of the mesage + chksum = crc32(chksum, &buffer[NFAPI_P7_HEADER_LENGTH], len - NFAPI_P7_HEADER_LENGTH); + // return the inverse + return ~(chksum); +} + +int nfapi_p7_update_checksum(uint8_t *buffer, uint32_t len) { + uint32_t checksum = nfapi_calculate_checksum(buffer, len); + uint8_t *p_write = &buffer[8]; + return (push32(checksum, &p_write, buffer + len) > 0 ? 0 : -1); +} + +int nfapi_p7_update_transmit_timestamp(uint8_t *buffer, uint32_t timestamp) { + uint8_t *p_write = &buffer[12]; + return (push32(timestamp, &p_write, buffer + 16) > 0 ? 0 : -1); +} + +uint32_t nfapi_p7_calculate_checksum(uint8_t *buffer, uint32_t len) { + return nfapi_calculate_checksum(buffer, len); +} + +void *nfapi_p7_allocate(size_t size, nfapi_p7_codec_config_t *config) { + if(size == 0) + return 0; + + void *buffer_p = NULL; + + if(config && config->allocate) { + buffer_p = (config->allocate)(size); + + if(buffer_p != NULL) { + memset(buffer_p,0,size); + } + + return buffer_p; + } else { + buffer_p = calloc(1, size); + return buffer_p; + } +} + +void nfapi_p7_deallocate(void *ptr, nfapi_p7_codec_config_t *config) { + if(ptr == NULL) + return; + + if(config && config->deallocate) { + return (config->deallocate)(ptr); + } else { + return free(ptr); + } +} +// Pack routines + + +static uint8_t pack_dl_config_dci_dl_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel8_t *value = (nfapi_dl_config_dci_dl_pdu_rel8_t *)tlv; + //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() dci_format:%u\n", __FUNCTION__, value->dci_format); + return ( push8(value->dci_format, ppWritePackedMsg, end) && + push8(value->cce_idx, ppWritePackedMsg, end) && + push8(value->aggregation_level, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->resource_allocation_type, ppWritePackedMsg, end) && + push8(value->virtual_resource_block_assignment_flag, ppWritePackedMsg, end) && + push32(value->resource_block_coding, ppWritePackedMsg, end) && + push8(value->mcs_1, ppWritePackedMsg, end) && + push8(value->redundancy_version_1, ppWritePackedMsg, end) && + push8(value->new_data_indicator_1, ppWritePackedMsg, end) && + push8(value->transport_block_to_codeword_swap_flag, ppWritePackedMsg, end) && + push8(value->mcs_2, ppWritePackedMsg, end) && + push8(value->redundancy_version_2, ppWritePackedMsg, end) && + push8(value->new_data_indicator_2, ppWritePackedMsg, end) && + push8(value->harq_process, ppWritePackedMsg, end) && + push8(value->tpmi, ppWritePackedMsg, end) && + push8(value->pmi, ppWritePackedMsg, end) && + push8(value->precoding_information, ppWritePackedMsg, end) && + push8(value->tpc, ppWritePackedMsg, end) && + push8(value->downlink_assignment_index, ppWritePackedMsg, end) && + push8(value->ngap, ppWritePackedMsg, end) && + push8(value->transport_block_size_index, ppWritePackedMsg, end) && + push8(value->downlink_power_offset, ppWritePackedMsg, end) && + push8(value->allocate_prach_flag, ppWritePackedMsg, end) && + push8(value->preamble_index, ppWritePackedMsg, end) && + push8(value->prach_mask_index, ppWritePackedMsg, end) && + push8(value->rnti_type, ppWritePackedMsg, end) && + push16(value->transmission_power, ppWritePackedMsg, end)); +} + +static uint8_t pack_dl_config_dci_dl_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel9_t *value = (nfapi_dl_config_dci_dl_pdu_rel9_t *)tlv; + return( push8(value->mcch_flag, ppWritePackedMsg, end) && + push8(value->mcch_change_notification, ppWritePackedMsg, end) && + push8(value->scrambling_identity, ppWritePackedMsg, end)); +} + +static uint8_t pack_dl_config_dci_dl_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel10_t *value = (nfapi_dl_config_dci_dl_pdu_rel10_t *)tlv; + return ( push8(value->cross_carrier_scheduling_flag, ppWritePackedMsg, end) && + push8(value->carrier_indicator, ppWritePackedMsg, end) && + push8(value->srs_flag, ppWritePackedMsg, end) && + push8(value->srs_request, ppWritePackedMsg, end) && + push8(value->antenna_ports_scrambling_and_layers, ppWritePackedMsg, end) && + push8(value->total_dci_length_including_padding, ppWritePackedMsg, end) && + push8(value->n_dl_rb, ppWritePackedMsg, end)); +} + +static uint8_t pack_dl_config_dci_dl_pdu_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel11_t *value = (nfapi_dl_config_dci_dl_pdu_rel11_t *)tlv; + return ( push8(value->harq_ack_resource_offset, ppWritePackedMsg, end) && + push8(value->pdsch_re_mapping_quasi_co_location_indicator, ppWritePackedMsg, end)); +} + +static uint8_t pack_dl_config_dci_dl_pdu_rel12_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel12_t *value = (nfapi_dl_config_dci_dl_pdu_rel12_t *)tlv; + return ( push8(value->primary_cell_type, ppWritePackedMsg, end) && + push8(value->ul_dl_configuration_flag, ppWritePackedMsg, end) && + push8(value->number_ul_dl_configurations, ppWritePackedMsg, end) && + pusharray8(value->ul_dl_configuration_indication, NFAPI_MAX_UL_DL_CONFIGURATIONS, value->number_ul_dl_configurations, ppWritePackedMsg, end)); +} + +static uint8_t pack_tpm_value(nfapi_dl_config_dci_dl_tpm_t *value, uint8_t **ppWritePackedMsg, uint8_t *end) { + if (!( push8(value->num_prb_per_subband, ppWritePackedMsg, end) && + push8(value->number_of_subbands, ppWritePackedMsg, end) && + push8(value->num_antennas, ppWritePackedMsg, end))) + return 0; + + uint8_t idx = 0; + + for(idx = 0; idx < value->number_of_subbands; ++idx) { + nfapi_dl_config_dci_dl_tpm_subband_info_t *subband_info = &(value->subband_info[idx]); + + if(!(push8(subband_info->subband_index, ppWritePackedMsg, end) && + push8(subband_info->scheduled_ues, ppWritePackedMsg, end))) + return 0; + + uint8_t antenna_idx = 0; + uint8_t scheduled_ue_idx = 0; + + for(antenna_idx = 0; antenna_idx < value->num_antennas; ++antenna_idx) { + for(scheduled_ue_idx = 0; scheduled_ue_idx < subband_info->scheduled_ues; ++scheduled_ue_idx) { + if(!push16(subband_info->precoding_value[antenna_idx][scheduled_ue_idx], ppWritePackedMsg, end)) + return 0; + } + } + } + + return 1; +} + + +static uint8_t pack_dl_tti_csi_rs_pdu_rel15_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *value = (nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *)tlv; + return( + push16(value->bwp_size, ppWritePackedMsg, end) && + push16(value->bwp_start, ppWritePackedMsg, end) && + push8(value->subcarrier_spacing, ppWritePackedMsg, end) && + push8(value->cyclic_prefix, ppWritePackedMsg, end) && + push16(value->start_rb, ppWritePackedMsg, end) && + push16(value->nr_of_rbs, ppWritePackedMsg, end) && + push8(value->csi_type, ppWritePackedMsg, end) && + push8(value->row, ppWritePackedMsg, end) && + push16(value->freq_domain, ppWritePackedMsg, end) && + push8(value->symb_l0, ppWritePackedMsg, end) && + push8(value->symb_l1, ppWritePackedMsg, end) && + push8(value->cdm_type, ppWritePackedMsg, end) && + push8(value->freq_density, ppWritePackedMsg, end) && + push16(value->scramb_id, ppWritePackedMsg, end) && + push8(value->power_control_offset, ppWritePackedMsg, end) && + push8(value->power_control_offset_ss, ppWritePackedMsg, end) + ); +} + + +static uint8_t pack_dl_tti_pdcch_pdu_rel15_value(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + + nfapi_nr_dl_tti_pdcch_pdu_rel15_t* value = (nfapi_nr_dl_tti_pdcch_pdu_rel15_t*)tlv; + + for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) + { + if(!(push16(value->dci_pdu[i].RNTI, ppWritePackedMsg, end) && + push16(value->dci_pdu[i].ScramblingId, ppWritePackedMsg, end) && + push16(value->dci_pdu[i].ScramblingRNTI, ppWritePackedMsg, end) && + push8(value->dci_pdu[i].CceIndex, ppWritePackedMsg, end) && + push8(value->dci_pdu[i].AggregationLevel, ppWritePackedMsg, end) && + push8(value->dci_pdu[i].beta_PDCCH_1_0, ppWritePackedMsg, end) && + push8(value->dci_pdu[i].powerControlOffsetSS, ppWritePackedMsg, end) && + push16(value->dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end) && + pusharray8(value->dci_pdu[i].Payload, value->dci_pdu[i].PayloadSizeBits, value->dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end))) + return 0; + + } + // TODO: resolve the packaging of array (currently sending a single element) + return( + push16(value->BWPSize, ppWritePackedMsg, end) && + push16(value->BWPStart, ppWritePackedMsg, end) && + push8(value->SubcarrierSpacing, ppWritePackedMsg, end) && + push8(value->CyclicPrefix, ppWritePackedMsg, end) && + push8(value->StartSymbolIndex, ppWritePackedMsg, end) && + push8(value->DurationSymbols, ppWritePackedMsg, end) && + pusharray8(value->FreqDomainResource, 6, 6, ppWritePackedMsg, end) && + push8(value->CceRegMappingType, ppWritePackedMsg, end) && + push8(value->RegBundleSize, ppWritePackedMsg, end) && + push8(value->InterleaverSize, ppWritePackedMsg, end) && + push8(value->CoreSetType, ppWritePackedMsg, end) && + push16(value->ShiftIndex, ppWritePackedMsg, end) && + push8(value->precoderGranularity, ppWritePackedMsg, end) && + push16(value->numDlDci, ppWritePackedMsg, end)); +} + + +static uint8_t pack_dl_tti_pdsch_pdu_rel15_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *value = (nfapi_nr_dl_tti_pdsch_pdu_rel15_t *)tlv; + // TODO: resolve the packaging of array (currently sending a single element) + return( + push16(value->pduBitmap, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push16(value->pduIndex, ppWritePackedMsg, end) && + push16(value->BWPSize, ppWritePackedMsg, end) && + push16(value->BWPStart, ppWritePackedMsg, end) && + push8(value->SubcarrierSpacing, ppWritePackedMsg, end) && + push8(value->CyclicPrefix, ppWritePackedMsg, end) && + push8(value->NrOfCodewords, ppWritePackedMsg, end) && + pusharray16(value->targetCodeRate, 2, 1, ppWritePackedMsg, end) && + pusharray8(value->qamModOrder, 2, 1, ppWritePackedMsg, end) && + pusharray8(value->mcsIndex, 2, 1, ppWritePackedMsg, end) && + pusharray8(value->mcsTable, 2, 1, ppWritePackedMsg, end) && + pusharray8(value->rvIndex, 2, 1, ppWritePackedMsg, end) && + pusharray32(value->TBSize, 2, 1, ppWritePackedMsg, end) && + push16(value->dataScramblingId, ppWritePackedMsg, end) && + push8(value->nrOfLayers, ppWritePackedMsg, end) && + push8(value->transmissionScheme, ppWritePackedMsg, end) && + push8(value->refPoint, ppWritePackedMsg, end) && + push16(value->dlDmrsSymbPos, ppWritePackedMsg, end) && + push8(value->dmrsConfigType, ppWritePackedMsg, end) && + push16(value->dlDmrsScramblingId, ppWritePackedMsg, end) && + push8(value->SCID, ppWritePackedMsg, end) && + push8(value->numDmrsCdmGrpsNoData, ppWritePackedMsg, end) && + push16(value->dmrsPorts, ppWritePackedMsg, end) && + push8(value->resourceAlloc, ppWritePackedMsg, end) && + push16(value->rbStart, ppWritePackedMsg, end) && + push16(value->rbSize, ppWritePackedMsg, end) && + push8(value->VRBtoPRBMapping, ppWritePackedMsg, end) && + push8(value->StartSymbolIndex, ppWritePackedMsg, end) && + push8(value->NrOfSymbols, ppWritePackedMsg, end) && + push8(value->PTRSPortIndex, ppWritePackedMsg, end) && + push8(value->PTRSTimeDensity, ppWritePackedMsg, end) && + push8(value->PTRSFreqDensity, ppWritePackedMsg, end) && + push8(value->PTRSReOffset, ppWritePackedMsg, end) + ); +} + + +static uint8_t pack_dl_tti_ssb_pdu_rel15_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_dl_tti_ssb_pdu_rel15_t *value = (nfapi_nr_dl_tti_ssb_pdu_rel15_t *)tlv; + return( + push16(value->PhysCellId, ppWritePackedMsg, end) && + push8(value->BetaPss, ppWritePackedMsg, end) && + push8(value->SsbBlockIndex, ppWritePackedMsg, end) && + push8(value->SsbSubcarrierOffset, ppWritePackedMsg, end) && + push16(value->ssbOffsetPointA, ppWritePackedMsg, end) && + push8(value->bchPayloadFlag, ppWritePackedMsg, end) && + push32(value->bchPayload, ppWritePackedMsg, end) + // TODO: pack precoding_and_beamforming too + ); +} + + +static uint8_t pack_dl_config_dci_dl_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel13_t *value = (nfapi_dl_config_dci_dl_pdu_rel13_t *)tlv; + return( push8(value->laa_end_partial_sf_flag, ppWritePackedMsg, end) && + push8(value->laa_end_partial_sf_configuration, ppWritePackedMsg, end) && + push8(value->initial_lbt_sf, ppWritePackedMsg, end) && + push8(value->codebook_size_determination, ppWritePackedMsg, end) && + push8(value->drms_table_flag, ppWritePackedMsg, end) && + push8(value->tpm_struct_flag, ppWritePackedMsg, end) && + (value->tpm_struct_flag == 1 ? pack_tpm_value(&(value->tpm), ppWritePackedMsg, end) : 1)); +} + +static uint8_t pack_dl_config_bch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_bch_pdu_rel8_t *value = (nfapi_dl_config_bch_pdu_rel8_t *)tlv; + //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s()\n", __FUNCTION__); + return( push16(value->length, ppWritePackedMsg, end) && + push16(value->pdu_index, ppWritePackedMsg, end) && + push16(value->transmission_power, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_mch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_mch_pdu_rel8_t *value = (nfapi_dl_config_mch_pdu_rel8_t *)tlv; + return ( push16(value->length, ppWritePackedMsg, end) && + push16(value->pdu_index, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->resource_allocation_type, ppWritePackedMsg, end) && + push32(value->resource_block_coding, ppWritePackedMsg, end) && + push8(value->modulation, ppWritePackedMsg, end) && + push16(value->transmission_power, ppWritePackedMsg, end) && + push16(value->mbsfn_area_id, ppWritePackedMsg, end)); +} + +static uint8_t pack_bf_vector_info(void *elem, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_bf_vector_t *bf = (nfapi_bf_vector_t *)elem; + return ( push8(bf->subband_index, ppWritePackedMsg, end) && + push8(bf->num_antennas, ppWritePackedMsg, end) && + pusharray16(bf->bf_value, NFAPI_MAX_NUM_ANTENNAS, bf->num_antennas, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_dlsch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel8_t *value = (nfapi_dl_config_dlsch_pdu_rel8_t *)tlv; + return ( push16(value->length, ppWritePackedMsg, end) && + push16(value->pdu_index, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->resource_allocation_type, ppWritePackedMsg, end) && + push8(value->virtual_resource_block_assignment_flag, ppWritePackedMsg, end) && + push32(value->resource_block_coding, ppWritePackedMsg, end) && + push8(value->modulation, ppWritePackedMsg, end) && + push8(value->redundancy_version, ppWritePackedMsg, end) && + push8(value->transport_blocks, ppWritePackedMsg, end) && + push8(value->transport_block_to_codeword_swap_flag, ppWritePackedMsg, end) && + push8(value->transmission_scheme, ppWritePackedMsg, end) && + push8(value->number_of_layers, ppWritePackedMsg, end) && + push8(value->number_of_subbands, ppWritePackedMsg, end) && + pusharray8(value->codebook_index, NFAPI_MAX_NUM_SUBBANDS, value->number_of_subbands, ppWritePackedMsg, end) && + push8(value->ue_category_capacity, ppWritePackedMsg, end) && + push8(value->pa, ppWritePackedMsg, end) && + push8(value->delta_power_offset_index, ppWritePackedMsg, end) && + push8(value->ngap, ppWritePackedMsg, end) && + push8(value->nprb, ppWritePackedMsg, end) && + push8(value->transmission_mode, ppWritePackedMsg, end) && + push8(value->num_bf_prb_per_subband, ppWritePackedMsg, end) && + push8(value->num_bf_vector, ppWritePackedMsg, end) && + packarray(value->bf_vector, sizeof(nfapi_bf_vector_t), NFAPI_MAX_BF_VECTORS, value->num_bf_vector, ppWritePackedMsg, end, &pack_bf_vector_info)); +} +static uint8_t pack_dl_config_dlsch_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel9_t *value = (nfapi_dl_config_dlsch_pdu_rel9_t *)tlv; + return ( push8(value->nscid, ppWritePackedMsg, end) ); +} +static uint8_t pack_dl_config_dlsch_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel10_t *value = (nfapi_dl_config_dlsch_pdu_rel10_t *)tlv; + return ( push8(value->csi_rs_flag, ppWritePackedMsg, end) && + push8(value->csi_rs_resource_config_r10, ppWritePackedMsg, end) && + push16(value->csi_rs_zero_tx_power_resource_config_bitmap_r10, ppWritePackedMsg, end) && + push8(value->csi_rs_number_nzp_configuration, ppWritePackedMsg, end) && + pusharray8(value->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, value->csi_rs_number_nzp_configuration, ppWritePackedMsg, end) && + push8(value->pdsch_start, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_dlsch_pdu_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel11_t *value = (nfapi_dl_config_dlsch_pdu_rel11_t *)tlv; + return( push8(value->drms_config_flag, ppWritePackedMsg, end) && + push16(value->drms_scrambling, ppWritePackedMsg, end) && + push8(value->csi_config_flag, ppWritePackedMsg, end) && + push16(value->csi_scrambling, ppWritePackedMsg, end) && + push8(value->pdsch_re_mapping_flag, ppWritePackedMsg, end) && + push8(value->pdsch_re_mapping_atenna_ports, ppWritePackedMsg, end) && + push8(value->pdsch_re_mapping_freq_shift, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_dlsch_pdu_rel12_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel12_t *value = (nfapi_dl_config_dlsch_pdu_rel12_t *)tlv; + return( push8(value->altcqi_table_r12, ppWritePackedMsg, end) && + push8(value->maxlayers, ppWritePackedMsg, end) && + push8(value->n_dl_harq, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_dlsch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel13_t *value = (nfapi_dl_config_dlsch_pdu_rel13_t *)tlv; + return( push8(value->dwpts_symbols, ppWritePackedMsg, end) && + push8(value->initial_lbt_sf, ppWritePackedMsg, end) && + push8(value->ue_type, ppWritePackedMsg, end) && + push8(value->pdsch_payload_type, ppWritePackedMsg, end) && + push16(value->initial_transmission_sf_io, ppWritePackedMsg, end) && + push8(value->drms_table_flag, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_pch_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_pch_pdu_rel8_t *value = (nfapi_dl_config_pch_pdu_rel8_t *)tlv; + return( push16(value->length, ppWritePackedMsg, end) && + push16(value->pdu_index, ppWritePackedMsg, end) && + push16(value->p_rnti, ppWritePackedMsg, end) && + push8(value->resource_allocation_type, ppWritePackedMsg, end) && + push8(value->virtual_resource_block_assignment_flag, ppWritePackedMsg, end) && + push32(value->resource_block_coding, ppWritePackedMsg, end) && + push8(value->mcs, ppWritePackedMsg, end) && + push8(value->redundancy_version, ppWritePackedMsg, end) && + push8(value->number_of_transport_blocks, ppWritePackedMsg, end) && + push8(value->transport_block_to_codeword_swap_flag, ppWritePackedMsg, end) && + push8(value->transmission_scheme, ppWritePackedMsg, end) && + push8(value->number_of_layers, ppWritePackedMsg, end) && + push8(value->codebook_index, ppWritePackedMsg, end) && + push8(value->ue_category_capacity, ppWritePackedMsg, end) && + push8(value->pa, ppWritePackedMsg, end) && + push16(value->transmission_power, ppWritePackedMsg, end) && + push8(value->nprb, ppWritePackedMsg, end) && + push8(value->ngap, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_pch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_pch_pdu_rel13_t *value = (nfapi_dl_config_pch_pdu_rel13_t *)tlv; + return ( push8(value->ue_mode, ppWritePackedMsg, end) && + push16(value->initial_transmission_sf_io, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_prs_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_prs_pdu_rel9_t *value = (nfapi_dl_config_prs_pdu_rel9_t *)tlv; + return( push16(value->transmission_power, ppWritePackedMsg, end) && + push8(value->prs_bandwidth, ppWritePackedMsg, end) && + push8(value->prs_cyclic_prefix_type, ppWritePackedMsg, end) && + push8(value->prs_muting, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_csi_rs_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_csi_rs_pdu_rel10_t *value = (nfapi_dl_config_csi_rs_pdu_rel10_t *)tlv; + return( push8(value->csi_rs_antenna_port_count_r10, ppWritePackedMsg, end) && + push8(value->csi_rs_resource_config_r10, ppWritePackedMsg, end) && + push16(value->transmission_power, ppWritePackedMsg, end) && + push16(value->csi_rs_zero_tx_power_resource_config_bitmap_r10, ppWritePackedMsg, end) && + push8(value->csi_rs_number_of_nzp_configuration, ppWritePackedMsg, end) && + pusharray8(value->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, value->csi_rs_number_of_nzp_configuration, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_csi_rs_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_csi_rs_pdu_rel13_t *value = (nfapi_dl_config_csi_rs_pdu_rel13_t *)tlv; + + if(!(push8(value->csi_rs_class, ppWritePackedMsg, end) && + push8(value->cdm_type, ppWritePackedMsg, end) && + push8(value->num_bf_vector, ppWritePackedMsg, end))) { + return 0; + } + + uint16_t i; + + for(i = 0; i < value->num_bf_vector; ++i) { + if(!(push8(value->bf_vector[i].csi_rs_resource_index, ppWritePackedMsg, end) && + pusharray16(value->bf_vector[i].bf_value, NFAPI_MAX_ANTENNA_PORT_COUNT, NFAPI_MAX_ANTENNA_PORT_COUNT, ppWritePackedMsg, end))) + return 0; + } + + return 1; +} +static uint8_t pack_bf_vector(nfapi_bf_vector_t *vector, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( push8(vector->subband_index, ppWritePackedMsg, end) && + push8(vector->num_antennas, ppWritePackedMsg, end) && + pusharray16(vector->bf_value, NFAPI_MAX_NUM_ANTENNAS, vector->num_antennas, ppWritePackedMsg, end)); +} + +static uint8_t pack_dl_config_epdcch_parameters_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_epdcch_parameters_rel11_t *value = (nfapi_dl_config_epdcch_parameters_rel11_t *)tlv; + return ( push8(value->epdcch_resource_assignment_flag, ppWritePackedMsg, end) && + push16(value->epdcch_id, ppWritePackedMsg, end) && + push8(value->epdcch_start_symbol, ppWritePackedMsg, end) && + push8(value->epdcch_num_prb, ppWritePackedMsg, end) && + pusharray8(value->epdcch_prb_index, NFAPI_MAX_EPDCCH_PRB, value->epdcch_num_prb, ppWritePackedMsg, end) && + pack_bf_vector(&value->bf_vector, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_epdcch_parameters_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_epdcch_parameters_rel13_t *value = (nfapi_dl_config_epdcch_parameters_rel13_t *)tlv; + return (push8(value->dwpts_symbols, ppWritePackedMsg, end) && + push8(value->initial_lbt_sf, ppWritePackedMsg, end)); +} +static uint8_t pack_dl_config_mpdcch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_mpdcch_pdu_rel13_t *value = (nfapi_dl_config_mpdcch_pdu_rel13_t *)tlv; + return ( push8(value->mpdcch_narrow_band, ppWritePackedMsg, end) && + push8(value->number_of_prb_pairs, ppWritePackedMsg, end) && + push8(value->resource_block_assignment, ppWritePackedMsg, end) && + push8(value->mpdcch_tansmission_type, ppWritePackedMsg, end) && + push8(value->start_symbol, ppWritePackedMsg, end) && + push8(value->ecce_index, ppWritePackedMsg, end) && + push8(value->aggregation_level, ppWritePackedMsg, end) && + push8(value->rnti_type, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->ce_mode, ppWritePackedMsg, end) && + push16(value->drms_scrambling_init, ppWritePackedMsg, end) && + push16(value->initial_transmission_sf_io, ppWritePackedMsg, end) && + push16(value->transmission_power, ppWritePackedMsg, end) && + push8(value->dci_format, ppWritePackedMsg, end) && + push16(value->resource_block_coding, ppWritePackedMsg, end) && + push8(value->mcs, ppWritePackedMsg, end) && + push8(value->pdsch_reptition_levels, ppWritePackedMsg, end) && + push8(value->redundancy_version, ppWritePackedMsg, end) && + push8(value->new_data_indicator, ppWritePackedMsg, end) && + push8(value->harq_process, ppWritePackedMsg, end) && + push8(value->tpmi_length, ppWritePackedMsg, end) && + push8(value->tpmi, ppWritePackedMsg, end) && + push8(value->pmi_flag, ppWritePackedMsg, end) && + push8(value->pmi, ppWritePackedMsg, end) && + push8(value->harq_resource_offset, ppWritePackedMsg, end) && + push8(value->dci_subframe_repetition_number, ppWritePackedMsg, end) && + push8(value->tpc, ppWritePackedMsg, end) && + push8(value->downlink_assignment_index_length, ppWritePackedMsg, end) && + push8(value->downlink_assignment_index, ppWritePackedMsg, end) && + push8(value->allocate_prach_flag, ppWritePackedMsg, end) && + push8(value->preamble_index, ppWritePackedMsg, end) && + push8(value->prach_mask_index, ppWritePackedMsg, end) && + push8(value->starting_ce_level, ppWritePackedMsg, end) && + push8(value->srs_request, ppWritePackedMsg, end) && + push8(value->antenna_ports_and_scrambling_identity_flag, ppWritePackedMsg, end) && + push8(value->antenna_ports_and_scrambling_identity, ppWritePackedMsg, end) && + push8(value->frequency_hopping_enabled_flag, ppWritePackedMsg, end) && + push8(value->paging_direct_indication_differentiation_flag, ppWritePackedMsg, end) && + push8(value->direct_indication, ppWritePackedMsg, end) && + push8(value->total_dci_length_including_padding, ppWritePackedMsg, end) && + push8(value->number_of_tx_antenna_ports, ppWritePackedMsg, end) && + pusharray16(value->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, value->number_of_tx_antenna_ports, ppWritePackedMsg, end)); +} + + +static uint8_t pack_dl_config_nbch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_nbch_pdu_rel13_t *value = (nfapi_dl_config_nbch_pdu_rel13_t *)tlv; + return (push16(value->length, ppWritePackedMsg, end) && + push16(value->pdu_index, ppWritePackedMsg, end) && + push16(value->transmission_power, ppWritePackedMsg, end) && + push16(value->hyper_sfn_2_lsbs, ppWritePackedMsg, end)); +} + + +static uint8_t pack_dl_config_npdcch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_npdcch_pdu_rel13_t *value = (nfapi_dl_config_npdcch_pdu_rel13_t *)tlv; + return (push16(value->length, ppWritePackedMsg, end) && + push16(value->pdu_index, ppWritePackedMsg, end) && + push8(value->ncce_index, ppWritePackedMsg, end) && + push8(value->aggregation_level, ppWritePackedMsg, end) && + push8(value->start_symbol, ppWritePackedMsg, end) && + push8(value->rnti_type, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->scrambling_reinitialization_batch_index, ppWritePackedMsg, end) && + push8(value->nrs_antenna_ports_assumed_by_the_ue, ppWritePackedMsg, end) && + push8(value->dci_format, ppWritePackedMsg, end) && + push8(value->scheduling_delay, ppWritePackedMsg, end) && + push8(value->resource_assignment, ppWritePackedMsg, end) && + push8(value->repetition_number, ppWritePackedMsg, end) && + push8(value->mcs, ppWritePackedMsg, end) && + push8(value->new_data_indicator, ppWritePackedMsg, end) && + push8(value->harq_ack_resource, ppWritePackedMsg, end) && + push8(value->npdcch_order_indication, ppWritePackedMsg, end) && + push8(value->starting_number_of_nprach_repetitions, ppWritePackedMsg, end) && + push8(value->subcarrier_indication_of_nprach, ppWritePackedMsg, end) && + push8(value->paging_direct_indication_differentation_flag, ppWritePackedMsg, end) && + push8(value->direct_indication, ppWritePackedMsg, end) && + push8(value->dci_subframe_repetition_number, ppWritePackedMsg, end) && + push8(value->total_dci_length_including_padding, ppWritePackedMsg, end)); +} + +static uint8_t pack_dl_config_ndlsch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_ndlsch_pdu_rel13_t *value = (nfapi_dl_config_ndlsch_pdu_rel13_t *)tlv; + return (push16(value->length, ppWritePackedMsg, end) && + push16(value->pdu_index, ppWritePackedMsg, end) && + push8(value->start_symbol, ppWritePackedMsg, end) && + push8(value->rnti_type, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push16(value->resource_assignment, ppWritePackedMsg, end) && + push16(value->repetition_number, ppWritePackedMsg, end) && + push8(value->modulation, ppWritePackedMsg, end) && + push8(value->number_of_subframes_for_resource_assignment, ppWritePackedMsg, end) && + push8(value->scrambling_sequence_initialization_cinit, ppWritePackedMsg, end) && + push16(value->sf_idx, ppWritePackedMsg, end) && + push8(value->nrs_antenna_ports_assumed_by_the_ue, ppWritePackedMsg, end)); +} + + +static uint8_t pack_dl_tti_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_dl_tti_request_pdu_t *value = (nfapi_nr_dl_tti_request_pdu_t *)tlv; + + if(!(push32(value->PDUSize, ppWritePackedMsg, end) && + push16(value->PDUType, ppWritePackedMsg, end) )) + return 0; + + // first match the pdu type, then call the respective function + switch(value->PDUType) { + case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: { + if(!(pack_dl_tti_csi_rs_pdu_rel15_value(&value->csi_rs_pdu.csi_rs_pdu_rel15,ppWritePackedMsg,end))) + return 0; + } + break; + + case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: { + if(!(pack_dl_tti_pdcch_pdu_rel15_value(&value->pdcch_pdu.pdcch_pdu_rel15,ppWritePackedMsg,end))) + return 0; + } + break; + + case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: { + if(!(pack_dl_tti_pdsch_pdu_rel15_value(&value->pdsch_pdu.pdsch_pdu_rel15,ppWritePackedMsg,end))) + return 0; + } + break; + + case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: { + if(!(pack_dl_tti_ssb_pdu_rel15_value(&value->ssb_pdu.ssb_pdu_rel15,ppWritePackedMsg,end))) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid DL_TTI pdu type %d \n", value->PDUType ); + } + break; + } + + return 1; +} + +static uint8_t pack_dl_config_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_dl_config_request_body_t *value = (nfapi_dl_config_request_body_t *)tlv; + + //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() dci:%u pdu:%u pdsch:%u rnti:%u pcfich:%u\n", __FUNCTION__, value->number_dci, value->number_pdu, value->number_pdsch_rnti, value->transmission_power_pcfich); + + if(!(push8(value->number_pdcch_ofdm_symbols, ppWritePackedMsg, end) && + push8(value->number_dci, ppWritePackedMsg, end) && + push16(value->number_pdu, ppWritePackedMsg, end) && + push8(value->number_pdsch_rnti, ppWritePackedMsg, end) && + push16(value->transmission_power_pcfich, ppWritePackedMsg, end))) { + return 0; + } + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_pdu; + + for(; i < total_number_of_pdus; ++i) { + nfapi_dl_config_request_pdu_t *pdu = &(value->dl_config_pdu_list[i]); + + if(push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) + return 0; + + // Put a 0 size in and then determine the size after the pdu + // has been writen and write the calculated size + uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; + pdu->pdu_size = 0; + + if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) + return 0; + + switch(pdu->pdu_type) { + case NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE: { + //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE\n", __FUNCTION__); + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL8_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel8_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL9_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel9_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL10_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel10_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL11_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel11, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel11_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL12_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel12, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel12_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL13_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel13_value))) { + return 0; + } + } + break; + + case NFAPI_DL_CONFIG_BCH_PDU_TYPE: { + //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() NFAPI_DL_CONFIG_BCH_PDU_TYPE\n", __FUNCTION__); + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_BCH_PDU_REL8_TAG, &pdu->bch_pdu.bch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_bch_pdu_rel8_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_MCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_MCH_PDU_REL8_TAG, &pdu->mch_pdu.mch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_mch_pdu_rel8_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_DLSCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL8_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel8_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL9_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel9_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL10_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel10_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL11_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel11, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel11_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL12_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel12, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel12_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL13_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_dlsch_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_PCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL8_TAG, &pdu->pch_pdu.pch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_pch_pdu_rel8_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL13_TAG, &pdu->pch_pdu.pch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_pch_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_PRS_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_PRS_PDU_REL9_TAG, &pdu->prs_pdu.prs_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_prs_pdu_rel9_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_CSI_RS_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL10_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_csi_rs_pdu_rel10_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL13_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_csi_rs_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_EPDCCH_DL_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL8_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel8, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel8_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL9_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel9, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel9_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL10_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel10, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel10_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL11_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel11, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel11_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL12_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel12, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel12_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL13_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_dci_dl_pdu_rel13_value) && + pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL11_TAG, &pdu->epdcch_pdu.epdcch_params_rel11, ppWritePackedMsg, end, &pack_dl_config_epdcch_parameters_rel11_value) & + pack_tlv(NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL13_TAG, &pdu->epdcch_pdu.epdcch_params_rel13, ppWritePackedMsg, end, &pack_dl_config_epdcch_parameters_rel13_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_MPDCCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_MPDCCH_PDU_REL13_TAG, &pdu->mpdcch_pdu.mpdcch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_mpdcch_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_NBCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_NBCH_PDU_REL13_TAG, &pdu->nbch_pdu.nbch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_nbch_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_NPDCCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_NPDCCH_PDU_REL13_TAG, &pdu->npdcch_pdu.npdcch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_npdcch_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_DL_CONFIG_NDLSCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_DL_CONFIG_REQUEST_NDLSCH_PDU_REL13_TAG, &pdu->ndlsch_pdu.ndlsch_pdu_rel13, ppWritePackedMsg, end, &pack_dl_config_ndlsch_pdu_rel13_value))) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); + } + break; + }; + + // add 1 for the pdu_type. The delta will include the pdu_size + pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); + + push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); + } + + return 1; +} + + +static uint8_t pack_dl_tti_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nr_dl_tti_request_t *pNfapiMsg = (nfapi_nr_dl_tti_request_t *)msg; + + if (!(push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && + push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && + push8(pNfapiMsg->dl_tti_request_body.nGroup, ppWritePackedMsg, end) && + push8(pNfapiMsg->dl_tti_request_body.nPDUs, ppWritePackedMsg, end) && + pusharray8(pNfapiMsg->dl_tti_request_body.nUe,256,pNfapiMsg->dl_tti_request_body.nGroup, ppWritePackedMsg, end) + //pusharray8(pNfapiMsg->PduIdx[0] ,256,256, ppWritePackedMsg, end) + )) + return 0; + + int arr[12]; + + for(int i=0; idl_tti_request_body.nGroup; i++) { + for(int j=0; jdl_tti_request_body.nUe[i]; j++) { + arr[j] = pNfapiMsg->dl_tti_request_body.PduIdx[i][j]; + } + + if(!(pusharrays32(arr,12,pNfapiMsg->dl_tti_request_body.nUe[i],ppWritePackedMsg, end))) + return 0; + } + + for(int i=0; idl_tti_request_body.nPDUs; i++) { + if(!pack_dl_tti_request_body_value(&pNfapiMsg->dl_tti_request_body.dl_tti_pdu_list[i],ppWritePackedMsg,end)) + return 0; + } + + return 1; +} + + +static uint8_t pack_dl_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_dl_config_request_t *pNfapiMsg = (nfapi_dl_config_request_t *)msg; + //return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + //pack_tlv(NFAPI_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->dl_config_request_body, ppWritePackedMsg, end, &pack_dl_config_request_body_value) && + //pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); + { + uint8_t x = push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end); + uint8_t y = pack_tlv(NFAPI_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->dl_config_request_body, ppWritePackedMsg, end, &pack_dl_config_request_body_value); + uint8_t z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); + + if (!x || !y || !z) { + NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() NFAPI_DL_CONFIG_REQUEST x:%u y:%u z:%u \n", __FUNCTION__,x,y,z); + } + + return x && y && z; + } +} + + + + +static uint8_t pack_ul_config_request_ulsch_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel8_t *ulsch_pdu_rel8 = (nfapi_ul_config_ulsch_pdu_rel8_t *)tlv; + return( push32(ulsch_pdu_rel8->handle, ppWritePackedMsg, end) && + push16(ulsch_pdu_rel8->size, ppWritePackedMsg, end) && + push16(ulsch_pdu_rel8->rnti, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->resource_block_start, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->number_of_resource_blocks, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->modulation_type, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->cyclic_shift_2_for_drms, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->frequency_hopping_enabled_flag, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->frequency_hopping_bits, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->new_data_indication, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->redundancy_version, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->harq_process_number, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->ul_tx_mode, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->current_tx_nb, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel8->n_srs, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_ulsch_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel10_t *ulsch_pdu_rel10 = (nfapi_ul_config_ulsch_pdu_rel10_t *)tlv; + return (push8(ulsch_pdu_rel10->resource_allocation_type, ppWritePackedMsg, end) && + push32(ulsch_pdu_rel10->resource_block_coding, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel10->transport_blocks, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel10->transmission_scheme, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel10->number_of_layers, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel10->codebook_index, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel10->disable_sequence_hopping_flag, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_ulsch_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel11_t *ulsch_pdu_rel11 = (nfapi_ul_config_ulsch_pdu_rel11_t *)tlv; + return (push8(ulsch_pdu_rel11->virtual_cell_id_enabled_flag, ppWritePackedMsg, end) && + push16(ulsch_pdu_rel11->npusch_identity, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel11->dmrs_config_flag, ppWritePackedMsg, end) && + push16(ulsch_pdu_rel11->ndmrs_csh_identity, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_ulsch_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel13_t *ulsch_pdu_rel13 = (nfapi_ul_config_ulsch_pdu_rel13_t *)tlv; + return (push8(ulsch_pdu_rel13->ue_type, ppWritePackedMsg, end) && + push16(ulsch_pdu_rel13->total_number_of_repetitions, ppWritePackedMsg, end) && + push16(ulsch_pdu_rel13->repetition_number, ppWritePackedMsg, end) && + push16(ulsch_pdu_rel13->initial_transmission_sf_io, ppWritePackedMsg, end) && + push8(ulsch_pdu_rel13->empty_symbols_due_to_re_tunning, ppWritePackedMsg, end)); +} + +//Pack fns for ul_tti PDUS + + +static uint8_t pack_ul_tti_request_prach_pdu(nfapi_nr_prach_pdu_t *prach_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { + return( + push16(prach_pdu->phys_cell_id, ppWritePackedMsg, end) && + push8(prach_pdu->num_prach_ocas, ppWritePackedMsg, end) && + push8(prach_pdu->prach_format, ppWritePackedMsg, end) && + push8(prach_pdu->num_ra, ppWritePackedMsg, end) && + push8(prach_pdu->prach_start_symbol, ppWritePackedMsg, end) && + push16(prach_pdu->num_cs, ppWritePackedMsg, end) + // TODO: ignoring beamforming tlv for now + ); +} + +static uint8_t pack_ul_tti_request_pucch_pdu(nfapi_nr_pucch_pdu_t *pucch_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { + return( + push16(pucch_pdu->rnti, ppWritePackedMsg, end) && + push32(pucch_pdu->handle, ppWritePackedMsg, end) && + push16(pucch_pdu->bwp_size, ppWritePackedMsg, end) && + push16(pucch_pdu->bwp_start, ppWritePackedMsg, end) && + push8(pucch_pdu->subcarrier_spacing, ppWritePackedMsg, end) && + push8(pucch_pdu->cyclic_prefix, ppWritePackedMsg, end) && + push8(pucch_pdu->format_type, ppWritePackedMsg, end) && + push8(pucch_pdu->multi_slot_tx_indicator, ppWritePackedMsg, end) && + push16(pucch_pdu->prb_start, ppWritePackedMsg, end) && + push16(pucch_pdu->prb_size, ppWritePackedMsg, end) && + push8(pucch_pdu->start_symbol_index, ppWritePackedMsg, end) && + push8(pucch_pdu->nr_of_symbols, ppWritePackedMsg, end) && + push8(pucch_pdu->freq_hop_flag, ppWritePackedMsg, end) && + push16(pucch_pdu->second_hop_prb, ppWritePackedMsg, end) && + push8(pucch_pdu->group_hop_flag, ppWritePackedMsg, end) && + push8(pucch_pdu->sequence_hop_flag, ppWritePackedMsg, end) && + push16(pucch_pdu->hopping_id, ppWritePackedMsg, end) && + push16(pucch_pdu->initial_cyclic_shift, ppWritePackedMsg, end) && + push16(pucch_pdu->data_scrambling_id, ppWritePackedMsg, end) && + push8(pucch_pdu->time_domain_occ_idx, ppWritePackedMsg, end) && + push8(pucch_pdu->pre_dft_occ_idx, ppWritePackedMsg, end) && + push8(pucch_pdu->pre_dft_occ_len, ppWritePackedMsg, end) && + push8(pucch_pdu->add_dmrs_flag, ppWritePackedMsg, end) && + push16(pucch_pdu->dmrs_scrambling_id, ppWritePackedMsg, end) && + push8(pucch_pdu->dmrs_cyclic_shift, ppWritePackedMsg, end) && + push8(pucch_pdu->sr_flag, ppWritePackedMsg, end) && + push8(pucch_pdu->bit_len_harq, ppWritePackedMsg, end) && + push16(pucch_pdu->bit_len_csi_part1, ppWritePackedMsg, end) && + push16(pucch_pdu->bit_len_csi_part2, ppWritePackedMsg, end) + // TODO: ignoring beamforming tlv for now + ); +} + + +static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { + if (!( + push16(pusch_pdu->pdu_bit_map, ppWritePackedMsg, end) && + push16(pusch_pdu->rnti, ppWritePackedMsg, end) && + push32(pusch_pdu->handle, ppWritePackedMsg, end) && + push16(pusch_pdu->bwp_size, ppWritePackedMsg, end) && + push16(pusch_pdu->bwp_start, ppWritePackedMsg, end) && + push8(pusch_pdu->subcarrier_spacing, ppWritePackedMsg, end) && + push8(pusch_pdu->cyclic_prefix, ppWritePackedMsg, end) && + push16(pusch_pdu->target_code_rate, ppWritePackedMsg, end) && + push8(pusch_pdu->qam_mod_order, ppWritePackedMsg, end) && + push8(pusch_pdu->mcs_index, ppWritePackedMsg, end) && + push8(pusch_pdu->mcs_table, ppWritePackedMsg, end) && + push8(pusch_pdu->transform_precoding, ppWritePackedMsg, end) && + push16(pusch_pdu->data_scrambling_id, ppWritePackedMsg, end) && + push8(pusch_pdu->nrOfLayers, ppWritePackedMsg, end) && + push16(pusch_pdu->ul_dmrs_symb_pos, ppWritePackedMsg, end) && + push8(pusch_pdu->dmrs_config_type, ppWritePackedMsg, end) && + push16(pusch_pdu->ul_dmrs_scrambling_id, ppWritePackedMsg, end) && + push8(pusch_pdu->scid, ppWritePackedMsg, end) && + push8(pusch_pdu->num_dmrs_cdm_grps_no_data, ppWritePackedMsg, end) && + push16(pusch_pdu->dmrs_ports, ppWritePackedMsg, end) && + push8(pusch_pdu->resource_alloc, ppWritePackedMsg, end) && + push8(pusch_pdu->resource_alloc,ppWritePackedMsg, end) && + push16(pusch_pdu->dmrs_ports, ppWritePackedMsg, end) && + push16(pusch_pdu->rb_start, ppWritePackedMsg, end) && + push16(pusch_pdu->rb_size, ppWritePackedMsg, end) && + push8(pusch_pdu->vrb_to_prb_mapping, ppWritePackedMsg, end) && + push8(pusch_pdu->frequency_hopping, ppWritePackedMsg, end) && + push16(pusch_pdu->tx_direct_current_location, ppWritePackedMsg, end) && + push8(pusch_pdu->uplink_frequency_shift_7p5khz, ppWritePackedMsg, end) && + push8(pusch_pdu->start_symbol_index, ppWritePackedMsg, end) && + push8(pusch_pdu->nr_of_symbols, ppWritePackedMsg, end) + // TODO: ignoring beamforming tlv for now + )) + return 0; + + //Pack Optional Data only included if indicated in pduBitmap + switch(pusch_pdu->pdu_bit_map) { + case PUSCH_PDU_BITMAP_PUSCH_DATA: { + // pack optional TLVs + return( + push8(pusch_pdu->pusch_data.rv_index, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_data.harq_process_id, ppWritePackedMsg, end) && + push32(pusch_pdu->pusch_data.tb_size, ppWritePackedMsg, end) && + push16(pusch_pdu->pusch_data.num_cb, ppWritePackedMsg, end) && + pusharray8(pusch_pdu->pusch_data.cb_present_and_position,1,1,ppWritePackedMsg, end) + ); + } + break; + + case PUSCH_PDU_BITMAP_PUSCH_UCI: { + return( + push16(pusch_pdu->pusch_uci.harq_ack_bit_length, ppWritePackedMsg, end) && + push16(pusch_pdu->pusch_uci.csi_part1_bit_length, ppWritePackedMsg, end) && + push16(pusch_pdu->pusch_uci.csi_part2_bit_length, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_uci.alpha_scaling, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_uci.beta_offset_harq_ack, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_uci.beta_offset_csi1, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_uci.beta_offset_csi2, ppWritePackedMsg, end) + ); + } + break; + + case PUSCH_PDU_BITMAP_PUSCH_PTRS: { + return( + push8(pusch_pdu->pusch_ptrs.num_ptrs_ports, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_dmrs_port, ppWritePackedMsg, end) && + push16(pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_port_index, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_ptrs.ptrs_time_density, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_ptrs.ptrs_freq_density, ppWritePackedMsg, end) && + push8(pusch_pdu->pusch_ptrs.ul_ptrs_power, ppWritePackedMsg, end) + ); + } + break; + + case PUSCH_PDU_BITMAP_DFTS_OFDM: { + return( + push8(pusch_pdu->dfts_ofdm.low_papr_group_number, ppWritePackedMsg, end) && + push16(pusch_pdu->dfts_ofdm.low_papr_sequence_number, ppWritePackedMsg, end) && + push8(pusch_pdu->dfts_ofdm.ul_ptrs_sample_density, ppWritePackedMsg, end) && + push8(pusch_pdu->dfts_ofdm.ul_ptrs_time_density_transform_precoding, ppWritePackedMsg, end) + ); + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_INFO, "Invalid pdu bitmap %d \n", pusch_pdu->pdu_bit_map ); + } + } + + return 1; +} + +static uint8_t pack_ul_tti_request_srs_pdu(nfapi_nr_srs_pdu_t *srs_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { + return( + push16(srs_pdu->rnti, ppWritePackedMsg, end) && + push32(srs_pdu->handle, ppWritePackedMsg, end) && + push16(srs_pdu->bwp_size, ppWritePackedMsg, end) && + push16(srs_pdu->bwp_start, ppWritePackedMsg, end) && + push8(srs_pdu->subcarrier_spacing, ppWritePackedMsg, end) && + push8(srs_pdu->cyclic_prefix, ppWritePackedMsg, end) && + push8(srs_pdu->num_ant_ports, ppWritePackedMsg, end) && + push8(srs_pdu->num_symbols, ppWritePackedMsg, end) && + push8(srs_pdu->num_repetitions, ppWritePackedMsg, end) && + push8(srs_pdu->time_start_position, ppWritePackedMsg, end) && + push8(srs_pdu->config_index, ppWritePackedMsg, end) && + push16(srs_pdu->sequence_id, ppWritePackedMsg, end) && + push8(srs_pdu->bandwidth_index, ppWritePackedMsg, end) && + push8(srs_pdu->comb_size, ppWritePackedMsg, end) && + push8(srs_pdu->comb_offset, ppWritePackedMsg, end) && + push8(srs_pdu->cyclic_shift, ppWritePackedMsg, end) && + push8(srs_pdu->frequency_position, ppWritePackedMsg, end) && + push8(srs_pdu->frequency_shift, ppWritePackedMsg, end) && + push8(srs_pdu->frequency_hopping, ppWritePackedMsg, end) && + push8(srs_pdu->group_or_sequence_hopping, ppWritePackedMsg, end) && + push8(srs_pdu->resource_type, ppWritePackedMsg, end) && + push16(srs_pdu->t_srs, ppWritePackedMsg, end) && + push16(srs_pdu->t_offset, ppWritePackedMsg, end) + // TODO: ignoring beamforming tlv for now + ); +} + +static uint8_t pack_ul_config_request_ulsch_pdu(nfapi_ul_config_ulsch_pdu *ulsch_pdu, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL8_TAG, &ulsch_pdu->ulsch_pdu_rel8, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel8_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL10_TAG, &ulsch_pdu->ulsch_pdu_rel10, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel10_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL11_TAG, &ulsch_pdu->ulsch_pdu_rel11, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel11_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL13_TAG, &ulsch_pdu->ulsch_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_rel13_value)); +} + +static uint8_t pack_ul_config_request_cqi_ri_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_ri_information_rel8_t *cqi_ri_info_rel8 = (nfapi_ul_config_cqi_ri_information_rel8_t *)tlv; + return ( push8(cqi_ri_info_rel8->dl_cqi_pmi_size_rank_1, ppWritePackedMsg, end) && + push8(cqi_ri_info_rel8->dl_cqi_pmi_size_rank_greater_1, ppWritePackedMsg, end) && + push8(cqi_ri_info_rel8->ri_size, ppWritePackedMsg, end) && + push8(cqi_ri_info_rel8->delta_offset_cqi, ppWritePackedMsg, end) && + push8(cqi_ri_info_rel8->delta_offset_ri, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_cqi_ri_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_ri_information_rel9_t *cqi_ri_info_rel9 = (nfapi_ul_config_cqi_ri_information_rel9_t *)tlv; + + if(!(push8(cqi_ri_info_rel9->report_type, ppWritePackedMsg, end) && + push8(cqi_ri_info_rel9->delta_offset_cqi, ppWritePackedMsg, end) && + push8(cqi_ri_info_rel9->delta_offset_ri, ppWritePackedMsg, end))) { + return 0; + } + + switch(cqi_ri_info_rel9->report_type) { + case NFAPI_CSI_REPORT_TYPE_PERIODIC: { + if(!(push8(cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size, ppWritePackedMsg, end) && + push8(cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.control_type, ppWritePackedMsg, end))) { + return 0; + } + } + break; + + case NFAPI_CSI_REPORT_TYPE_APERIODIC: { + if(push8(cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc, ppWritePackedMsg, end) == 0) + return 0; + + uint8_t i; + + for(i = 0; i < cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc; ++i) { + if(push8(cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].ri_size, ppWritePackedMsg, end) == 0) + return 0; + + uint8_t j; + + for(j = 0; j < 8; ++j) { + if(push8(cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].dl_cqi_pmi_size[j], ppWritePackedMsg, end) == 0) + return 0; + } + } + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid report type %d \n", cqi_ri_info_rel9->report_type ); + } + break; + }; + + return 1; +} + +static uint8_t pack_ul_config_request_cqi_ri_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_ri_information_rel13_t *cqi_ri_info_rel13 = (nfapi_ul_config_cqi_ri_information_rel13_t *)tlv; + + switch(cqi_ri_info_rel13->report_type) { + case NFAPI_CSI_REPORT_TYPE_PERIODIC: { + if(push16(cqi_ri_info_rel13->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size_2, ppWritePackedMsg, end) == 0) + return 0; + } + break; + + case NFAPI_CSI_REPORT_TYPE_APERIODIC: { + // No parameters + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid report type %d \n", cqi_ri_info_rel13->report_type ); + } + break; + }; + + return 1; +} + +static uint8_t pack_ul_config_request_cqi_ri_information(nfapi_ul_config_cqi_ri_information *cqi_ri_info, uint8_t **ppWritePackedMsg, uint8_t *end) { + return (pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL8_TAG, &cqi_ri_info->cqi_ri_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_cqi_ri_rel8_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL9_TAG, &cqi_ri_info->cqi_ri_information_rel9, ppWritePackedMsg, end, &pack_ul_config_request_cqi_ri_rel9_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL13_TAG, &cqi_ri_info->cqi_ri_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_cqi_ri_rel13_value)); +} + +static uint8_t pack_ul_config_request_init_tx_params_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_initial_transmission_parameters_rel8_t *init_tx_params_rel8 = (nfapi_ul_config_initial_transmission_parameters_rel8_t *)tlv; + return (push8(init_tx_params_rel8->n_srs_initial, ppWritePackedMsg, end) && + push8(init_tx_params_rel8->initial_number_of_resource_blocks, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_initial_transmission_parameters(nfapi_ul_config_initial_transmission_parameters *init_tx_params, uint8_t **ppWritePackedMsg, uint8_t *end) { + return pack_tlv(NFAPI_UL_CONFIG_REQUEST_INITIAL_TRANSMISSION_PARAMETERS_REL8_TAG, &init_tx_params->initial_transmission_parameters_rel8, ppWritePackedMsg, end, + &pack_ul_config_request_init_tx_params_rel8_value); +} + +static uint8_t pack_ul_config_request_ulsch_harq_info_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_harq_information_rel10_t *harq_info_rel10 = (nfapi_ul_config_ulsch_harq_information_rel10_t *)tlv; + return (push8(harq_info_rel10->harq_size, ppWritePackedMsg, end) && + push8(harq_info_rel10->delta_offset_harq, ppWritePackedMsg, end) && + push8(harq_info_rel10->ack_nack_mode, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_ulsch_harq_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_ulsch_harq_information_rel13_t *)tlv; + return (push16(harq_info_rel13->harq_size_2, ppWritePackedMsg, end) && + push8(harq_info_rel13->delta_offset_harq_2, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_ulsch_harq_information(nfapi_ul_config_ulsch_harq_information *harq_info, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL10_TAG, &harq_info->harq_information_rel10, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_harq_info_rel10_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL13_TAG, &harq_info->harq_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_ulsch_harq_info_rel13_value)); +} + +static uint8_t pack_ul_config_request_ue_info_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ue_information_rel8_t *ue_info_rel8 = (nfapi_ul_config_ue_information_rel8_t *)tlv; + return ( push32(ue_info_rel8->handle, ppWritePackedMsg, end) && + push16(ue_info_rel8->rnti, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_ue_info_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ue_information_rel11_t *ue_info_rel11 = (nfapi_ul_config_ue_information_rel11_t *)tlv; + return ( push8(ue_info_rel11->virtual_cell_id_enabled_flag, ppWritePackedMsg, end) && + push16(ue_info_rel11->npusch_identity, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_ue_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_ue_information_rel13_t *ue_info_rel13 = (nfapi_ul_config_ue_information_rel13_t *)tlv; + return ( push8(ue_info_rel13->ue_type, ppWritePackedMsg, end) && + push8(ue_info_rel13->empty_symbols, ppWritePackedMsg, end) && + push16(ue_info_rel13->total_number_of_repetitions, ppWritePackedMsg, end) && + push16(ue_info_rel13->repetition_number, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_ue_information(nfapi_ul_config_ue_information *ue_info, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL8_TAG, &ue_info->ue_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_ue_info_rel8_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL11_TAG, &ue_info->ue_information_rel11, ppWritePackedMsg, end, &pack_ul_config_request_ue_info_rel11_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL13_TAG, &ue_info->ue_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_ue_info_rel13_value)); +} + +static uint8_t pack_ul_config_request_harq_info_rel10_tdd_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel10_tdd_t *harq_info_rel10_tdd = (nfapi_ul_config_harq_information_rel10_tdd_t *)tlv; + return ( push8(harq_info_rel10_tdd->harq_size, ppWritePackedMsg, end) && + push8(harq_info_rel10_tdd->ack_nack_mode, ppWritePackedMsg, end) && + push8(harq_info_rel10_tdd->number_of_pucch_resources, ppWritePackedMsg, end) && + push16(harq_info_rel10_tdd->n_pucch_1_0, ppWritePackedMsg, end) && + push16(harq_info_rel10_tdd->n_pucch_1_1, ppWritePackedMsg, end) && + push16(harq_info_rel10_tdd->n_pucch_1_2, ppWritePackedMsg, end) && + push16(harq_info_rel10_tdd->n_pucch_1_3, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_harq_info_rel8_fdd_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel8_fdd_t *harq_info_rel8_fdd = (nfapi_ul_config_harq_information_rel8_fdd_t *)tlv; + return ( push16(harq_info_rel8_fdd->n_pucch_1_0, ppWritePackedMsg, end) && + push8(harq_info_rel8_fdd->harq_size, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_harq_info_rel9_fdd_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel9_fdd_t *harq_info_rel9_fdd = (nfapi_ul_config_harq_information_rel9_fdd_t *)tlv; + return ( push8(harq_info_rel9_fdd->harq_size, ppWritePackedMsg, end) && + push8(harq_info_rel9_fdd->ack_nack_mode, ppWritePackedMsg, end) && + push8(harq_info_rel9_fdd->number_of_pucch_resources, ppWritePackedMsg, end) && + push16(harq_info_rel9_fdd->n_pucch_1_0, ppWritePackedMsg, end) && + push16(harq_info_rel9_fdd->n_pucch_1_1, ppWritePackedMsg, end) && + push16(harq_info_rel9_fdd->n_pucch_1_2, ppWritePackedMsg, end) && + push16(harq_info_rel9_fdd->n_pucch_1_3, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_harq_info_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel11_t *harq_info_rel11 = (nfapi_ul_config_harq_information_rel11_t *)tlv; + return ( push8(harq_info_rel11->num_ant_ports, ppWritePackedMsg, end) && + push16(harq_info_rel11->n_pucch_2_0, ppWritePackedMsg, end) && + push16(harq_info_rel11->n_pucch_2_1, ppWritePackedMsg, end) && + push16(harq_info_rel11->n_pucch_2_2, ppWritePackedMsg, end) && + push16(harq_info_rel11->n_pucch_2_3, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_harq_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_harq_information_rel13_t *)tlv; + return ( push16(harq_info_rel13->harq_size_2, ppWritePackedMsg, end) && + push8(harq_info_rel13->starting_prb, ppWritePackedMsg, end) && + push8(harq_info_rel13->n_prb, ppWritePackedMsg, end) && + push8(harq_info_rel13->cdm_index, ppWritePackedMsg, end) && + push8(harq_info_rel13->n_srs, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_harq_information(nfapi_ul_config_harq_information *harq_info, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL10_TDD_TAG, &harq_info->harq_information_rel10_tdd, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel10_tdd_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL8_FDD_TAG, &harq_info->harq_information_rel8_fdd, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel8_fdd_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL9_FDD_TAG, &harq_info->harq_information_rel9_fdd, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel9_fdd_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL11_TAG, &harq_info->harq_information_rel11, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel11_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL13_TAG, &harq_info->harq_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_harq_info_rel13_value)); +} + +static uint8_t pack_ul_config_request_cqi_info_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_information_rel8_t *cqi_info_rel8 = (nfapi_ul_config_cqi_information_rel8_t *)tlv; + return ( push16(cqi_info_rel8->pucch_index, ppWritePackedMsg, end) && + push8(cqi_info_rel8->dl_cqi_pmi_size, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_cqi_info_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_information_rel10_t *cqi_info_rel10 = (nfapi_ul_config_cqi_information_rel10_t *)tlv; + return ( push8(cqi_info_rel10->number_of_pucch_resource, ppWritePackedMsg, end) && + push16(cqi_info_rel10->pucch_index_p1, ppWritePackedMsg, end)); +} +static uint8_t pack_ul_config_request_cqi_info_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_information_rel13_t *cqi_info_rel13 = (nfapi_ul_config_cqi_information_rel13_t *)tlv; + return ( push8(cqi_info_rel13->csi_mode, ppWritePackedMsg, end) && + push16(cqi_info_rel13->dl_cqi_pmi_size_2, ppWritePackedMsg, end) && + push8(cqi_info_rel13->starting_prb, ppWritePackedMsg, end) && + push8(cqi_info_rel13->n_prb, ppWritePackedMsg, end) && + push8(cqi_info_rel13->cdm_index, ppWritePackedMsg, end) && + push8(cqi_info_rel13->n_srs, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_cqi_information(nfapi_ul_config_cqi_information *cqi_info, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL8_TAG, &cqi_info->cqi_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_cqi_info_rel8_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL10_TAG, &cqi_info->cqi_information_rel10, ppWritePackedMsg, end, &pack_ul_config_request_cqi_info_rel10_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL13_TAG, &cqi_info->cqi_information_rel13, ppWritePackedMsg, end, &pack_ul_config_request_cqi_info_rel13_value)); +} + +static uint8_t pack_ul_config_request_sr_info_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_sr_information_rel8_t *sr_info_rel8 = (nfapi_ul_config_sr_information_rel8_t *)tlv; + return push16(sr_info_rel8->pucch_index, ppWritePackedMsg, end); +} +static uint8_t pack_ul_config_request_sr_info_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_sr_information_rel10_t *sr_info_rel10 = (nfapi_ul_config_sr_information_rel10_t *)tlv; + return ( push8(sr_info_rel10->number_of_pucch_resources, ppWritePackedMsg, end) && + push16(sr_info_rel10->pucch_index_p1, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_sr_information(nfapi_ul_config_sr_information *sr_info, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( pack_tlv(NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL8_TAG, &sr_info->sr_information_rel8, ppWritePackedMsg, end, &pack_ul_config_request_sr_info_rel8_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL10_TAG, &sr_info->sr_information_rel10, ppWritePackedMsg, end, &pack_ul_config_request_sr_info_rel10_value)); +} + +static uint8_t pack_ul_config_request_srs_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_srs_pdu_rel8_t *srs_pdu_rel8 = (nfapi_ul_config_srs_pdu_rel8_t *)tlv; + return (push32(srs_pdu_rel8->handle, ppWritePackedMsg, end) && + push16(srs_pdu_rel8->size, ppWritePackedMsg, end) && + push16(srs_pdu_rel8->rnti, ppWritePackedMsg, end) && + push8(srs_pdu_rel8->srs_bandwidth, ppWritePackedMsg, end) && + push8(srs_pdu_rel8->frequency_domain_position, ppWritePackedMsg, end) && + push8(srs_pdu_rel8->srs_hopping_bandwidth, ppWritePackedMsg, end) && + push8(srs_pdu_rel8->transmission_comb, ppWritePackedMsg, end) && + push16(srs_pdu_rel8->i_srs, ppWritePackedMsg, end) && + push8(srs_pdu_rel8->sounding_reference_cyclic_shift, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_srs_pdu_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_srs_pdu_rel10_t *srs_pdu_rel10 = (nfapi_ul_config_srs_pdu_rel10_t *)tlv; + return push8(srs_pdu_rel10->antenna_port, ppWritePackedMsg, end); +} + +static uint8_t pack_ul_config_request_srs_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_srs_pdu_rel13_t *srs_pdu_rel13 = (nfapi_ul_config_srs_pdu_rel13_t *)tlv; + return ( push8(srs_pdu_rel13->number_of_combs, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_nb_harq_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_nb_harq_information_rel13_fdd_t *nb_harq_pdu_rel13 = (nfapi_ul_config_nb_harq_information_rel13_fdd_t *)tlv; + return ( push8(nb_harq_pdu_rel13->harq_ack_resource, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_config_request_nulsch_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_nulsch_pdu_rel13_t *nulsch_pdu_rel13 = (nfapi_ul_config_nulsch_pdu_rel13_t *)tlv; + return (push8(nulsch_pdu_rel13->nulsch_format, ppWritePackedMsg, end) && + push32(nulsch_pdu_rel13->handle, ppWritePackedMsg, end) && + push16(nulsch_pdu_rel13->size, ppWritePackedMsg, end) && + push16(nulsch_pdu_rel13->rnti, ppWritePackedMsg, end) && + push8(nulsch_pdu_rel13->subcarrier_indication, ppWritePackedMsg, end) && + push8(nulsch_pdu_rel13->resource_assignment, ppWritePackedMsg, end) && + push8(nulsch_pdu_rel13->mcs, ppWritePackedMsg, end) && + push8(nulsch_pdu_rel13->redudancy_version, ppWritePackedMsg, end) && + push8(nulsch_pdu_rel13->repetition_number, ppWritePackedMsg, end) && + push8(nulsch_pdu_rel13->new_data_indication, ppWritePackedMsg, end) && + push8(nulsch_pdu_rel13->n_srs, ppWritePackedMsg, end) && + push16(nulsch_pdu_rel13->scrambling_sequence_initialization_cinit, ppWritePackedMsg, end) && + push16(nulsch_pdu_rel13->sf_idx, ppWritePackedMsg, end) && + pack_ul_config_request_ue_information(&(nulsch_pdu_rel13->ue_information), ppWritePackedMsg, end) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_NB_HARQ_INFORMATION_REL13_FDD_TAG, &nulsch_pdu_rel13->nb_harq_information.nb_harq_information_rel13_fdd, ppWritePackedMsg, end, + &pack_ul_config_request_nb_harq_rel13_value)); +} +static uint8_t pack_ul_config_request_nrach_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_nrach_pdu_rel13_t *nrach_pdu_rel13 = (nfapi_ul_config_nrach_pdu_rel13_t *)tlv; + return ( push8(nrach_pdu_rel13->nprach_config_0, ppWritePackedMsg, end) && + push8(nrach_pdu_rel13->nprach_config_1, ppWritePackedMsg, end) && + push8(nrach_pdu_rel13->nprach_config_2, ppWritePackedMsg, end)); +} + + + +static uint8_t pack_ul_tti_pdu_list_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_ul_tti_request_number_of_pdus_t *value = (nfapi_nr_ul_tti_request_number_of_pdus_t *)tlv; + + if(!(push16(value->pdu_size, ppWritePackedMsg, end) && + push16(value->pdu_type, ppWritePackedMsg, end) )) + return 0; + + // first match the pdu type, then call the respective function + switch(value->pdu_type) { + case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: { + if(!pack_ul_tti_request_prach_pdu(&value->prach_pdu, ppWritePackedMsg, end)) + return 0; + } + break; + + case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE: { + if(!pack_ul_tti_request_pucch_pdu(&value->pucch_pdu, ppWritePackedMsg, end)) + return 0; + } + break; + + case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: { + if(!pack_ul_tti_request_pusch_pdu(&value->pusch_pdu, ppWritePackedMsg, end)) + return 0; + } + break; + + case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: { + if(!pack_ul_tti_request_srs_pdu(&value->srs_pdu, ppWritePackedMsg, end)) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid UL_TTI pdu type %d \n", value->pdu_type ); + } + break; + } + + return 1; +} + +static uint8_t pack_ul_tti_groups_list_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_ul_tti_request_number_of_groups_t *value = (nfapi_nr_ul_tti_request_number_of_groups_t *)tlv; + + if(!push8(value->n_ue, ppWritePackedMsg, end)) + return 0; + + for(int i=0; in_ue; i++) { + if(!push8(value->ue_list[i].pdu_idx, ppWritePackedMsg, end)) + return 0; + } + + return 1; +} + +static uint8_t pack_ul_config_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_config_request_body_t *value = (nfapi_ul_config_request_body_t *)tlv; + + if(!(push8(value->number_of_pdus, ppWritePackedMsg, end) && + push8(value->rach_prach_frequency_resources, ppWritePackedMsg, end) && + push8(value->srs_present, ppWritePackedMsg, end))) + return 0; + + uint16_t i = 0; + + for(i = 0; i < value->number_of_pdus; ++i) { + nfapi_ul_config_request_pdu_t *pdu = &(value->ul_config_pdu_list[i]); + + if(push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) + return 0; + + // Put a 0 size in and then determine the size after the pdu + // has been writen and write the calculated size + uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; + pdu->pdu_size = 0; + + if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) + return 0; + + switch(pdu->pdu_type) { + case NFAPI_UL_CONFIG_ULSCH_PDU_TYPE: { + if(!pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_pdu), ppWritePackedMsg, end)) + return 0; + } + break; + + case NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE: { + if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_cqi_ri_pdu.ulsch_pdu), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_ri_information(&(pdu->ulsch_cqi_ri_pdu.cqi_ri_information), ppWritePackedMsg, end) && + pack_ul_config_request_initial_transmission_parameters(&(pdu->ulsch_cqi_ri_pdu.initial_transmission_parameters), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE: { + if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_harq_pdu.ulsch_pdu), ppWritePackedMsg, end) && + pack_ul_config_request_ulsch_harq_information(&(pdu->ulsch_harq_pdu.harq_information), ppWritePackedMsg, end) && + pack_ul_config_request_initial_transmission_parameters(&(pdu->ulsch_harq_pdu.initial_transmission_parameters), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE: { + if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_ri_information(&(pdu->ulsch_cqi_harq_ri_pdu.cqi_ri_information), ppWritePackedMsg, end) && + pack_ul_config_request_ulsch_harq_information(&(pdu->ulsch_cqi_harq_ri_pdu.harq_information), ppWritePackedMsg, end) && + pack_ul_config_request_initial_transmission_parameters(&(pdu->ulsch_cqi_harq_ri_pdu.initial_transmission_parameters), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_pdu.ue_information), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_information(&(pdu->uci_cqi_pdu.cqi_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->uci_sr_pdu.ue_information), ppWritePackedMsg, end) && + pack_ul_config_request_sr_information(&(pdu->uci_sr_pdu.sr_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->uci_harq_pdu.ue_information), ppWritePackedMsg, end) && + pack_ul_config_request_harq_information(&(pdu->uci_harq_pdu.harq_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->uci_sr_harq_pdu.ue_information), ppWritePackedMsg, end) && + pack_ul_config_request_sr_information(&(pdu->uci_sr_harq_pdu.sr_information), ppWritePackedMsg, end) && + pack_ul_config_request_harq_information(&(pdu->uci_sr_harq_pdu.harq_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_harq_pdu.ue_information), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_information(&(pdu->uci_cqi_harq_pdu.cqi_information), ppWritePackedMsg, end) && + pack_ul_config_request_harq_information(&(pdu->uci_cqi_harq_pdu.harq_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_sr_pdu.ue_information), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_information(&(pdu->uci_cqi_sr_pdu.cqi_information), ppWritePackedMsg, end) && + pack_ul_config_request_sr_information(&(pdu->uci_cqi_sr_pdu.sr_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->uci_cqi_sr_harq_pdu.ue_information), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_information(&(pdu->uci_cqi_sr_harq_pdu.cqi_information), ppWritePackedMsg, end) && + pack_ul_config_request_sr_information(&(pdu->uci_cqi_sr_harq_pdu.sr_information), ppWritePackedMsg, end) && + pack_ul_config_request_harq_information(&(pdu->uci_cqi_sr_harq_pdu.harq_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_SRS_PDU_TYPE: { + if(!(pack_tlv(NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL8_TAG, &pdu->srs_pdu.srs_pdu_rel8, ppWritePackedMsg, end, &pack_ul_config_request_srs_pdu_rel8_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL10_TAG, &pdu->srs_pdu.srs_pdu_rel10, ppWritePackedMsg, end, &pack_ul_config_request_srs_pdu_rel10_value) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL13_TAG, &pdu->srs_pdu.srs_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_srs_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_HARQ_BUFFER_PDU_TYPE: { + if(!(pack_ul_config_request_ue_information(&(pdu->harq_buffer_pdu.ue_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_ULSCH_UCI_CSI_PDU_TYPE: { + if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_uci_csi_pdu.ulsch_pdu), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_information(&(pdu->ulsch_uci_csi_pdu.csi_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE: { + if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_uci_harq_pdu.ulsch_pdu), ppWritePackedMsg, end) && + pack_ul_config_request_harq_information(&(pdu->ulsch_uci_harq_pdu.harq_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE: { + if(!(pack_ul_config_request_ulsch_pdu(&(pdu->ulsch_csi_uci_harq_pdu.ulsch_pdu), ppWritePackedMsg, end) && + pack_ul_config_request_cqi_information(&(pdu->ulsch_csi_uci_harq_pdu.csi_information), ppWritePackedMsg, end) && + pack_ul_config_request_harq_information(&(pdu->ulsch_csi_uci_harq_pdu.harq_information), ppWritePackedMsg, end))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_NULSCH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_UL_CONFIG_REQUEST_NULSCH_PDU_REL13_TAG, &pdu->nulsch_pdu.nulsch_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_nulsch_pdu_rel13_value))) + return 0; + } + break; + + case NFAPI_UL_CONFIG_NRACH_PDU_TYPE: { + if(!(pack_tlv(NFAPI_UL_CONFIG_REQUEST_NRACH_PDU_REL13_TAG, &pdu->nrach_pdu.nrach_pdu_rel13, ppWritePackedMsg, end, &pack_ul_config_request_nrach_pdu_rel13_value))) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); + } + break; + }; + + // add 1 for the pdu_type. The delta will include the pdu_size + pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); + + push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); + } + + return 1; +} + + +static uint8_t pack_ul_tti_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nr_ul_tti_request_t *pNfapiMsg = (nfapi_nr_ul_tti_request_t *)msg; + + if (!(push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && + push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && + push8(pNfapiMsg->n_pdus, ppWritePackedMsg, end) && + push8(pNfapiMsg->rach_present, ppWritePackedMsg, end) && + push8(pNfapiMsg->n_ulsch, ppWritePackedMsg, end) && + push8(pNfapiMsg->n_ulcch, ppWritePackedMsg, end) && + push8(pNfapiMsg->n_group, ppWritePackedMsg, end) )) + return 0; + + for(int i=0; in_pdus; i++) { + if(!pack_ul_tti_pdu_list_value(&pNfapiMsg->pdus_list[i], ppWritePackedMsg, end)) + return 0; + } + + for(int i=0; in_group; i++) { + if(!pack_ul_tti_groups_list_value(&pNfapiMsg->groups_list[i], ppWritePackedMsg, end)) + return 0; + } + + return 1; +} + + +static uint8_t pack_ul_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_ul_config_request_t *pNfapiMsg = (nfapi_ul_config_request_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_UL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->ul_config_request_body, ppWritePackedMsg, end, &pack_ul_config_request_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)) ; +} + +static uint8_t pack_hi_dci0_hi_rel8_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_hi_pdu_rel8_t *hi_pdu_rel8 = (nfapi_hi_dci0_hi_pdu_rel8_t *)tlv; + return ( push8(hi_pdu_rel8->resource_block_start, ppWritePackedMsg, end) && + push8(hi_pdu_rel8->cyclic_shift_2_for_drms, ppWritePackedMsg, end) && + push8(hi_pdu_rel8->hi_value, ppWritePackedMsg, end) && + push8(hi_pdu_rel8->i_phich, ppWritePackedMsg, end) && + push16(hi_pdu_rel8->transmission_power, ppWritePackedMsg, end)); +} + +static uint8_t pack_hi_dci0_hi_rel10_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_hi_pdu_rel10_t *hi_pdu_rel10 = (nfapi_hi_dci0_hi_pdu_rel10_t *)tlv; + return ( push8(hi_pdu_rel10->flag_tb2, ppWritePackedMsg, end) && + push8(hi_pdu_rel10->hi_value_2, ppWritePackedMsg, end)); +} + +static uint8_t pack_hi_dci0_dci_rel8_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_dci_pdu_rel8_t *dci_pdu_rel8 = (nfapi_hi_dci0_dci_pdu_rel8_t *)tlv; + return ( push8(dci_pdu_rel8->dci_format, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->cce_index, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->aggregation_level, ppWritePackedMsg, end) && + push16(dci_pdu_rel8->rnti, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->resource_block_start, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->number_of_resource_block, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->mcs_1, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->cyclic_shift_2_for_drms, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->frequency_hopping_enabled_flag, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->frequency_hopping_bits, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->new_data_indication_1, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->ue_tx_antenna_seleciton, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->tpc, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->cqi_csi_request, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->ul_index, ppWritePackedMsg, end) && + push8(dci_pdu_rel8->dl_assignment_index, ppWritePackedMsg, end) && + push32(dci_pdu_rel8->tpc_bitmap, ppWritePackedMsg, end) && + push16(dci_pdu_rel8->transmission_power, ppWritePackedMsg, end)); +} + +static uint8_t pack_hi_dci0_dci_rel10_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_dci_pdu_rel10_t *dci_pdu_rel10 = (nfapi_hi_dci0_dci_pdu_rel10_t *)tlv; + return ( push8(dci_pdu_rel10->cross_carrier_scheduling_flag, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->carrier_indicator, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->size_of_cqi_csi_feild, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->srs_flag, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->srs_request, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->resource_allocation_flag, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->resource_allocation_type, ppWritePackedMsg, end) && + push32(dci_pdu_rel10->resource_block_coding, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->mcs_2, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->new_data_indication_2, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->number_of_antenna_ports, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->tpmi, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->total_dci_length_including_padding, ppWritePackedMsg, end) && + push8(dci_pdu_rel10->n_ul_rb, ppWritePackedMsg, end)); +} + +static uint8_t pack_hi_dci0_dci_rel12_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_dci_pdu_rel12_t *dci_pdu_rel12 = (nfapi_hi_dci0_dci_pdu_rel12_t *)tlv; + return ( push8(dci_pdu_rel12->pscch_resource, ppWritePackedMsg, end) && + push8(dci_pdu_rel12->time_resource_pattern, ppWritePackedMsg, end)); +} + +static uint8_t pack_hi_dci0_mpdcch_dci_rel13_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *mpdcch_dci_pdu_rel13 = (nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *)tlv; + return ( push8(mpdcch_dci_pdu_rel13->mpdcch_narrowband, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->number_of_prb_pairs, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->resource_block_assignment, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->mpdcch_transmission_type, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->start_symbol, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->ecce_index, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->aggreagation_level, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->rnti_type, ppWritePackedMsg, end) && + push16(mpdcch_dci_pdu_rel13->rnti, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->ce_mode, ppWritePackedMsg, end) && + push16(mpdcch_dci_pdu_rel13->drms_scrambling_init, ppWritePackedMsg, end) && + push16(mpdcch_dci_pdu_rel13->initial_transmission_sf_io, ppWritePackedMsg, end) && + push16(mpdcch_dci_pdu_rel13->transmission_power, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->dci_format, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->resource_block_start, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->number_of_resource_blocks, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->mcs, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->pusch_repetition_levels, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->frequency_hopping_flag, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->new_data_indication, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->harq_process, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->redudency_version, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->tpc, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->csi_request, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->ul_inex, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->dai_presence_flag, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->dl_assignment_index, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->srs_request, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->dci_subframe_repetition_number, ppWritePackedMsg, end) && + push32(mpdcch_dci_pdu_rel13->tcp_bitmap, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->total_dci_length_include_padding, ppWritePackedMsg, end) && + push8(mpdcch_dci_pdu_rel13->number_of_tx_antenna_ports, ppWritePackedMsg, end) && + pusharray16(mpdcch_dci_pdu_rel13->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, mpdcch_dci_pdu_rel13->number_of_tx_antenna_ports, ppWritePackedMsg, end)); +} + +static uint8_t pack_hi_dci0_npdcch_dci_rel13_pdu_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *npdcch_dci_pdu_rel13 = (nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *)tlv; + return ( push8(npdcch_dci_pdu_rel13->ncce_index, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->aggregation_level, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->start_symbol, ppWritePackedMsg, end) && + push16(npdcch_dci_pdu_rel13->rnti, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->scrambling_reinitialization_batch_index, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->nrs_antenna_ports_assumed_by_the_ue, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->subcarrier_indication, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->resource_assignment, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->scheduling_delay, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->mcs, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->redudancy_version, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->repetition_number, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->new_data_indicator, ppWritePackedMsg, end) && + push8(npdcch_dci_pdu_rel13->dci_subframe_repetition_number, ppWritePackedMsg, end)); +} + + +static uint8_t pack_hi_dci0_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_hi_dci0_request_body_t *value = (nfapi_hi_dci0_request_body_t *)tlv; + + if(!(push16(value->sfnsf, ppWritePackedMsg, end) && + push8(value->number_of_dci, ppWritePackedMsg, end) && + push8(value->number_of_hi, ppWritePackedMsg, end))) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_dci + value->number_of_hi; + + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_hi_dci0_request_pdu_t *pdu = &(value->hi_dci0_pdu_list[i]); + + if(push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) + return 0; + + // Put a 0 size in and then determine the size after the pdu + // has been writen and write the calculated size + uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; + pdu->pdu_size = 0; + + if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) + return 0; + + switch(pdu->pdu_type) { + case NFAPI_HI_DCI0_HI_PDU_TYPE: { + if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_HI_PDU_REL8_TAG, &pdu->hi_pdu.hi_pdu_rel8, ppWritePackedMsg, end, pack_hi_dci0_hi_rel8_pdu_value) && + pack_tlv(NFAPI_HI_DCI0_REQUEST_HI_PDU_REL10_TAG, &pdu->hi_pdu.hi_pdu_rel10, ppWritePackedMsg, end, pack_hi_dci0_hi_rel10_pdu_value))) + return 0; + } + break; + + case NFAPI_HI_DCI0_DCI_PDU_TYPE: { + if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL8_TAG, &pdu->dci_pdu.dci_pdu_rel8, ppWritePackedMsg, end, pack_hi_dci0_dci_rel8_pdu_value) && + pack_tlv(NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL10_TAG, &pdu->dci_pdu.dci_pdu_rel10, ppWritePackedMsg, end, pack_hi_dci0_dci_rel10_pdu_value) && + pack_tlv(NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL12_TAG, &pdu->dci_pdu.dci_pdu_rel12, ppWritePackedMsg, end, pack_hi_dci0_dci_rel12_pdu_value))) + return 0; + } + break; + + case NFAPI_HI_DCI0_EPDCCH_DCI_PDU_TYPE: { + if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL8_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel8, ppWritePackedMsg, end, pack_hi_dci0_dci_rel8_pdu_value) && + pack_tlv(NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL10_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel10, ppWritePackedMsg, end, pack_hi_dci0_dci_rel10_pdu_value) && + pack_tlv(NFAPI_HI_DCI0_REQUEST_EPDCCH_PARAMETERS_REL11_TAG, &pdu->epdcch_dci_pdu.epdcch_parameters_rel11, ppWritePackedMsg, end, pack_dl_config_epdcch_parameters_rel11_value))) + return 0; + } + break; + + case NFAPI_HI_DCI0_MPDCCH_DCI_PDU_TYPE: { + if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_MPDCCH_DCI_PDU_REL13_TAG, &pdu->mpdcch_dci_pdu.mpdcch_dci_pdu_rel13, ppWritePackedMsg, end, pack_hi_dci0_mpdcch_dci_rel13_pdu_value))) + return 0; + } + break; + + case NFAPI_HI_DCI0_NPDCCH_DCI_PDU_TYPE: { + if(!(pack_tlv(NFAPI_HI_DCI0_REQUEST_NPDCCH_DCI_PDU_REL13_TAG, &pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13, ppWritePackedMsg, end, pack_hi_dci0_npdcch_dci_rel13_pdu_value))) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); + } + break; + }; + + // add 1 for the pdu_type. The delta will include the pdu_size + pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); + + push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); + } + + return 1; +} + +static uint8_t pack_ul_dci_pdu_list_value(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) +{ + nfapi_nr_ul_dci_request_pdus_t* value = (nfapi_nr_ul_dci_request_pdus_t*)tlv; + + for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) + { + if(!(push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].RNTI, ppWritePackedMsg, end) && + push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingId, ppWritePackedMsg, end) && + + push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingRNTI, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].CceIndex, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].AggregationLevel, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].beta_PDCCH_1_0, ppWritePackedMsg, end) && + + push8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].powerControlOffsetSS, ppWritePackedMsg, end) && + push16(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end) && + + pusharray8(value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].Payload, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, ppWritePackedMsg, end))) + + return 0; + } + + return (push16(value->PDUType, ppWritePackedMsg, end) && + push16(value->PDUSize, ppWritePackedMsg, end) && + push16(value->pdcch_pdu.pdcch_pdu_rel15.BWPSize, ppWritePackedMsg, end) && + push16(value->pdcch_pdu.pdcch_pdu_rel15.BWPStart, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.SubcarrierSpacing, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.CyclicPrefix, ppWritePackedMsg, end) && + + push8(value->pdcch_pdu.pdcch_pdu_rel15.StartSymbolIndex, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.DurationSymbols, ppWritePackedMsg, end) && + pusharray8(value->pdcch_pdu.pdcch_pdu_rel15.FreqDomainResource, 6, 6, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.CceRegMappingType, ppWritePackedMsg, end) && + + push8(value->pdcch_pdu.pdcch_pdu_rel15.RegBundleSize, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.InterleaverSize, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.CoreSetType, ppWritePackedMsg, end) && + push16(value->pdcch_pdu.pdcch_pdu_rel15.ShiftIndex, ppWritePackedMsg, end) && + push8(value->pdcch_pdu.pdcch_pdu_rel15.precoderGranularity, ppWritePackedMsg, end) && + push16(value->pdcch_pdu.pdcch_pdu_rel15.numDlDci, ppWritePackedMsg, end)); + +} + +static uint8_t pack_ul_dci_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nr_ul_dci_request_t *pNfapiMsg = (nfapi_nr_ul_dci_request_t *)msg; + + if (!(push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && + push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && + push8(pNfapiMsg->numPdus, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; inumPdus; i++) { + if(!pack_ul_dci_pdu_list_value(&pNfapiMsg->ul_dci_pdu_list[i], ppWritePackedMsg, end)) + return 0; + } + + return 1; +} + + + +static uint8_t pack_hi_dci0_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_hi_dci0_request_t *pNfapiMsg = (nfapi_hi_dci0_request_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_HI_DCI0_REQUEST_BODY_TAG, &pNfapiMsg->hi_dci0_request_body, ppWritePackedMsg, end, &pack_hi_dci0_request_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +//pack_tx_data_pdu_list_value +static uint8_t pack_tx_data_pdu_list_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_pdu_t *value = (nfapi_nr_pdu_t *)tlv; + + if(!(push32(value->num_TLV, ppWritePackedMsg, end) && + push16(value->PDU_index, ppWritePackedMsg, end) && + push16(value->PDU_length, ppWritePackedMsg, end) + )) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_tlvs = value->num_TLV; + + for(; i < total_number_of_tlvs; ++i) { + if (!(push16(value->TLVs[i].length, ppWritePackedMsg, end) && + push16(value->TLVs[i].tag, ppWritePackedMsg, end))) + return 0; + + switch(value->TLVs[i].tag) { + case 0: { + if(!pusharray32(value->TLVs[i].value.direct, 16384, value->TLVs[i].length, ppWritePackedMsg, end)) + return 0; + + break; + } + + case 1: { + if(!pusharray32(value->TLVs[i].value.ptr, value->TLVs[i].length, value->TLVs[i].length, ppWritePackedMsg, end)) + return 0; + + break; + } + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid tag value %d \n", value->TLVs[i].tag ); + break; + } + } + } + + return 1; +} + +static uint8_t pack_tx_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_tx_request_body_t *value = (nfapi_tx_request_body_t *)tlv; + + if(push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_pdus; + + for(; i < total_number_of_pdus; ++i) { + nfapi_tx_request_pdu_t *pdu = &(value->tx_pdu_list[i]); + + if(!(push16(pdu->pdu_length, ppWritePackedMsg, end) && + push16(pdu->pdu_index, ppWritePackedMsg, end))) + return 0; + + uint8_t j; + + for(j = 0; j < pdu->num_segments; ++j) { + // Use -1 as it is unbounded + // DJP - does not handle -1 + // DJP - if(pusharray8(pdu->segments[j].segment_data, (uint32_t)(-1), pdu->segments[j].segment_length, ppWritePackedMsg, end) == 0) + int push_ret = pusharray8(pdu->segments[j].segment_data, 65535, pdu->segments[j].segment_length, ppWritePackedMsg, end); + + if (pdu->segments[j].segment_length == 3) { + NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() BCH? segment_data:%x %x %x\n", __FUNCTION__, + pdu->segments[j].segment_data[0], + pdu->segments[j].segment_data[1], + pdu->segments[j].segment_data[2] + ); + } + + //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() segment_data:%p segment_length:%u pusharray8()=%d\n", __FUNCTION__, pdu->segments[j].segment_data, pdu->segments[j].segment_length, push_ret); + + if (push_ret == 0) { + return 0; + } + } + } + + return 1; +} + +static uint8_t pack_tx_data_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nr_tx_data_request_t *pNfapiMsg = (nfapi_nr_tx_data_request_t *)msg; + + if (!( + push16(pNfapiMsg->SFN, ppWritePackedMsg, end) && + push16(pNfapiMsg->Slot, ppWritePackedMsg, end) && + push16(pNfapiMsg->Number_of_PDUs, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; iNumber_of_PDUs; i++) { + if(!pack_tx_data_pdu_list_value(&pNfapiMsg->pdu_list[i], ppWritePackedMsg, end)) + return 0; + } + + return 1; +} + +static uint8_t pack_tx_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_tx_request_t *pNfapiMsg = (nfapi_tx_request_t *)msg; + int x = push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end); + int y = pack_tlv(NFAPI_TX_REQUEST_BODY_TAG, &pNfapiMsg->tx_request_body, ppWritePackedMsg, end, &pack_tx_request_body_value); + int z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); + //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() x:%d y:%d z:%d\n", __FUNCTION__, x, y, z); + return x && y && z; +} + +static uint8_t pack_release_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ue_release_request_body_t *value = (nfapi_ue_release_request_body_t *)tlv; + + if(push16(value->number_of_TLVs, ppWritePackedMsg, end) == 0) { + return 0; + } + + uint8_t j; + uint16_t num = value->number_of_TLVs; + + for(j = 0; j < num; ++j) { + if(push16(value->ue_release_request_TLVs_list[j].rnti, ppWritePackedMsg, end) == 0) { + return 0; + } + } + + return 1; +} + +static uint8_t pack_ue_release_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_ue_release_request_t *pNfapiMsg = (nfapi_ue_release_request_t *)msg; + int x = push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end); + int y = pack_tlv(NFAPI_UE_RELEASE_BODY_TAG, &pNfapiMsg->ue_release_request_body, ppWritePackedMsg, end, &pack_release_request_body_value); + int z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); + return x && y && z; +} + +static uint8_t pack_ue_release_response(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_ue_release_response_t *pNfapiMsg = (nfapi_ue_release_response_t *)msg; + int x = push32(pNfapiMsg->error_code, ppWritePackedMsg, end); + int z = pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config); + return x && z; +} + +static uint8_t pack_rx_ue_information_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_rx_ue_information *value = (nfapi_rx_ue_information *)tlv; + return ( push32(value->handle, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) ); +} + +static uint8_t unpack_rx_ue_information_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_rx_ue_information *value = (nfapi_rx_ue_information *)tlv; + return ( pull32(ppReadPackedMsg, &value->handle, end) && + pull16(ppReadPackedMsg, &value->rnti, end)); +} + +static uint8_t pack_harq_indication_tdd_harq_data_bundling(nfapi_harq_indication_tdd_harq_data_bundling_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( push8(data->value_0, ppWritePackedMsg, end) && + push8(data->value_1, ppWritePackedMsg, end)); +} + +static uint8_t pack_harq_indication_tdd_harq_data_multiplexing(nfapi_harq_indication_tdd_harq_data_multiplexing_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( push8(data->value_0, ppWritePackedMsg, end) && + push8(data->value_1, ppWritePackedMsg, end) && + push8(data->value_2, ppWritePackedMsg, end) && + push8(data->value_3, ppWritePackedMsg, end)); +} + +static uint8_t pack_harq_indication_tdd_harq_data_special_bundling(nfapi_harq_indication_tdd_harq_data_special_bundling_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( push8(data->value_0, ppWritePackedMsg, end) ); +} + +static uint8_t pack_harq_indication_tdd_harq_data(nfapi_harq_indication_tdd_harq_data_t *data, uint8_t **ppWritePackedMsg, uint8_t *end) { + return ( push8(data->value_0, ppWritePackedMsg, end) ); +} + +static uint8_t pack_harq_indication_tdd_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_rel8_t *harq_indication_tdd_rel8 = (nfapi_harq_indication_tdd_rel8_t *)tlv; + + if(!(push8(harq_indication_tdd_rel8->mode, ppWritePackedMsg, end) && + push8(harq_indication_tdd_rel8->number_of_ack_nack, ppWritePackedMsg, end))) + return 0; + + uint8_t result = 0; + + switch(harq_indication_tdd_rel8->mode) { + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: + result = pack_harq_indication_tdd_harq_data_bundling(&harq_indication_tdd_rel8->harq_data.bundling, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: + result = pack_harq_indication_tdd_harq_data_multiplexing(&harq_indication_tdd_rel8->harq_data.multiplex, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: + result = pack_harq_indication_tdd_harq_data_special_bundling(&harq_indication_tdd_rel8->harq_data.special_bundling, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: + result = 1; + break; + + default: + // err.... + break; + } + + return result; +} + +static uint8_t pack_harq_indication_tdd_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_rel9_t *harq_indication_tdd_rel9 = (nfapi_harq_indication_tdd_rel9_t *)tlv; + + if(!(push8(harq_indication_tdd_rel9->mode, ppWritePackedMsg, end) && + push8(harq_indication_tdd_rel9->number_of_ack_nack, ppWritePackedMsg, end))) + return 0; + + uint8_t idx; + + for(idx = 0; idx < harq_indication_tdd_rel9->number_of_ack_nack; ++idx) { + uint8_t result = 0; + + switch(harq_indication_tdd_rel9->mode) { + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: + result = pack_harq_indication_tdd_harq_data(&(harq_indication_tdd_rel9->harq_data[idx].bundling), ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel9->harq_data[idx].multiplex, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: + result = pack_harq_indication_tdd_harq_data_special_bundling(&harq_indication_tdd_rel9->harq_data[idx].special_bundling, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel9->harq_data[idx].channel_selection, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel9->harq_data[idx].format_3, ppWritePackedMsg, end); + break; + + default: + // err.... + break; + } + + if(result == 0) + return 0; + } + + return 1; +} + +static uint8_t pack_harq_indication_tdd_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_rel13_t *harq_indication_tdd_rel13 = (nfapi_harq_indication_tdd_rel13_t *)tlv; + + if(!(push8(harq_indication_tdd_rel13->mode, ppWritePackedMsg, end) && + push16(harq_indication_tdd_rel13->number_of_ack_nack, ppWritePackedMsg, end))) + return 0; + + uint8_t idx; + + for(idx = 0; idx < harq_indication_tdd_rel13->number_of_ack_nack; ++idx) { + uint8_t result = 0; + + switch(harq_indication_tdd_rel13->mode) { + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].bundling, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].multiplex, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: + result = pack_harq_indication_tdd_harq_data_special_bundling(&harq_indication_tdd_rel13->harq_data[idx].special_bundling, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].channel_selection, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].format_3, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_4: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].format_4, ppWritePackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_5: + result = pack_harq_indication_tdd_harq_data(&harq_indication_tdd_rel13->harq_data[idx].format_5, ppWritePackedMsg, end); + break; + + default: + // err.... + break; + } + + if(result == 0) + return 0; + } + + return 1; +} + +static uint8_t pack_harq_indication_fdd_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_harq_indication_fdd_rel8_t *harq_indication_fdd_rel8 = (nfapi_harq_indication_fdd_rel8_t *)tlv; + return ( push8(harq_indication_fdd_rel8->harq_tb1, ppWritePackedMsg, end) && + push8(harq_indication_fdd_rel8->harq_tb2, ppWritePackedMsg, end)); +} + +static uint8_t pack_harq_indication_fdd_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_harq_indication_fdd_rel9_t *harq_indication_fdd_rel9 = (nfapi_harq_indication_fdd_rel9_t *)tlv; + return ( push8(harq_indication_fdd_rel9->mode, ppWritePackedMsg, end) && + push8(harq_indication_fdd_rel9->number_of_ack_nack, ppWritePackedMsg, end) && + pusharray8(harq_indication_fdd_rel9->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL9_MAX, harq_indication_fdd_rel9->number_of_ack_nack, ppWritePackedMsg, end)); +} + +static uint8_t pack_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_harq_indication_fdd_rel13_t *harq_indication_fdd_rel13 = (nfapi_harq_indication_fdd_rel13_t *)tlv; + return ( push8(harq_indication_fdd_rel13->mode, ppWritePackedMsg, end) && + push16(harq_indication_fdd_rel13->number_of_ack_nack, ppWritePackedMsg, end) && + pusharray8(harq_indication_fdd_rel13->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL13_MAX, harq_indication_fdd_rel13->number_of_ack_nack, ppWritePackedMsg, end)); +} + +static uint8_t pack_ul_cqi_information_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_ul_cqi_information_t *value = (nfapi_ul_cqi_information_t *)tlv; + return ( push8(value->ul_cqi, ppWritePackedMsg, end) && + push8(value->channel, ppWritePackedMsg, end)); +} + +static uint8_t pack_harq_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_harq_indication_body_t *value = (nfapi_harq_indication_body_t *)tlv; + + if(push16(value->number_of_harqs, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_harqs; + + for(; i < total_number_of_pdus; ++i) { + nfapi_harq_indication_pdu_t *pdu = &(value->harq_pdu_list[i]); + uint8_t *instance_length_p = *ppWritePackedMsg; + + if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + return 0; + + if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && + pack_tlv(NFAPI_HARQ_INDICATION_TDD_REL8_TAG, &pdu->harq_indication_tdd_rel8, ppWritePackedMsg, end, pack_harq_indication_tdd_rel8_value) && + pack_tlv(NFAPI_HARQ_INDICATION_TDD_REL9_TAG, &pdu->harq_indication_tdd_rel9, ppWritePackedMsg, end, pack_harq_indication_tdd_rel9_value) && + pack_tlv(NFAPI_HARQ_INDICATION_TDD_REL13_TAG, &pdu->harq_indication_tdd_rel13, ppWritePackedMsg, end, pack_harq_indication_tdd_rel13_value) && + pack_tlv(NFAPI_HARQ_INDICATION_FDD_REL8_TAG, &pdu->harq_indication_fdd_rel8, ppWritePackedMsg, end, pack_harq_indication_fdd_rel8_value) && + pack_tlv(NFAPI_HARQ_INDICATION_FDD_REL9_TAG, &pdu->harq_indication_fdd_rel9, ppWritePackedMsg, end, pack_harq_indication_fdd_rel9_value) && + pack_tlv(NFAPI_HARQ_INDICATION_FDD_REL13_TAG, &pdu->harq_indication_fdd_rel13, ppWritePackedMsg, end, pack_harq_indication_fdd_rel13_value) && + pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + push16(instance_length, &instance_length_p, end); + } + + return 1; +} + +static uint8_t pack_harq_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_harq_indication_t *pNfapiMsg = (nfapi_harq_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->harq_indication_body, ppWritePackedMsg, end, pack_harq_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_crc_indication_rel8_body(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_crc_indication_rel8_t *crc_indication_rel8 = (nfapi_crc_indication_rel8_t *)tlv; + return ( push8(crc_indication_rel8->crc_flag, ppWritePackedMsg, end) ); +} + +static uint8_t pack_crc_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_crc_indication_body_t *value = (nfapi_crc_indication_body_t *)tlv; + + if(push16(value->number_of_crcs, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_crcs; + + for(; i < total_number_of_pdus; ++i) { + nfapi_crc_indication_pdu_t *pdu = &(value->crc_pdu_list[i]); + uint8_t *instance_length_p = *ppWritePackedMsg; + + if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + return 0; + + if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && + pack_tlv(NFAPI_CRC_INDICATION_REL8_TAG, &pdu->crc_indication_rel8, ppWritePackedMsg, end, pack_crc_indication_rel8_body))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + push16(instance_length, &instance_length_p, end); + } + + return 1; +} + +static uint8_t pack_crc_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_crc_indication_t *pNfapiMsg = (nfapi_crc_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_CRC_INDICATION_BODY_TAG, &pNfapiMsg->crc_indication_body, ppWritePackedMsg, end, &pack_crc_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} +static uint8_t pack_rx_indication_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_rx_indication_rel8_t *value = (nfapi_rx_indication_rel8_t *)tlv; + return ( push16(value->length, ppWritePackedMsg, end) && + push16(value->offset, ppWritePackedMsg, end) && + push8(value->ul_cqi, ppWritePackedMsg, end) && + push16(value->timing_advance, ppWritePackedMsg, end)); +} +static uint8_t pack_rx_indication_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_rx_indication_rel9_t *value = (nfapi_rx_indication_rel9_t *)tlv; + return ( push16(value->timing_advance_r9, ppWritePackedMsg, end)); +} + +static uint8_t pack_rx_ulsch_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_rx_indication_body_t *value = (nfapi_rx_indication_body_t *)tlv; + + //printf("RX ULSCH BODY\n"); + + if( push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) + return 0; + + // need to calculate the data offset's. + uint16_t i = 0; + uint16_t offset = 2; // taking into account the number_of_pdus + uint16_t total_number_of_pdus = value->number_of_pdus; + //printf("ULSCH:pdus:%d\n", total_number_of_pdus); + + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); + + if(pdu->rx_ue_information.tl.tag == NFAPI_RX_UE_INFORMATION_TAG) { + //printf("NFAPI_RX_UE_INFORMATION_TAG\n"); + offset += 4 + 6; + } + + if(pdu->rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { + //printf("NFAPI_RX_INDICATION_REL8_TAG\n"); + offset += 4 + 7; + } + + if(pdu->rx_indication_rel9.tl.tag == NFAPI_RX_INDICATION_REL9_TAG) { + //printf("NFAPI_RX_INDICATION_REL9_TAG\n"); + offset += 4 + 2; + } + } + + // Now update the structure to include the offset + for(i =0; i < total_number_of_pdus; ++i) { + nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); + + if(pdu->rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { + if(pdu->rx_indication_rel8.offset == 1) { + pdu->rx_indication_rel8.offset = offset; + offset += pdu->rx_indication_rel8.length; + } + } + } + + // Write out the pdu + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); + + if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && + pack_tlv(NFAPI_RX_INDICATION_REL8_TAG, &pdu->rx_indication_rel8, ppWritePackedMsg, end, pack_rx_indication_rel8_value) && + pack_tlv(NFAPI_RX_INDICATION_REL9_TAG, &pdu->rx_indication_rel9, ppWritePackedMsg, end, pack_rx_indication_rel9_value))) + return 0; + } + + // Write out the pdu data + for(i = 0; i < total_number_of_pdus; ++i) { + uint16_t length = 0; + nfapi_rx_indication_pdu_t *pdu = &(value->rx_pdu_list[i]); + + if(pdu->rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { + length = pdu->rx_indication_rel8.length; + } + + if( pusharray8(value->rx_pdu_list[i].data, length, length, ppWritePackedMsg, end) == 0) + return 0; + } + + return 1; +} + + +static uint8_t pack_rx_ulsch_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_rx_indication_t *pNfapiMsg = (nfapi_rx_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_RX_INDICATION_BODY_TAG, &pNfapiMsg->rx_indication_body, ppWritePackedMsg, end, pack_rx_ulsch_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_preamble_pdu_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_preamble_pdu_rel8_t *preamble_rel8 = (nfapi_preamble_pdu_rel8_t *)tlv; + return ( push16(preamble_rel8->rnti, ppWritePackedMsg, end) && + push8(preamble_rel8->preamble, ppWritePackedMsg, end) && + push16(preamble_rel8->timing_advance, ppWritePackedMsg, end)); +} +static uint8_t pack_preamble_pdu_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_preamble_pdu_rel9_t *preamble_rel9 = (nfapi_preamble_pdu_rel9_t *)tlv; + return ( push16(preamble_rel9->timing_advance_r9, ppWritePackedMsg, end) ); +} +static uint8_t pack_preamble_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_preamble_pdu_rel13_t *preamble_rel13 = (nfapi_preamble_pdu_rel13_t *)tlv; + return ( push8(preamble_rel13->rach_resource_type, ppWritePackedMsg, end) ); +} + +static uint8_t pack_rach_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_rach_indication_body_t *value = (nfapi_rach_indication_body_t *)tlv; + + if( push16(value->number_of_preambles, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_preambles; + + for(; i < total_number_of_pdus; ++i) { + nfapi_preamble_pdu_t *pdu = &(value->preamble_list[i]); + uint8_t *instance_length_p = *ppWritePackedMsg; + + if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + return 0; + + if(!(pack_tlv(NFAPI_PREAMBLE_REL8_TAG, &pdu->preamble_rel8, ppWritePackedMsg, end, pack_preamble_pdu_rel8_value) && + pack_tlv(NFAPI_PREAMBLE_REL9_TAG, &pdu->preamble_rel9, ppWritePackedMsg, end, pack_preamble_pdu_rel9_value) && + pack_tlv(NFAPI_PREAMBLE_REL13_TAG, &pdu->preamble_rel13, ppWritePackedMsg, end, pack_preamble_pdu_rel13_value))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + push16(instance_length, &instance_length_p, end); + } + + return 1; +} + +static uint8_t pack_rach_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_rach_indication_t *pNfapiMsg = (nfapi_rach_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_RACH_INDICATION_BODY_TAG, &pNfapiMsg->rach_indication_body, ppWritePackedMsg, end, pack_rach_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_srs_indication_fdd_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_srs_indication_fdd_rel8_t *srs_pdu_rel8 = (nfapi_srs_indication_fdd_rel8_t *)tlv; + return ( push16(srs_pdu_rel8->doppler_estimation, ppWritePackedMsg, end) && + push16(srs_pdu_rel8->timing_advance, ppWritePackedMsg, end) && + push8(srs_pdu_rel8->number_of_resource_blocks, ppWritePackedMsg, end) && + push8(srs_pdu_rel8->rb_start, ppWritePackedMsg, end) && + pusharray8(srs_pdu_rel8->snr, NFAPI_NUM_RB_MAX, srs_pdu_rel8->number_of_resource_blocks, ppWritePackedMsg, end)); +} + +static uint8_t pack_srs_indication_fdd_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_srs_indication_fdd_rel9_t *srs_pdu_rel9 = (nfapi_srs_indication_fdd_rel9_t *)tlv; + return ( push16(srs_pdu_rel9->timing_advance_r9, ppWritePackedMsg, end) ); +} + +static uint8_t pack_srs_indication_tdd_rel10_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_srs_indication_ttd_rel10_t *srs_pdu_rel10 = (nfapi_srs_indication_ttd_rel10_t *)tlv; + return ( push8(srs_pdu_rel10->uppts_symbol, ppWritePackedMsg, end) ); +} + +static uint8_t pack_srs_indication_fdd_rel11_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_srs_indication_fdd_rel11_t *srs_pdu_rel11 = (nfapi_srs_indication_fdd_rel11_t *)tlv; + return ( push16(srs_pdu_rel11->ul_rtoa, ppWritePackedMsg, end) ) ; +} + +static uint8_t pack_tdd_channel_measurement_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_tdd_channel_measurement_t *value = (nfapi_tdd_channel_measurement_t *)tlv; + + if(!(push8(value->num_prb_per_subband, ppWritePackedMsg, end) && + push8(value->number_of_subbands, ppWritePackedMsg, end) && + push8(value->num_atennas, ppWritePackedMsg, end))) + return 0; + + uint8_t idx = 0; + + for(idx = 0; idx < value->number_of_subbands; ++idx) { + if(!(push8(value->subands[idx].subband_index, ppWritePackedMsg, end) && + pusharray16(value->subands[idx].channel, NFAPI_MAX_NUM_PHYSICAL_ANTENNAS, value->num_atennas, ppWritePackedMsg, end))) + return 0; + } + + return 1; +} + +static uint8_t pack_srs_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_srs_indication_body_t *value = (nfapi_srs_indication_body_t *)tlv; + + if( push8(value->number_of_ues, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_ues; + + for(; i < total_number_of_pdus; ++i) { + nfapi_srs_indication_pdu_t *pdu = &(value->srs_pdu_list[i]); + uint8_t *instance_length_p = *ppWritePackedMsg; + + if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + return 0; + + if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, &pack_rx_ue_information_value) && + pack_tlv(NFAPI_SRS_INDICATION_FDD_REL8_TAG, &pdu->srs_indication_fdd_rel8, ppWritePackedMsg, end, &pack_srs_indication_fdd_rel8_value) && + pack_tlv(NFAPI_SRS_INDICATION_FDD_REL9_TAG, &pdu->srs_indication_fdd_rel9, ppWritePackedMsg, end, &pack_srs_indication_fdd_rel9_value) && + pack_tlv(NFAPI_SRS_INDICATION_TDD_REL10_TAG, &pdu->srs_indication_tdd_rel10, ppWritePackedMsg, end, &pack_srs_indication_tdd_rel10_value) && + pack_tlv(NFAPI_SRS_INDICATION_FDD_REL11_TAG, &pdu->srs_indication_fdd_rel11, ppWritePackedMsg, end, &pack_srs_indication_fdd_rel11_value) && + pack_tlv(NFAPI_TDD_CHANNEL_MEASUREMENT_TAG, &pdu->tdd_channel_measurement, ppWritePackedMsg, end, &pack_tdd_channel_measurement_value))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + push16(instance_length, &instance_length_p, end); + } + + return 1; +} + +static uint8_t pack_srs_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_srs_indication_t *pNfapiMsg = (nfapi_srs_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_SRS_INDICATION_BODY_TAG, &pNfapiMsg->srs_indication_body, ppWritePackedMsg, end, &pack_srs_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_sr_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_sr_indication_body_t *value = (nfapi_sr_indication_body_t *)tlv; + + if(push16(value->number_of_srs, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_srs; + + for(; i < total_number_of_pdus; ++i) { + nfapi_sr_indication_pdu_t *pdu = &(value->sr_pdu_list[i]); + uint8_t *instance_length_p = *ppWritePackedMsg; + + if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + return 0; + + if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && + pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + push16(instance_length, &instance_length_p, end); + } + + return 1; +} + +static uint8_t pack_sr_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_sr_indication_t *pNfapiMsg = (nfapi_sr_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_SR_INDICATION_BODY_TAG, &pNfapiMsg->sr_indication_body, ppWritePackedMsg, end, &pack_sr_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_cqi_indication_rel8_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_cqi_indication_rel8_t *cqi_pdu_rel8 = (nfapi_cqi_indication_rel8_t *)tlv; + return ( push16(cqi_pdu_rel8->length, ppWritePackedMsg, end) && + push16(cqi_pdu_rel8->data_offset, ppWritePackedMsg, end) && + push8(cqi_pdu_rel8->ul_cqi, ppWritePackedMsg, end) && + push8(cqi_pdu_rel8->ri, ppWritePackedMsg, end) && + push16(cqi_pdu_rel8->timing_advance, ppWritePackedMsg, end)); +} + +static uint8_t pack_cqi_indication_rel9_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_cqi_indication_rel9_t *cqi_pdu_rel9 = (nfapi_cqi_indication_rel9_t *)tlv; + return ( push16(cqi_pdu_rel9->length, ppWritePackedMsg, end) && + push16(cqi_pdu_rel9->data_offset, ppWritePackedMsg, end) && + push8(cqi_pdu_rel9->ul_cqi, ppWritePackedMsg, end) && + push8(cqi_pdu_rel9->number_of_cc_reported, ppWritePackedMsg, end) && + pusharray8(cqi_pdu_rel9->ri, NFAPI_CC_MAX, cqi_pdu_rel9->number_of_cc_reported, ppWritePackedMsg, end) && + push16(cqi_pdu_rel9->timing_advance, ppWritePackedMsg, end) && + push16(cqi_pdu_rel9->timing_advance_r9, ppWritePackedMsg, end)); +} + +static uint8_t pack_cqi_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_cqi_indication_body_t *value = (nfapi_cqi_indication_body_t *)tlv; + + if( push16(value->number_of_cqis, ppWritePackedMsg, end) == 0) + return 0; + + // need to calculate the data offset's. This very bittle due the hardcoding + // of the sizes. can not use the sizeof as we have an array for the Rel9 + // info + uint16_t i = 0; + uint16_t offset = 2; // taking into account the number_of_cqis + uint16_t total_number_of_pdus = value->number_of_cqis; + + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); + offset += 2; // for the instance length + + if(pdu->rx_ue_information.tl.tag == NFAPI_RX_UE_INFORMATION_TAG) { + offset += 4 + 6; // sizeof(nfapi_rx_ue_information) - sizeof(nfapi_tl_t) + } + + if(pdu->cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { + offset += 4 + 8; + } + + if(pdu->cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { + offset += 4 + 10 + pdu->cqi_indication_rel9.number_of_cc_reported; + } + + if(pdu->ul_cqi_information.tl.tag == NFAPI_UL_CQI_INFORMATION_TAG) { + offset += 4 + 2; + } + } + + // Now update the structure to include the offset + for(i =0; i < total_number_of_pdus; ++i) { + nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); + + if(pdu->cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { + if(pdu->cqi_indication_rel8.data_offset == 1) { + pdu->cqi_indication_rel8.data_offset = offset; + offset += pdu->cqi_indication_rel8.length; + } + } + + if(pdu->cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { + if(pdu->cqi_indication_rel9.data_offset == 1) { + pdu->cqi_indication_rel9.data_offset = offset; + offset += pdu->cqi_indication_rel9.length; + } + } + } + + // Write out the cqi information + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); + uint8_t *instance_length_p = *ppWritePackedMsg; + + if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + return 0; + + if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end,pack_rx_ue_information_value) && + pack_tlv(NFAPI_CQI_INDICATION_REL8_TAG, &pdu->cqi_indication_rel8, ppWritePackedMsg, end, pack_cqi_indication_rel8_value) && + pack_tlv(NFAPI_CQI_INDICATION_REL9_TAG, &pdu->cqi_indication_rel9, ppWritePackedMsg, end, pack_cqi_indication_rel9_value) && + pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + push16(instance_length, &instance_length_p, end); + } + + // Write out the cqi raw data + for(i = 0; i < total_number_of_pdus; ++i) { + uint16_t length = 0; + nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); + + if(pdu->cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { + length = pdu->cqi_indication_rel8.length; + } + + if(pdu->cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { + length = pdu->cqi_indication_rel9.length; + } + + if( pusharray8(value->cqi_raw_pdu_list[i].pdu, NFAPI_CQI_RAW_MAX_LEN, length, ppWritePackedMsg, end) == 0) + return 0; + } + + return 1; +} + +static uint8_t pack_cqi_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_cqi_indication_t *pNfapiMsg = (nfapi_cqi_indication_t *)msg; + //Fixme: allocate some mem to fix pure bug, need to find out proper size + pNfapiMsg->vendor_extension=NULL;//(nfapi_vendor_extension_tlv_t)malloc( sizeof(* pNfapiMsg->vendor_extension)); + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_CQI_INDICATION_BODY_TAG, &pNfapiMsg->cqi_indication_body, ppWritePackedMsg, end, pack_cqi_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_lbt_pdsch_req_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_lbt_pdsch_req_pdu_rel13_t *value = (nfapi_lbt_pdsch_req_pdu_rel13_t *)tlv; + return ( push32(value->handle, ppWritePackedMsg, end) && + push32(value->mp_cca, ppWritePackedMsg, end) && + push32(value->n_cca, ppWritePackedMsg, end) && + push32(value->offset, ppWritePackedMsg, end) && + push32(value->lte_txop_sf, ppWritePackedMsg, end) && + push16(value->txop_sfn_sf_end, ppWritePackedMsg, end) && + push32(value->lbt_mode, ppWritePackedMsg, end)); +} + +static uint8_t pack_lbt_drs_req_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_lbt_drs_req_pdu_rel13_t *value = (nfapi_lbt_drs_req_pdu_rel13_t *)tlv; + return ( push32(value->handle, ppWritePackedMsg, end) && + push32(value->offset, ppWritePackedMsg, end) && + push16(value->sfn_sf_end, ppWritePackedMsg, end) && + push32(value->lbt_mode, ppWritePackedMsg, end)); +} + +static uint8_t pack_lbt_dl_config_request_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_lbt_dl_config_request_body_t *value = (nfapi_lbt_dl_config_request_body_t *)tlv; + + if( push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_pdus; + + for(; i < total_number_of_pdus; ++i) { + nfapi_lbt_dl_config_request_pdu_t *pdu = &(value->lbt_dl_config_req_pdu_list[i]); + + if( push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) + return 0; + + // Put a 0 size in and then determine the size after the pdu + // has been writen and write the calculated size + uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; + pdu->pdu_size = 0; + + if( push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) + return 0; + + switch(pdu->pdu_type) { + case NFAPI_LBT_DL_CONFIG_REQUEST_PDSCH_PDU_TYPE: { + if( pack_tlv(NFAPI_LBT_PDSCH_REQ_PDU_REL13_TAG, &pdu->lbt_pdsch_req_pdu.lbt_pdsch_req_pdu_rel13, ppWritePackedMsg, end, pack_lbt_pdsch_req_pdu_rel13_value) == 0) + return 0; + } + break; + + case NFAPI_LBT_DL_CONFIG_REQUEST_DRS_PDU_TYPE: { + if(pack_tlv(NFAPI_LBT_DRS_REQ_PDU_REL13_TAG, &pdu->lbt_drs_req_pdu.lbt_drs_req_pdu_rel13, ppWritePackedMsg, end, pack_lbt_drs_req_pdu_rel13_value) == 0) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL_CONFIG.request invalid pdu type %d \n", pdu->pdu_type ); + } + break; + }; + + // add 1 for the pdu_type. The delta will include the pdu_size + pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); + + push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); + } + + return 1; +} + +static uint8_t pack_lbt_pdsch_rsp_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_lbt_pdsch_rsp_pdu_rel13_t *value = (nfapi_lbt_pdsch_rsp_pdu_rel13_t *)tlv; + return ( push32(value->handle, ppWritePackedMsg, end) && + push32(value->result, ppWritePackedMsg, end) && + push32(value->lte_txop_symbols, ppWritePackedMsg, end) && + push32(value->initial_partial_sf, ppWritePackedMsg, end)); +} + +static uint8_t pack_lbt_drs_rsp_pdu_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_lbt_drs_rsp_pdu_rel13_t *value = (nfapi_lbt_drs_rsp_pdu_rel13_t *)tlv; + return ( push32(value->handle, ppWritePackedMsg, end) && + push32(value->result, ppWritePackedMsg, end)); +} + +static uint8_t pack_lbt_dl_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_lbt_dl_config_request_t *pNfapiMsg = (nfapi_lbt_dl_config_request_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_LBT_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->lbt_dl_config_request_body, ppWritePackedMsg, end, &pack_lbt_dl_config_request_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_lbt_dl_config_indication_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_lbt_dl_indication_body_t *value = (nfapi_lbt_dl_indication_body_t *)tlv; + + if( push16(value->number_of_pdus, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_pdus = value->number_of_pdus; + + for(; i < total_number_of_pdus; ++i) { + nfapi_lbt_dl_indication_pdu_t *pdu = &(value->lbt_indication_pdu_list[i]); + + if( push8(pdu->pdu_type, ppWritePackedMsg, end) == 0) + return 0; + + // Put a 0 size in and then determine the size after the pdu + // has been writen and write the calculated size + uint8_t *pWritePackedMsgPduSize = *ppWritePackedMsg; + pdu->pdu_size = 0; + + if(push8(pdu->pdu_size, ppWritePackedMsg, end) == 0) + return 0; + + switch(pdu->pdu_type) { + case NFAPI_LBT_DL_RSP_PDSCH_PDU_TYPE: { + if( pack_tlv(NFAPI_LBT_PDSCH_RSP_PDU_REL13_TAG, &pdu->lbt_pdsch_rsp_pdu.lbt_pdsch_rsp_pdu_rel13, ppWritePackedMsg, end, pack_lbt_pdsch_rsp_pdu_rel13_value) == 0) + return 0; + } + break; + + case NFAPI_LBT_DL_RSP_DRS_PDU_TYPE: { + if( pack_tlv(NFAPI_LBT_DRS_RSP_PDU_REL13_TAG, &pdu->lbt_drs_rsp_pdu.lbt_drs_rsp_pdu_rel13, ppWritePackedMsg, end, pack_lbt_drs_rsp_pdu_rel13_value) == 0) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL.indication body invalid pdu type %d \n", pdu->pdu_type ); + } + break; + }; + + // add 1 for the pdu_type. The delta will include the pdu_size + pdu->pdu_size = 1 + (*ppWritePackedMsg - pWritePackedMsgPduSize); + + push8(pdu->pdu_size, &pWritePackedMsgPduSize, end); + } + + return 1; +} + +static uint8_t pack_lbt_dl_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_lbt_dl_indication_t *pNfapiMsg = (nfapi_lbt_dl_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_LBT_DL_INDICATION_BODY_TAG, &pNfapiMsg->lbt_dl_indication_body, ppWritePackedMsg, end, &pack_lbt_dl_config_indication_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nb_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nb_harq_indication_fdd_rel13_t *nb_harq_indication_fdd_rel13 = (nfapi_nb_harq_indication_fdd_rel13_t *)tlv; + return ( push8(nb_harq_indication_fdd_rel13->harq_tb1, ppWritePackedMsg, end) ); +} + +static uint8_t pack_nb_harq_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nb_harq_indication_body_t *value = (nfapi_nb_harq_indication_body_t *)tlv; + + if( push16(value->number_of_harqs, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_harqs = value->number_of_harqs; + + for(; i < total_number_of_harqs; ++i) { + nfapi_nb_harq_indication_pdu_t *pdu = &(value->nb_harq_pdu_list[i]); + uint8_t *instance_length_p = *ppWritePackedMsg; + + if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + return 0; + + if(!(pack_tlv(NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, ppWritePackedMsg, end, pack_rx_ue_information_value) && + pack_tlv(NFAPI_NB_HARQ_INDICATION_FDD_REL13_TAG, &pdu->nb_harq_indication_fdd_rel13, ppWritePackedMsg, end, pack_nb_harq_indication_fdd_rel13_value) && + pack_tlv(NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, ppWritePackedMsg, end, pack_ul_cqi_information_value))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + push16(instance_length, &instance_length_p, end); + } + + return 1; +} + + +static uint8_t pack_nb_harq_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nb_harq_indication_t *pNfapiMsg = (nfapi_nb_harq_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_NB_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->nb_harq_indication_body, ppWritePackedMsg, end, &pack_nb_harq_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nrach_indication_rel13_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nrach_indication_pdu_rel13_t *nrach_indication_fdd_rel13 = (nfapi_nrach_indication_pdu_rel13_t *)tlv; + return ( push16(nrach_indication_fdd_rel13->rnti, ppWritePackedMsg, end) && + push8(nrach_indication_fdd_rel13->initial_sc, ppWritePackedMsg, end) && + push16(nrach_indication_fdd_rel13->timing_advance, ppWritePackedMsg, end) && + push8(nrach_indication_fdd_rel13->nrach_ce_level, ppWritePackedMsg, end)); +} + + +static uint8_t pack_nrach_indication_body_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nrach_indication_body_t *value = (nfapi_nrach_indication_body_t *)tlv; + + if( push8(value->number_of_initial_scs_detected, ppWritePackedMsg, end) == 0) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_initial_scs_detected = value->number_of_initial_scs_detected; + + for(; i < total_number_of_initial_scs_detected; ++i) { + nfapi_nrach_indication_pdu_t *pdu = &(value->nrach_pdu_list[i]); + + //uint8_t* instance_length_p = *ppWritePackedMsg; + //if(!push16(pdu->instance_length, ppWritePackedMsg, end)) + // return 0; + + if(!(pack_tlv(NFAPI_NRACH_INDICATION_REL13_TAG, &pdu->nrach_indication_rel13, ppWritePackedMsg, end, pack_nrach_indication_rel13_value))) + return 0; + + // calculate the instance length subtracting the size of the instance + // length feild + //uint16_t instance_length = *ppWritePackedMsg - instance_length_p - 2; + //push16(instance_length, &instance_length_p, end); + } + + return 1; +} + +static uint8_t pack_nrach_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nrach_indication_t *pNfapiMsg = (nfapi_nrach_indication_t *)msg; + return ( push16(pNfapiMsg->sfn_sf, ppWritePackedMsg, end) && + pack_tlv(NFAPI_NRACH_INDICATION_BODY_TAG, &pNfapiMsg->nrach_indication_body, ppWritePackedMsg, end, &pack_nrach_indication_body_value) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nr_dl_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nr_dl_node_sync_t *pNfapiMsg = (nfapi_nr_dl_node_sync_t *)msg; + return ( push32(pNfapiMsg->t1, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->delta_sfn_slot, ppWritePackedMsg, end) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_dl_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_dl_node_sync_t *pNfapiMsg = (nfapi_dl_node_sync_t *)msg; + return ( push32(pNfapiMsg->t1, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->delta_sfn_sf, ppWritePackedMsg, end) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nr_ul_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nr_ul_node_sync_t *pNfapiMsg = (nfapi_nr_ul_node_sync_t *)msg; + return (push32(pNfapiMsg->t1, ppWritePackedMsg, end) && + push32(pNfapiMsg->t2, ppWritePackedMsg, end) && + push32(pNfapiMsg->t3, ppWritePackedMsg, end) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_ul_node_sync(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_ul_node_sync_t *pNfapiMsg = (nfapi_ul_node_sync_t *)msg; + return (push32(pNfapiMsg->t1, ppWritePackedMsg, end) && + push32(pNfapiMsg->t2, ppWritePackedMsg, end) && + push32(pNfapiMsg->t3, ppWritePackedMsg, end) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_timing_info(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_timing_info_t *pNfapiMsg = (nfapi_timing_info_t *)msg; + return (push32(pNfapiMsg->last_sfn_sf, ppWritePackedMsg, end) && + push32(pNfapiMsg->time_since_last_timing_info, ppWritePackedMsg, end) && + push32(pNfapiMsg->dl_config_jitter, ppWritePackedMsg, end) && + push32(pNfapiMsg->tx_request_jitter, ppWritePackedMsg, end) && + push32(pNfapiMsg->ul_config_jitter, ppWritePackedMsg, end) && + push32(pNfapiMsg->hi_dci0_jitter, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->dl_config_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->tx_request_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->ul_config_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->hi_dci0_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->dl_config_earliest_arrival, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->tx_request_earliest_arrival, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->ul_config_earliest_arrival, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->hi_dci0_earliest_arrival, ppWritePackedMsg, end) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +static uint8_t pack_nr_timing_info(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nr_timing_info_t *pNfapiMsg = (nfapi_nr_timing_info_t *)msg; + return (push32(pNfapiMsg->last_sfn, ppWritePackedMsg, end) && + push32(pNfapiMsg->last_slot, ppWritePackedMsg, end) && + push32(pNfapiMsg->time_since_last_timing_info, ppWritePackedMsg, end) && + push32(pNfapiMsg->dl_tti_jitter, ppWritePackedMsg, end) && + push32(pNfapiMsg->tx_data_request_jitter, ppWritePackedMsg, end) && + push32(pNfapiMsg->ul_tti_jitter, ppWritePackedMsg, end) && + push32(pNfapiMsg->ul_dci_jitter, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->dl_tti_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->tx_data_request_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->ul_tti_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->ul_dci_latest_delay, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->dl_tti_earliest_arrival, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->tx_data_request_earliest_arrival, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->ul_tti_earliest_arrival, ppWritePackedMsg, end) && + pushs32(pNfapiMsg->ul_dci_earliest_arrival, ppWritePackedMsg, end) && + pack_p7_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)); +} + +//NR UPLINK indication function packing + +//SLOT INDICATION + +static uint8_t pack_nr_slot_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_slot_indication_scf_t *pNfapiMsg = (nfapi_nr_slot_indication_scf_t*)msg; + + if (!(push16((uint16_t)pNfapiMsg->sfn , ppWritePackedMsg, end) && + push16((uint16_t)pNfapiMsg->slot , ppWritePackedMsg, end) + )) + return 0; + +return 1; +} + +//RX DATA INDICATION + +static uint8_t pack_nr_rx_data_indication_body(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) +{ + nfapi_nr_rx_data_pdu_t* value = (nfapi_nr_rx_data_pdu_t*)tlv; + + if(!(push32(value->handle, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->harq_id, ppWritePackedMsg, end) && + push16(value->pdu_length, ppWritePackedMsg, end) && + push8(value->ul_cqi, ppWritePackedMsg, end) && + push16(value->timing_advance, ppWritePackedMsg, end) && + push16(value->rssi, ppWritePackedMsg, end) + )) + return 0; + + return 1; +} + + +static uint8_t pack_nr_rx_data_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_rx_data_indication_t *pNfapiMsg = (nfapi_nr_rx_data_indication_t*)msg; + + if (!(push16(pNfapiMsg->sfn , ppWritePackedMsg, end) && + push16(pNfapiMsg->slot , ppWritePackedMsg, end) && + push16(pNfapiMsg->number_of_pdus, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; inumber_of_pdus;i++) + { + if(!pack_nr_rx_data_indication_body(pNfapiMsg->pdu_list,ppWritePackedMsg,end)) + return 0; + } + +return 1; +} + +//NR CRC INDICATION + +static uint8_t pack_nr_crc_indication_body(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) +{ + nfapi_nr_crc_t* value = (nfapi_nr_crc_t*)tlv; + + if(!(push32(value->handle, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->harq_id, ppWritePackedMsg, end) && + push8(value->tb_crc_status, ppWritePackedMsg, end) && + push16(value->num_cb, ppWritePackedMsg, end) && + //pusharray8(value->cb_crc_status, (int)(value->num_cb / 8) + 1, (int)(value->num_cb / 8) + 1, ppWritePackedMsg, end) && //length is ceil(NumCb/8) + push8(value->ul_cqi, ppWritePackedMsg, end) && + push16(value->timing_advance, ppWritePackedMsg, end) && + push16(value->rssi, ppWritePackedMsg, end) + )) + return 0; + + return 1; +} + +static uint8_t pack_nr_crc_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_crc_indication_t *pNfapiMsg = (nfapi_nr_crc_indication_t*)msg; + + if (!(push16(pNfapiMsg->sfn , ppWritePackedMsg, end) && + push16(pNfapiMsg->slot , ppWritePackedMsg, end) && + push16(pNfapiMsg->number_crcs, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; inumber_crcs;i++) + { + if(!pack_nr_crc_indication_body(pNfapiMsg->crc_list,ppWritePackedMsg,end)) + return 0; + } + +return 1; +} + +//SRS INDICATION + +static uint8_t pack_nr_srs_indication_body(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) +{ + nfapi_nr_srs_indication_pdu_t* value = (nfapi_nr_srs_indication_pdu_t*)tlv; + + if(!(push32(value->handle, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push16(value->timing_advance, ppWritePackedMsg, end) && + push8(value->num_symbols, ppWritePackedMsg, end) && + push8(value->wide_band_snr, ppWritePackedMsg, end) && + push8(value->num_reported_symbols, ppWritePackedMsg, end) && + push8(value->reported_symbol_list->num_rbs, ppWritePackedMsg, end) + )) + return 0; + for(int i = 0; i < value->reported_symbol_list->num_rbs; i++) + { + if(!(push8(value->reported_symbol_list->rb_list->rb_snr, ppWritePackedMsg, end) + )) + return 0; + } + return 1; +} + +static uint8_t pack_nr_srs_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_srs_indication_t *pNfapiMsg = (nfapi_nr_srs_indication_t*)msg; + + if (!(push16(pNfapiMsg->sfn , ppWritePackedMsg, end) && + push16(pNfapiMsg->slot , ppWritePackedMsg, end) && + push16(pNfapiMsg->number_of_pdus, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; inumber_of_pdus;i++) + { + if(!pack_nr_srs_indication_body(&(pNfapiMsg->pdu_list[i]),ppWritePackedMsg,end)) + return 0; + } + +return 1; +} + +//RACH INDICATION + +static uint8_t pack_nr_rach_indication_body(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) +{ + nfapi_nr_prach_indication_pdu_t* value = (nfapi_nr_prach_indication_pdu_t*)tlv; + + if(!(push16(value->phy_cell_id, ppWritePackedMsg, end) && + push8(value->symbol_index, ppWritePackedMsg, end) && + push8(value->slot_index, ppWritePackedMsg, end) && + push8(value->freq_index, ppWritePackedMsg, end) && + push8(value->avg_rssi, ppWritePackedMsg, end) && + push8(value->avg_snr, ppWritePackedMsg, end) && + push8(value->num_preamble, ppWritePackedMsg, end) + )) + return 0; + for(int i = 0; i < value->num_preamble; i++) + { + if(!(push8(value->preamble_list->preamble_index, ppWritePackedMsg, end) && + push16(value->preamble_list->timing_advance, ppWritePackedMsg, end) && + push32(value->preamble_list->preamble_pwr, ppWritePackedMsg, end) + )) + return 0; + } + return 1; +} + +static uint8_t pack_nr_rach_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_rach_indication_t *pNfapiMsg = (nfapi_nr_rach_indication_t*)msg; + + if (!(push16(pNfapiMsg->sfn , ppWritePackedMsg, end) && + push16(pNfapiMsg->slot , ppWritePackedMsg, end) && + push8(pNfapiMsg->number_of_pdus, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; inumber_of_pdus;i++) + { + if(!pack_nr_rach_indication_body(&(pNfapiMsg->pdu_list[i]),ppWritePackedMsg,end)) + return 0; + } + +return 1; +} + + +//UCI INDICATION + +static uint8_t pack_nr_uci_pucch_0_1(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_uci_pucch_pdu_format_0_1_t* value = (nfapi_nr_uci_pucch_pdu_format_0_1_t*)tlv; + + if(!(push8(value->pduBitmap, ppWritePackedMsg, end) && + push32(value->handle, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->pucch_format, ppWritePackedMsg, end) && + push8(value->ul_cqi, ppWritePackedMsg, end) && + push16(value->timing_advance, ppWritePackedMsg, end) && + push16(value->rssi, ppWritePackedMsg, end) + )) + return 0; + if (value->pduBitmap & 0x01) { //SR + if(!(push8(value->sr->sr_indication, ppWritePackedMsg, end) && + push8(value->sr->sr_confidence_level, ppWritePackedMsg, end) + )) + return 0; + } + + if (((value->pduBitmap >> 1) & 0x01)) { //HARQ + if(!(push8(value->harq->num_harq, ppWritePackedMsg, end) && + push8(value->harq->harq_confidence_level, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; iharq->num_harq;i++) + { + if(!(push8(value->harq->harq_list[i].harq_value, ppWritePackedMsg, end) + )) + return 0; + } + } + + return 1; +} + +static uint8_t pack_nr_uci_pucch_2_3_4(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) { + nfapi_nr_uci_pucch_pdu_format_2_3_4_t* value = (nfapi_nr_uci_pucch_pdu_format_2_3_4_t*) tlv; + + if(!(push8(value->pduBitmap, ppWritePackedMsg, end) && + push32(value->handle, ppWritePackedMsg, end) && + push16(value->rnti, ppWritePackedMsg, end) && + push8(value->pucch_format, ppWritePackedMsg, end) && + push8(value->ul_cqi, ppWritePackedMsg, end) && + push16(value->timing_advance, ppWritePackedMsg, end) && + push16(value->rssi, ppWritePackedMsg, end) + )) + return 0; + + if (value->pduBitmap & 0x01) { //SR + if(!(push8(value->sr.sr_bit_len, ppWritePackedMsg, end) && + pusharray8(value->sr.sr_payload, (int)(value->sr.sr_bit_len / 8) + 1, (int)(value->sr.sr_bit_len / 8) + 1, ppWritePackedMsg, end) + )) + return 0; + } + + if (((value->pduBitmap >> 1) & 0x01)) { //HARQ + if(!(push8(value->harq.harq_crc, ppWritePackedMsg, end) && + push8(value->harq.harq_bit_len, ppWritePackedMsg, end) && + pusharray8(value->harq.harq_payload, (int)(value->harq.harq_bit_len / 8) + 1, (int)(value->harq.harq_bit_len / 8) + 1, ppWritePackedMsg, end) + )) + return 0; + } + + if (((value->pduBitmap >> 2) & 0x01)) { //CSI-1 + if(!(push8(value->csi_part1.csi_part1_crc, ppWritePackedMsg, end) && + push8(value->csi_part1.csi_part1_bit_len, ppWritePackedMsg, end) && + pusharray8(value->csi_part1.csi_part1_payload, (int)(value->csi_part1.csi_part1_bit_len / 8) + 1, (int)(value->csi_part1.csi_part1_bit_len / 8) + 1, ppWritePackedMsg, end) + )) + return 0; + } + + if (((value->pduBitmap >> 3) & 0x01)) { //CSI-2 + if(!(push8(value->csi_part2.csi_part2_crc, ppWritePackedMsg, end) && + push8(value->csi_part2.csi_part2_bit_len, ppWritePackedMsg, end) && + pusharray8(value->csi_part2.csi_part2_payload, (int)(value->csi_part2.csi_part2_bit_len / 8) + 1, (int)(value->csi_part2.csi_part2_bit_len / 8) + 1, ppWritePackedMsg, end) + )) + return 0; + } + + return 1; +} + +static uint8_t pack_nr_uci_indication_body(void* tlv, uint8_t **ppWritePackedMsg, uint8_t *end) +{ + nfapi_nr_uci_t* value = (nfapi_nr_uci_t*)tlv; + + if(!(push16(value->pdu_type, ppWritePackedMsg, end) && + push16(value->pdu_size, ppWritePackedMsg, end) + )) + return 0; + + switch (value->pdu_type) { + case NFAPI_NR_UCI_PUSCH_PDU_TYPE: + printf("Unhandled NFAPI_NR_UCI_PUSCH_PDU_TYPE \n"); + break; + + case NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE: + pack_nr_uci_pucch_0_1(&value->pucch_pdu_format_0_1, ppWritePackedMsg, end); + break; + + case NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE: + pack_nr_uci_pucch_2_3_4(&value->pucch_pdu_format_2_3_4, ppWritePackedMsg, end); + break; + } + + return 1; +} + +static uint8_t pack_nr_uci_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_uci_indication_t *pNfapiMsg = (nfapi_nr_uci_indication_t*)msg; + + if (!(push16(pNfapiMsg->sfn , ppWritePackedMsg, end) && + push16(pNfapiMsg->slot , ppWritePackedMsg, end) && + push16(pNfapiMsg->num_ucis, ppWritePackedMsg, end) + )) + return 0; + + for(int i=0; inum_ucis;i++) + { + if(!pack_nr_uci_indication_body(pNfapiMsg->uci_list,ppWritePackedMsg,end)) + return 0; + } + +return 1; +} + + +// Main pack function - public + +int nfapi_nr_p7_message_pack(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen, nfapi_p7_codec_config_t* config) +{ + nfapi_p7_message_header_t *pMessageHeader = pMessageBuf; + uint8_t *pWritePackedMessage = pPackedBuf; + uint8_t *pPackedLengthField = &pWritePackedMessage[4]; + uint8_t *end = pPackedBuf + packedBufLen; + + if (pMessageBuf == NULL || pPackedBuf == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack supplied pointers are null\n"); + return -1; + } + /* + printf("\n P7 MESSAGE SENT: \n"); + for(int i=0; i< packedBufLen; i++){ + printf("%d", *(uint8_t *)(pMessageBuf + i)); + } + printf("\n"); + */ + // process the header + if(!(push16(pMessageHeader->phy_id, &pWritePackedMessage, end) && + push16(pMessageHeader->message_id, &pWritePackedMessage, end) && + push16(0/*pMessageHeader->message_length*/, &pWritePackedMessage, end) && + push16(pMessageHeader->m_segment_sequence, &pWritePackedMessage, end) && + push32(0/*pMessageHeader->checksum*/, &pWritePackedMessage, end) && + push32(pMessageHeader->transmit_timestamp, &pWritePackedMessage, end))) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack header failed\n"); + return -1; + } + + if (pMessageHeader->message_id != NFAPI_TIMING_INFO) + { + //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() message_id:0x%04x phy_id:%u m_segment_sequence:%u timestamp:%u\n", __FUNCTION__, pMessageHeader->message_id, pMessageHeader->phy_id, pMessageHeader->m_segment_sequence, pMessageHeader->transmit_timestamp); + } + // look for the specific message + uint8_t result = 0; + switch (pMessageHeader->message_id) + { + case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST: + result = pack_dl_tti_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST: + result = pack_ul_tti_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST: + result = pack_tx_data_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST: + result = pack_ul_dci_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_UE_RELEASE_REQUEST: + result =pack_ue_release_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_UE_RELEASE_RESPONSE: + result =pack_ue_release_response(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_SLOT_INDICATION: + result = pack_nr_slot_indication(pMessageHeader, &pWritePackedMessage, end, config); + + case NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION: + result = pack_nr_rx_data_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_CRC_INDICATION: + result = pack_nr_crc_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UCI_INDICATION: + result = pack_nr_uci_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_SRS_INDICATION: + result = pack_nr_srs_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION: + result = pack_nr_rach_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC: + result = pack_nr_dl_node_sync(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC: + result = pack_nr_ul_node_sync(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_TIMING_INFO: + result = pack_nr_timing_info(pMessageHeader, &pWritePackedMessage, end, config); + break; + + default: + { + if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) + { + if(config && config->pack_p7_vendor_extension) + { + result = (config->pack_p7_vendor_extension)(pMessageHeader, &pWritePackedMessage, end, config); + } + else + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, pMessageHeader->message_id); + } + } + else + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); + } + } + break; + } + + if(result == 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack failed to pack message\n"); + return -1; + } + + // check for a valid message length + uintptr_t msgHead = (uintptr_t)pPackedBuf; + uintptr_t msgEnd = (uintptr_t)pWritePackedMessage; + uint32_t packedMsgLen = msgEnd - msgHead; + uint16_t packedMsgLen16; + if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); + return -1; + } + else + { + packedMsgLen16 = (uint16_t)packedMsgLen; + } + + // Update the message length in the header + pMessageHeader->message_length = packedMsgLen16; + + if(!push16(packedMsgLen16, &pPackedLengthField, end)) + return -1; + + if(1) + { + //quick test + if(pMessageHeader->message_length != packedMsgLen) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "nfapi packedMsgLen(%d) != message_length(%d) id %d\n", packedMsgLen, pMessageHeader->message_length, pMessageHeader->message_id); + } + } + + return (packedMsgLen); +} + +int nfapi_p7_message_pack(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen, nfapi_p7_codec_config_t *config) { + nfapi_p7_message_header_t *pMessageHeader = pMessageBuf; + uint8_t *pWritePackedMessage = pPackedBuf; + uint8_t *pPackedLengthField = &pWritePackedMessage[4]; + uint8_t *end = pPackedBuf + packedBufLen; + + if (pMessageBuf == NULL || pPackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack supplied pointers are null\n"); + return -1; + } + + /* + printf("\n P7 MESSAGE SENT: \n"); + for(int i=0; i< packedBufLen; i++){ + printf("%d", *(uint8_t *)(pMessageBuf + i)); + } + printf("\n"); + */ + // process the header + if(!(push16(pMessageHeader->phy_id, &pWritePackedMessage, end) && + push16(pMessageHeader->message_id, &pWritePackedMessage, end) && + push16(0/*pMessageHeader->message_length*/, &pWritePackedMessage, end) && + push16(pMessageHeader->m_segment_sequence, &pWritePackedMessage, end) && + push32(0/*pMessageHeader->checksum*/, &pWritePackedMessage, end) && + push32(pMessageHeader->transmit_timestamp, &pWritePackedMessage, end))) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack header failed\n"); + return -1; + } + + if (pMessageHeader->message_id != NFAPI_TIMING_INFO) { + //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() message_id:0x%04x phy_id:%u m_segment_sequence:%u timestamp:%u\n", __FUNCTION__, pMessageHeader->message_id, pMessageHeader->phy_id, pMessageHeader->m_segment_sequence, pMessageHeader->transmit_timestamp); + } + + // look for the specific message + uint8_t result = 0; + + switch (pMessageHeader->message_id) { + case NFAPI_DL_CONFIG_REQUEST: + //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() NFAPI_DL_CONFIG_REQUEST\n", __FUNCTION__); + result = pack_dl_config_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_UL_CONFIG_REQUEST: + result = pack_ul_config_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_TX_REQUEST: + //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() NFAPI_TX_REQUEST\n", __FUNCTION__); + result = pack_tx_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_HI_DCI0_REQUEST: + result = pack_hi_dci0_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_UE_RELEASE_REQUEST: + result =pack_ue_release_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_UE_RELEASE_RESPONSE: + result =pack_ue_release_response(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_HARQ_INDICATION: + result = pack_harq_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_CRC_INDICATION: + result = pack_crc_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_RX_ULSCH_INDICATION: + //printf("RX ULSCH\n"); + result = pack_rx_ulsch_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_RACH_INDICATION: + result = pack_rach_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_SRS_INDICATION: + result = pack_srs_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_RX_SR_INDICATION: + result = pack_sr_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_RX_CQI_INDICATION: + result = pack_cqi_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_LBT_DL_CONFIG_REQUEST: + result = pack_lbt_dl_config_request(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_LBT_DL_INDICATION: + result = pack_lbt_dl_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NB_HARQ_INDICATION: + result = pack_nb_harq_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_NRACH_INDICATION: + result = pack_nrach_indication(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_DL_NODE_SYNC: + result = pack_dl_node_sync(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_UL_NODE_SYNC: + result = pack_ul_node_sync(pMessageHeader, &pWritePackedMessage, end, config); + break; + + case NFAPI_TIMING_INFO: + result = pack_timing_info(pMessageHeader, &pWritePackedMessage, end, config); + break; + + default: { + if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { + if(config && config->pack_p7_vendor_extension) { + result = (config->pack_p7_vendor_extension)(pMessageHeader, &pWritePackedMessage, end, config); + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve ecoder provided\n", __FUNCTION__, pMessageHeader->message_id); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); + } + } + break; + } + + if(result == 0) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 Pack failed to pack message\n"); + return -1; + } + + // check for a valid message length + uintptr_t msgHead = (uintptr_t)pPackedBuf; + uintptr_t msgEnd = (uintptr_t)pWritePackedMessage; + uint32_t packedMsgLen = msgEnd - msgHead; + uint16_t packedMsgLen16; + + if (packedMsgLen > 0xFFFF || packedMsgLen > packedBufLen) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "Packed message length error %d, buffer supplied %d\n", packedMsgLen, packedBufLen); + return -1; + } else { + packedMsgLen16 = (uint16_t)packedMsgLen; + } + + // Update the message length in the header + pMessageHeader->message_length = packedMsgLen16; + + if(!push16(packedMsgLen16, &pPackedLengthField, end)) + return -1; + + if(1) { + //quick test + if(pMessageHeader->message_length != packedMsgLen) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "nfapi packedMsgLen(%d) != message_length(%d) id %d\n", packedMsgLen, pMessageHeader->message_length, pMessageHeader->message_id); + } + } + + return (packedMsgLen); +} + +// Unpack routines +// NR: +static uint8_t unpack_dl_tti_csi_rs_pdu_rel15_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *value = (nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *)tlv; + return( + pull16(ppReadPackedMsg, &value->bwp_size, end) && + pull16(ppReadPackedMsg, &value->bwp_start, end) && + pull8(ppReadPackedMsg, &value->subcarrier_spacing, end) && + pull8(ppReadPackedMsg, &value->cyclic_prefix, end) && + pull16(ppReadPackedMsg, &value->start_rb, end) && + pull16(ppReadPackedMsg, &value->nr_of_rbs, end) && + pull8(ppReadPackedMsg, &value->csi_type, end) && + pull8(ppReadPackedMsg, &value->row, end) && + pull16(ppReadPackedMsg, &value->freq_domain, end) && + pull8(ppReadPackedMsg, &value->symb_l0, end) && + pull8(ppReadPackedMsg, &value->symb_l1, end) && + pull8(ppReadPackedMsg, &value->cdm_type, end) && + pull8(ppReadPackedMsg, &value->freq_density, end) && + pull16(ppReadPackedMsg, &value->scramb_id, end) && + pull8(ppReadPackedMsg, &value->power_control_offset, end) && + pull8(ppReadPackedMsg, &value->power_control_offset_ss, end) + ); +} + + +static uint8_t unpack_dl_tti_pdcch_pdu_rel15_value(void* tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + + nfapi_nr_dl_tti_pdcch_pdu_rel15_t* value = (nfapi_nr_dl_tti_pdcch_pdu_rel15_t*)tlv; + + for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) + { + if(!(pull16(ppReadPackedMsg, &value->dci_pdu[i].RNTI, end) && + pull16(ppReadPackedMsg, &value->dci_pdu[i].ScramblingId, end) && + pull16(ppReadPackedMsg, &value->dci_pdu[i].ScramblingRNTI, end) && + pull8(ppReadPackedMsg, &value->dci_pdu[i].CceIndex, end) && + pull8(ppReadPackedMsg, &value->dci_pdu[i].AggregationLevel, end) && + pull8(ppReadPackedMsg, &value->dci_pdu[i].beta_PDCCH_1_0, end) && + pull8(ppReadPackedMsg, &value->dci_pdu[i].powerControlOffsetSS, end) && + pull16(ppReadPackedMsg, &value->dci_pdu[i].PayloadSizeBits, end) && + + pullarray8(ppReadPackedMsg, value->dci_pdu[i].Payload, value->dci_pdu[i].PayloadSizeBits, value->dci_pdu[i].PayloadSizeBits, end))) + + return 0; + } + // TODO: resolve the packaging of array (currently sending a single element) + return( + pull16(ppReadPackedMsg, &value->BWPSize, end) && + pull16(ppReadPackedMsg, &value->BWPStart, end) && + pull8(ppReadPackedMsg, &value->SubcarrierSpacing, end) && + pull8(ppReadPackedMsg, &value->CyclicPrefix, end) && + pull8(ppReadPackedMsg, &value->StartSymbolIndex, end) && + pull8(ppReadPackedMsg, &value->DurationSymbols, end) && + pullarray8(ppReadPackedMsg, value->FreqDomainResource, 6, 6, end) && + pull8(ppReadPackedMsg, &value->CceRegMappingType, end) && + pull8(ppReadPackedMsg, &value->RegBundleSize, end) && + pull8(ppReadPackedMsg, &value->InterleaverSize, end) && + pull8(ppReadPackedMsg, &value->CoreSetType, end) && + pull16(ppReadPackedMsg, &value->ShiftIndex, end) && + pull8(ppReadPackedMsg, &value->precoderGranularity, end) && + pull16(ppReadPackedMsg, &value->numDlDci, end)); +} + + + +static uint8_t unpack_dl_tti_pdsch_pdu_rel15_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *value = (nfapi_nr_dl_tti_pdsch_pdu_rel15_t *)tlv; + // TODO: resolve the packaging of array (currently sending a single element) + return( + pull16(ppReadPackedMsg, &value->pduBitmap, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull16(ppReadPackedMsg, &value->pduIndex, end) && + pull16(ppReadPackedMsg, &value->BWPSize, end) && + pull16(ppReadPackedMsg, &value->BWPStart, end) && + pull8(ppReadPackedMsg, &value->SubcarrierSpacing, end) && + pull8(ppReadPackedMsg, &value->CyclicPrefix, end) && + pull8(ppReadPackedMsg, &value->NrOfCodewords, end) && + pullarray16(ppReadPackedMsg, value->targetCodeRate, 2, 1, end) && + pullarray8(ppReadPackedMsg, value->qamModOrder, 2, 1, end) && + pullarray8(ppReadPackedMsg, value->mcsIndex, 2, 1, end) && + pullarray8(ppReadPackedMsg, value->mcsTable, 2, 1, end) && + pullarray8(ppReadPackedMsg, value->rvIndex, 2, 1, end) && + pullarray32(ppReadPackedMsg, value->TBSize, 2, 1, end) && + pull16(ppReadPackedMsg, &value->dataScramblingId, end) && + pull8(ppReadPackedMsg, &value->nrOfLayers, end) && + pull8(ppReadPackedMsg, &value->transmissionScheme, end) && + pull8(ppReadPackedMsg, &value->refPoint, end) && + pull16(ppReadPackedMsg, &value->dlDmrsSymbPos, end) && + pull8(ppReadPackedMsg, &value->dmrsConfigType, end) && + pull16(ppReadPackedMsg, &value->dlDmrsScramblingId, end) && + pull8(ppReadPackedMsg, &value->SCID, end) && + pull8(ppReadPackedMsg, &value->numDmrsCdmGrpsNoData, end) && + pull16(ppReadPackedMsg, &value->dmrsPorts, end) && + pull8(ppReadPackedMsg, &value->resourceAlloc, end) && + pull16(ppReadPackedMsg, &value->rbStart, end) && + pull16(ppReadPackedMsg, &value->rbSize, end) && + pull8(ppReadPackedMsg, &value->VRBtoPRBMapping, end) && + pull8(ppReadPackedMsg, &value->StartSymbolIndex, end) && + pull8(ppReadPackedMsg, &value->NrOfSymbols, end) && + pull8(ppReadPackedMsg, &value->PTRSPortIndex, end) && + pull8(ppReadPackedMsg, &value->PTRSTimeDensity, end) && + pull8(ppReadPackedMsg, &value->PTRSFreqDensity, end) && + pull8(ppReadPackedMsg, &value->PTRSReOffset, end) + ); +} + + +static uint8_t unpack_dl_tti_ssb_pdu_rel15_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_dl_tti_ssb_pdu_rel15_t *value = (nfapi_nr_dl_tti_ssb_pdu_rel15_t *)tlv; + return( + pull16(ppReadPackedMsg, &value->PhysCellId, end) && + pull8(ppReadPackedMsg, &value->BetaPss, end) && + pull8(ppReadPackedMsg, &value->SsbBlockIndex, end) && + pull8(ppReadPackedMsg, &value->SsbSubcarrierOffset, end) && + pull16(ppReadPackedMsg, &value->ssbOffsetPointA, end) && + pull8(ppReadPackedMsg, &value->bchPayloadFlag, end) && + pull32(ppReadPackedMsg, &value->bchPayload, end) + // TODO: pack precoding_and_beamforming too + ); +} + + +// LTE: +static uint8_t unpack_dl_config_dci_dl_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel8_t *dci_dl_pdu_rel8 = (nfapi_dl_config_dci_dl_pdu_rel8_t *)tlv; + return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->dci_format, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->cce_idx, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->aggregation_level, end) && + pull16(ppReadPackedMsg, &dci_dl_pdu_rel8->rnti, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->resource_allocation_type, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->virtual_resource_block_assignment_flag, end) && + pull32(ppReadPackedMsg, &dci_dl_pdu_rel8->resource_block_coding, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->mcs_1, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->redundancy_version_1, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->new_data_indicator_1, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->transport_block_to_codeword_swap_flag, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->mcs_2, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->redundancy_version_2, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->new_data_indicator_2, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->harq_process, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->tpmi, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->pmi, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->precoding_information, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->tpc, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->downlink_assignment_index, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->ngap, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->transport_block_size_index, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->downlink_power_offset, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->allocate_prach_flag, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->preamble_index, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->prach_mask_index, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel8->rnti_type, end) && + pull16(ppReadPackedMsg, &dci_dl_pdu_rel8->transmission_power, end)); +} + +static uint8_t unpack_dl_config_dci_dl_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel9_t *dci_dl_pdu_rel9 = (nfapi_dl_config_dci_dl_pdu_rel9_t *)tlv; + return ( pull8(ppReadPackedMsg, &dci_dl_pdu_rel9->mcch_flag, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel9->mcch_change_notification, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel9->scrambling_identity, end)); +} + +static uint8_t unpack_dl_config_dci_dl_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel10_t *dci_dl_pdu_rel10 = (nfapi_dl_config_dci_dl_pdu_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->cross_carrier_scheduling_flag, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->carrier_indicator, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->srs_flag, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->srs_request, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->antenna_ports_scrambling_and_layers, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->total_dci_length_including_padding, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel10->n_dl_rb, end)); +} + +static uint8_t unpack_dl_config_dci_dl_pdu_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel11_t *dci_dl_pdu_rel11 = (nfapi_dl_config_dci_dl_pdu_rel11_t *)tlv; + return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel11->harq_ack_resource_offset, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel11->pdsch_re_mapping_quasi_co_location_indicator, end)); +} + +static uint8_t unpack_dl_config_dci_dl_pdu_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel12_t *dci_dl_pdu_rel12 = (nfapi_dl_config_dci_dl_pdu_rel12_t *)tlv; + return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel12->primary_cell_type, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel12->ul_dl_configuration_flag, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel12->number_ul_dl_configurations, end) && + pullarray8(ppReadPackedMsg, dci_dl_pdu_rel12->ul_dl_configuration_indication, NFAPI_MAX_UL_DL_CONFIGURATIONS, dci_dl_pdu_rel12->number_ul_dl_configurations, end)); +} + +static uint8_t unpack_tpm_value(uint8_t **ppReadPackedMsg, nfapi_dl_config_dci_dl_tpm_t *value, uint8_t *end) { + if(!(pull8(ppReadPackedMsg, &value->num_prb_per_subband, end) && + pull8(ppReadPackedMsg, &value->number_of_subbands, end) && + pull8(ppReadPackedMsg, &value->num_antennas, end))) + return 0; + + uint8_t idx = 0; + + for(idx = 0; idx < value->number_of_subbands; ++idx) { + nfapi_dl_config_dci_dl_tpm_subband_info_t *subband_info = &(value->subband_info[idx]); + + if(!(pull8(ppReadPackedMsg, &subband_info->subband_index, end) && + pull8(ppReadPackedMsg, &subband_info->scheduled_ues, end))) + return 0; + + uint8_t antenna_idx = 0; + uint8_t scheduled_ue_idx = 0; + + for(antenna_idx = 0; antenna_idx < value->num_antennas; ++antenna_idx) { + for(scheduled_ue_idx = 0; scheduled_ue_idx < subband_info->scheduled_ues; ++scheduled_ue_idx) { + if(!pull16(ppReadPackedMsg, &(subband_info->precoding_value[antenna_idx][scheduled_ue_idx]), end)) + return 0; + } + } + } + + return 1; +} + + +static uint8_t unpack_dl_config_dci_dl_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dci_dl_pdu_rel13_t *dci_dl_pdu_rel13 = (nfapi_dl_config_dci_dl_pdu_rel13_t *)tlv; + // If the length is greater than 5 then the TPM struct flag and possiably the TPM structure have been + // added + uint8_t tpm_struct_flag_present = dci_dl_pdu_rel13->tl.length > 5; + dci_dl_pdu_rel13->tpm_struct_flag = 0; + return (pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->laa_end_partial_sf_flag, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->laa_end_partial_sf_configuration, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->initial_lbt_sf, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->codebook_size_determination, end) && + pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->drms_table_flag, end) && + ( (tpm_struct_flag_present == 1) ? pull8(ppReadPackedMsg, &dci_dl_pdu_rel13->tpm_struct_flag, end) : 1) && + ( (tpm_struct_flag_present == 1 && dci_dl_pdu_rel13->tpm_struct_flag == 1) ? unpack_tpm_value(ppReadPackedMsg, &dci_dl_pdu_rel13->tpm, end) : 1)); +} + +static uint8_t unpack_dl_config_bch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_bch_pdu_rel8_t *bch_pdu_rel8 = (nfapi_dl_config_bch_pdu_rel8_t *)tlv; + return ( pull16(ppReadPackedMsg, &bch_pdu_rel8->length, end) && + pull16(ppReadPackedMsg, (uint16_t *)&bch_pdu_rel8->pdu_index, end) && + pull16(ppReadPackedMsg, &bch_pdu_rel8->transmission_power, end)); +} + +static uint8_t unpack_dl_config_mch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_mch_pdu_rel8_t *mch_pdu_rel8 = (nfapi_dl_config_mch_pdu_rel8_t *)tlv; + return (pull16(ppReadPackedMsg, &mch_pdu_rel8->length, end) && + pull16(ppReadPackedMsg, (uint16_t *)&mch_pdu_rel8->pdu_index, end) && + pull16(ppReadPackedMsg, &mch_pdu_rel8->rnti, end) && + pull8(ppReadPackedMsg, &mch_pdu_rel8->resource_allocation_type, end) && + pull32(ppReadPackedMsg, &mch_pdu_rel8->resource_block_coding, end) && + pull8(ppReadPackedMsg, &mch_pdu_rel8->modulation, end) && + pull16(ppReadPackedMsg, &mch_pdu_rel8->transmission_power, end) && + pull16(ppReadPackedMsg, &mch_pdu_rel8->mbsfn_area_id, end)); +} + +static uint8_t unpack_dl_config_dlsch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel8_t *dlsch_pdu_rel8 = (nfapi_dl_config_dlsch_pdu_rel8_t *)tlv; + + if (!(pull16(ppReadPackedMsg, &dlsch_pdu_rel8->length, end) && + pull16(ppReadPackedMsg, (uint16_t *)&dlsch_pdu_rel8->pdu_index, end) && + pull16(ppReadPackedMsg, &dlsch_pdu_rel8->rnti, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->resource_allocation_type, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->virtual_resource_block_assignment_flag, end) && + pull32(ppReadPackedMsg, &dlsch_pdu_rel8->resource_block_coding, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->modulation, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->redundancy_version, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transport_blocks, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transport_block_to_codeword_swap_flag, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transmission_scheme, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->number_of_layers, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->number_of_subbands, end) && + pullarray8(ppReadPackedMsg, dlsch_pdu_rel8->codebook_index, NFAPI_MAX_NUM_SUBBANDS, dlsch_pdu_rel8->number_of_subbands, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->ue_category_capacity, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->pa, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->delta_power_offset_index, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->ngap, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->nprb, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->transmission_mode, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->num_bf_prb_per_subband, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->num_bf_vector, end))) + return 0; + + uint16_t j = 0; + + for(j = 0; j < dlsch_pdu_rel8->num_bf_vector; ++j) { + if(!(pull8(ppReadPackedMsg, &dlsch_pdu_rel8->bf_vector[j].subband_index, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel8->bf_vector[j].num_antennas, end) && + pullarray16(ppReadPackedMsg, dlsch_pdu_rel8->bf_vector[j].bf_value, NFAPI_MAX_NUM_ANTENNAS, dlsch_pdu_rel8->bf_vector[j].num_antennas, end))) + return 0; + } + + return 1; +} +static uint8_t unpack_dl_config_dlsch_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel9_t *dlsch_pdu_rel9 = (nfapi_dl_config_dlsch_pdu_rel9_t *)tlv; + return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel9->nscid, end) ); +} +static uint8_t unpack_dl_config_dlsch_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel10_t *dlsch_pdu_rel10 = (nfapi_dl_config_dlsch_pdu_rel10_t *)tlv; + return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_flag, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_resource_config_r10, end) && + pull16(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_zero_tx_power_resource_config_bitmap_r10, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel10->csi_rs_number_nzp_configuration, end) && + pullarray8(ppReadPackedMsg, dlsch_pdu_rel10->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, dlsch_pdu_rel10->csi_rs_number_nzp_configuration, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel10->pdsch_start, end)) ; +} +static uint8_t unpack_dl_config_dlsch_pdu_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel11_t *dlsch_pdu_rel11 = (nfapi_dl_config_dlsch_pdu_rel11_t *)tlv; + return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel11->drms_config_flag, end) && + pull16(ppReadPackedMsg, &dlsch_pdu_rel11->drms_scrambling, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel11->csi_config_flag, end) && + pull16(ppReadPackedMsg, &dlsch_pdu_rel11->csi_scrambling, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel11->pdsch_re_mapping_flag, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel11->pdsch_re_mapping_atenna_ports, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel11->pdsch_re_mapping_freq_shift, end)); +} +static uint8_t unpack_dl_config_dlsch_pdu_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel12_t *dlsch_pdu_rel12 = (nfapi_dl_config_dlsch_pdu_rel12_t *)tlv; + return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel12->altcqi_table_r12, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel12->maxlayers, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel12->n_dl_harq, end)); +} +static uint8_t unpack_dl_config_dlsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_dlsch_pdu_rel13_t *dlsch_pdu_rel13 = (nfapi_dl_config_dlsch_pdu_rel13_t *)tlv; + return ( pull8(ppReadPackedMsg, &dlsch_pdu_rel13->dwpts_symbols, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel13->initial_lbt_sf, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel13->ue_type, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel13->pdsch_payload_type, end) && + pull16(ppReadPackedMsg, &dlsch_pdu_rel13->initial_transmission_sf_io, end) && + pull8(ppReadPackedMsg, &dlsch_pdu_rel13->drms_table_flag, end)); +} + +static uint8_t unpack_dl_config_pch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_pch_pdu_rel8_t *pch_pdu_rel8 = (nfapi_dl_config_pch_pdu_rel8_t *)tlv; + return ( pull16(ppReadPackedMsg, &pch_pdu_rel8->length, end) && + pull16(ppReadPackedMsg, (uint16_t *)&pch_pdu_rel8->pdu_index, end) && + pull16(ppReadPackedMsg, &pch_pdu_rel8->p_rnti, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->resource_allocation_type, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->virtual_resource_block_assignment_flag, end) && + pull32(ppReadPackedMsg, &pch_pdu_rel8->resource_block_coding, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->mcs, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->redundancy_version, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->number_of_transport_blocks, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->transport_block_to_codeword_swap_flag, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->transmission_scheme, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->number_of_layers, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->codebook_index, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->ue_category_capacity, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->pa, end) && + pull16(ppReadPackedMsg, &pch_pdu_rel8->transmission_power, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->nprb, end) && + pull8(ppReadPackedMsg, &pch_pdu_rel8->ngap, end)); +} +static uint8_t unpack_dl_config_pch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_pch_pdu_rel13_t *pch_pdu_rel13 = (nfapi_dl_config_pch_pdu_rel13_t *)tlv; + return ( pull8(ppReadPackedMsg, &pch_pdu_rel13->ue_mode, end) && + pull16(ppReadPackedMsg, &pch_pdu_rel13->initial_transmission_sf_io, end)); +} + +static uint8_t unpack_dl_config_prs_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_prs_pdu_rel9_t *prs_pdu_rel9 = (nfapi_dl_config_prs_pdu_rel9_t *)tlv; + return ( pull16(ppReadPackedMsg, &prs_pdu_rel9->transmission_power, end) && + pull8(ppReadPackedMsg, &prs_pdu_rel9->prs_bandwidth, end) && + pull8(ppReadPackedMsg, &prs_pdu_rel9->prs_cyclic_prefix_type, end) && + pull8(ppReadPackedMsg, &prs_pdu_rel9->prs_muting, end)); +} + +static uint8_t unpack_dl_config_csi_rs_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_csi_rs_pdu_rel10_t *csi_rs_pdu_rel10 = (nfapi_dl_config_csi_rs_pdu_rel10_t *)tlv; + return ( pull8(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_antenna_port_count_r10, end) && + pull8(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_resource_config_r10, end) && + pull16(ppReadPackedMsg, &csi_rs_pdu_rel10->transmission_power, end) && + pull16(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_zero_tx_power_resource_config_bitmap_r10, end) && + pull8(ppReadPackedMsg, &csi_rs_pdu_rel10->csi_rs_number_of_nzp_configuration, end) && + pullarray8(ppReadPackedMsg, csi_rs_pdu_rel10->csi_rs_resource_config, NFAPI_MAX_CSI_RS_RESOURCE_CONFIG, csi_rs_pdu_rel10->csi_rs_number_of_nzp_configuration, end)); +} + +static uint8_t unpack_dl_config_csi_rs_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_csi_rs_pdu_rel13_t *csi_rs_pdu_rel13 = (nfapi_dl_config_csi_rs_pdu_rel13_t *)tlv; + + if (!(pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->csi_rs_class, end) && + pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->cdm_type, end) && + pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->num_bf_vector, end))) + return 0; + + uint16_t idx =0; + + for(idx = 0; idx < csi_rs_pdu_rel13->num_bf_vector; ++idx) { + if(!(pull8(ppReadPackedMsg, &csi_rs_pdu_rel13->bf_vector[idx].csi_rs_resource_index, end))) + return 0; + + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : HOW TO DECODE BF VALUE \n"); + //pullarray16(ppReadPackedMsg, &csi_rs_pdu_rel13->bf_vector[idx].bf_vector, ??); + } + + return 1; +} + +static uint8_t unpack_dl_config_epdcch_params_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_epdcch_parameters_rel11_t *epdcch_params_rel11 = (nfapi_dl_config_epdcch_parameters_rel11_t *)tlv; + return (pull8(ppReadPackedMsg, &epdcch_params_rel11->epdcch_resource_assignment_flag, end) && + pull16(ppReadPackedMsg, &epdcch_params_rel11->epdcch_id, end) && + pull8(ppReadPackedMsg, &epdcch_params_rel11->epdcch_start_symbol, end) && + pull8(ppReadPackedMsg, &epdcch_params_rel11->epdcch_num_prb, end) && + pullarray8(ppReadPackedMsg, epdcch_params_rel11->epdcch_prb_index, NFAPI_MAX_EPDCCH_PRB, epdcch_params_rel11->epdcch_num_prb, end) && + pull8(ppReadPackedMsg, &epdcch_params_rel11->bf_vector.subband_index, end) && + pull8(ppReadPackedMsg, &epdcch_params_rel11->bf_vector.num_antennas, end) && + pullarray16(ppReadPackedMsg, epdcch_params_rel11->bf_vector.bf_value, NFAPI_MAX_NUM_ANTENNAS, epdcch_params_rel11->bf_vector.num_antennas, end)); +} + +static uint8_t unpack_dl_config_epdcch_params_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_epdcch_parameters_rel13_t *epdcch_params_rel13 = (nfapi_dl_config_epdcch_parameters_rel13_t *)tlv; + return ( pull8(ppReadPackedMsg, &epdcch_params_rel13->dwpts_symbols, end) && + pull8(ppReadPackedMsg, &epdcch_params_rel13->initial_lbt_sf, end)); +} + +static uint8_t unpack_dl_config_mpdcch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_mpdcch_pdu_rel13_t *mpdcch_params_rel13 = (nfapi_dl_config_mpdcch_pdu_rel13_t *)tlv; + return ( pull8(ppReadPackedMsg, &mpdcch_params_rel13->mpdcch_narrow_band, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->number_of_prb_pairs, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->resource_block_assignment, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->mpdcch_tansmission_type, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->start_symbol, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->ecce_index, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->aggregation_level, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->rnti_type, end) && + pull16(ppReadPackedMsg, &mpdcch_params_rel13->rnti, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->ce_mode, end) && + pull16(ppReadPackedMsg, &mpdcch_params_rel13->drms_scrambling_init, end) && + pull16(ppReadPackedMsg, &mpdcch_params_rel13->initial_transmission_sf_io, end) && + pull16(ppReadPackedMsg, &mpdcch_params_rel13->transmission_power, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->dci_format, end) && + pull16(ppReadPackedMsg, &mpdcch_params_rel13->resource_block_coding, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->mcs, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->pdsch_reptition_levels, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->redundancy_version, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->new_data_indicator, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->harq_process, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->tpmi_length, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->tpmi, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->pmi_flag, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->pmi, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->harq_resource_offset, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->dci_subframe_repetition_number, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->tpc, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->downlink_assignment_index_length, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->downlink_assignment_index, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->allocate_prach_flag, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->preamble_index, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->prach_mask_index, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->starting_ce_level, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->srs_request, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->antenna_ports_and_scrambling_identity_flag, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->antenna_ports_and_scrambling_identity, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->frequency_hopping_enabled_flag, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->paging_direct_indication_differentiation_flag, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->direct_indication, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->total_dci_length_including_padding, end) && + pull8(ppReadPackedMsg, &mpdcch_params_rel13->number_of_tx_antenna_ports, end) && + pullarray16(ppReadPackedMsg, mpdcch_params_rel13->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, mpdcch_params_rel13->number_of_tx_antenna_ports, end)); +} + + +static uint8_t unpack_dl_config_nbch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_nbch_pdu_rel13_t *nbch_params_rel13 = (nfapi_dl_config_nbch_pdu_rel13_t *)tlv; + return ( pull16(ppReadPackedMsg, &nbch_params_rel13->length, end) && + pull16(ppReadPackedMsg, (uint16_t *)&nbch_params_rel13->pdu_index, end) && + pull16(ppReadPackedMsg, &nbch_params_rel13->transmission_power, end) && + pull16(ppReadPackedMsg, &nbch_params_rel13->hyper_sfn_2_lsbs, end)); +} + +static uint8_t unpack_dl_config_npdcch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_npdcch_pdu_rel13_t *npdcch_params_rel13 = (nfapi_dl_config_npdcch_pdu_rel13_t *)tlv; + return ( pull16(ppReadPackedMsg, &npdcch_params_rel13->length, end) && + pull16(ppReadPackedMsg, (uint16_t *)&npdcch_params_rel13->pdu_index, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->ncce_index, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->aggregation_level, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->start_symbol, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->rnti_type, end) && + pull16(ppReadPackedMsg, &npdcch_params_rel13->rnti, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->scrambling_reinitialization_batch_index, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->nrs_antenna_ports_assumed_by_the_ue, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->dci_format, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->scheduling_delay, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->resource_assignment, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->repetition_number, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->mcs, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->new_data_indicator, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->harq_ack_resource, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->npdcch_order_indication, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->starting_number_of_nprach_repetitions, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->subcarrier_indication_of_nprach, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->paging_direct_indication_differentation_flag, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->direct_indication, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->dci_subframe_repetition_number, end) && + pull8(ppReadPackedMsg, &npdcch_params_rel13->total_dci_length_including_padding, end)); +} + +static uint8_t unpack_dl_config_ndlsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_dl_config_ndlsch_pdu_rel13_t *ndlsch_params_rel13 = (nfapi_dl_config_ndlsch_pdu_rel13_t *)tlv; + return ( pull16(ppReadPackedMsg, &ndlsch_params_rel13->length, end) && + pull16(ppReadPackedMsg, (uint16_t *)&ndlsch_params_rel13->pdu_index, end) && + pull8(ppReadPackedMsg, &ndlsch_params_rel13->start_symbol, end) && + pull8(ppReadPackedMsg, &ndlsch_params_rel13->rnti_type, end) && + pull16(ppReadPackedMsg, &ndlsch_params_rel13->rnti, end) && + pull16(ppReadPackedMsg, &ndlsch_params_rel13->resource_assignment, end) && + pull16(ppReadPackedMsg, &ndlsch_params_rel13->repetition_number, end) && + pull8(ppReadPackedMsg, &ndlsch_params_rel13->modulation, end) && + pull8(ppReadPackedMsg, &ndlsch_params_rel13->number_of_subframes_for_resource_assignment, end) && + pull8(ppReadPackedMsg, &ndlsch_params_rel13->scrambling_sequence_initialization_cinit, end) && + pull16(ppReadPackedMsg, &ndlsch_params_rel13->sf_idx, end) && + pull8(ppReadPackedMsg, &ndlsch_params_rel13->nrs_antenna_ports_assumed_by_the_ue, end)); +} + + +static uint8_t unpack_dl_tti_request_body_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { + nfapi_nr_dl_tti_request_pdu_t *value = (nfapi_nr_dl_tti_request_pdu_t *)msg; + + if(!(pull32(ppReadPackedMsg, &value->PDUSize, end) && + pull16(ppReadPackedMsg, &value->PDUType, end) )) + return 0; + + // first match the pdu type, then call the respective function + switch(value->PDUType) { + case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: { + if(!(unpack_dl_tti_csi_rs_pdu_rel15_value(&value->csi_rs_pdu.csi_rs_pdu_rel15,ppReadPackedMsg,end))) + return 0; + } + break; + + case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: { + if(!(unpack_dl_tti_pdcch_pdu_rel15_value(&value->pdcch_pdu.pdcch_pdu_rel15,ppReadPackedMsg,end))) + return 0; + } + break; + + case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: { + if(!(unpack_dl_tti_pdsch_pdu_rel15_value(&value->pdsch_pdu.pdsch_pdu_rel15,ppReadPackedMsg,end))) + return 0; + } + break; + + case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: { + if(!(unpack_dl_tti_ssb_pdu_rel15_value(&value->ssb_pdu.ssb_pdu_rel15,ppReadPackedMsg,end))) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid DL_TTI pdu type %d \n", value->PDUType ); + } + break; + } + + return 1; +} + + + + +static uint8_t unpack_dl_config_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_dl_config_request_body_t *value = (nfapi_dl_config_request_body_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &value->number_pdcch_ofdm_symbols, end) && + pull8(ppReadPackedMsg, &value->number_dci, end) && + pull16(ppReadPackedMsg, &value->number_pdu, end) && + pull8(ppReadPackedMsg, &value->number_pdsch_rnti, end) && + pull16(ppReadPackedMsg, &value->transmission_power_pcfich, end))) + return 0; + + if(value->number_pdu > NFAPI_DL_CONFIG_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of dl config pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_pdu, NFAPI_DL_CONFIG_MAX_PDU); + return 0; + } + + if(value->number_pdu) { + value->dl_config_pdu_list = (nfapi_dl_config_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_dl_config_request_pdu_t) * value->number_pdu, config); + + if(value->dl_config_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate dl config pdu list (count:%d)\n", __FUNCTION__, value->number_pdu); + return 0; + } + } else { + value->dl_config_pdu_list = 0; + } + + uint16_t i; + uint16_t total_number_of_pdus = value->number_pdu; + + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_dl_config_request_pdu_t *pdu = &(value->dl_config_pdu_list[i]); + + if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && + pull8(ppReadPackedMsg, &pdu->pdu_size, end))) + return 0; + + uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; + + if(packedPduEnd > end) { + // pdu end of beyond buffer end + return 0; + } + + switch(pdu->pdu_type) { + case NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL8_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel8, &unpack_dl_config_dci_dl_pdu_rel8_value}, + { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL9_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel9, &unpack_dl_config_dci_dl_pdu_rel9_value}, + { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL10_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel10, &unpack_dl_config_dci_dl_pdu_rel10_value}, + { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL11_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel11, &unpack_dl_config_dci_dl_pdu_rel11_value}, + { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL12_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel12, &unpack_dl_config_dci_dl_pdu_rel12_value}, + { NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL13_TAG, &pdu->dci_dl_pdu.dci_dl_pdu_rel13, &unpack_dl_config_dci_dl_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_BCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_BCH_PDU_REL8_TAG, &pdu->bch_pdu.bch_pdu_rel8, &unpack_dl_config_bch_pdu_rel8_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_MCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_MCH_PDU_REL8_TAG, &pdu->mch_pdu.mch_pdu_rel8, &unpack_dl_config_mch_pdu_rel8_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_DLSCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL8_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel8, &unpack_dl_config_dlsch_pdu_rel8_value}, + { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL9_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel9, &unpack_dl_config_dlsch_pdu_rel9_value}, + { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL10_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel10, &unpack_dl_config_dlsch_pdu_rel10_value}, + { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL11_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel11, &unpack_dl_config_dlsch_pdu_rel11_value}, + { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL12_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel12, &unpack_dl_config_dlsch_pdu_rel12_value}, + { NFAPI_DL_CONFIG_REQUEST_DLSCH_PDU_REL13_TAG, &pdu->dlsch_pdu.dlsch_pdu_rel13, &unpack_dl_config_dlsch_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_PCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL8_TAG, &pdu->pch_pdu.pch_pdu_rel8, &unpack_dl_config_pch_pdu_rel8_value}, + { NFAPI_DL_CONFIG_REQUEST_PCH_PDU_REL13_TAG, &pdu->pch_pdu.pch_pdu_rel13, &unpack_dl_config_pch_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_PRS_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_PRS_PDU_REL9_TAG, &pdu->prs_pdu.prs_pdu_rel9, &unpack_dl_config_prs_pdu_rel9_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_CSI_RS_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL10_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel10, &unpack_dl_config_csi_rs_pdu_rel10_value}, + { NFAPI_DL_CONFIG_REQUEST_CSI_RS_PDU_REL13_TAG, &pdu->csi_rs_pdu.csi_rs_pdu_rel13, &unpack_dl_config_csi_rs_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_EPDCCH_DL_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL8_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel8, &unpack_dl_config_dci_dl_pdu_rel8_value}, + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL9_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel9, &unpack_dl_config_dci_dl_pdu_rel9_value}, + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL10_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel10, &unpack_dl_config_dci_dl_pdu_rel10_value}, + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL11_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel11, &unpack_dl_config_dci_dl_pdu_rel11_value}, + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL12_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel12, &unpack_dl_config_dci_dl_pdu_rel12_value}, + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PDU_REL13_TAG, &pdu->epdcch_pdu.epdcch_pdu_rel13, &unpack_dl_config_dci_dl_pdu_rel13_value}, + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL11_TAG, &pdu->epdcch_pdu.epdcch_params_rel11, &unpack_dl_config_epdcch_params_rel11_value}, + { NFAPI_DL_CONFIG_REQUEST_EPDCCH_PARAM_REL13_TAG, &pdu->epdcch_pdu.epdcch_params_rel13, &unpack_dl_config_epdcch_params_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_MPDCCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_MPDCCH_PDU_REL13_TAG, &pdu->mpdcch_pdu.mpdcch_pdu_rel13, &unpack_dl_config_mpdcch_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_NBCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_NBCH_PDU_REL13_TAG, &pdu->nbch_pdu.nbch_pdu_rel13, &unpack_dl_config_nbch_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_NPDCCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_NPDCCH_PDU_REL13_TAG, &pdu->npdcch_pdu.npdcch_pdu_rel13, &unpack_dl_config_npdcch_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_DL_CONFIG_NDLSCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_NDLSCH_PDU_REL13_TAG, &pdu->ndlsch_pdu.ndlsch_pdu_rel13, &unpack_dl_config_ndlsch_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + default: + // Need to log an error + break; + } + } + + return 1; +} + + +static uint8_t unpack_dl_tti_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nr_dl_tti_request_t *pNfapiMsg = (nfapi_nr_dl_tti_request_t *)msg; + + if (!(pull16(ppReadPackedMsg,&pNfapiMsg->SFN, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->dl_tti_request_body.nGroup, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->dl_tti_request_body.nPDUs, end) && + pullarray8(ppReadPackedMsg,pNfapiMsg->dl_tti_request_body.nUe,256,pNfapiMsg->dl_tti_request_body.nGroup, end) + //pusharray8(pNfapiMsg->PduIdx[0] ,256,256, ppWritePackedMsg, end) + )) + return 0; + + int arr[12]; + + for(int i=0; idl_tti_request_body.nGroup; i++) { + for(int j=0; jdl_tti_request_body.nUe[i]; j++) { + arr[j] = pNfapiMsg->dl_tti_request_body.PduIdx[i][j]; + } + + if(!(pullarrays32(ppReadPackedMsg,arr,12,pNfapiMsg->dl_tti_request_body.nUe[i], end))) + return 0; + } + + for(int i=0; idl_tti_request_body.nPDUs; i++) { + if(!unpack_dl_tti_request_body_value(ppReadPackedMsg, end, &pNfapiMsg->dl_tti_request_body.dl_tti_pdu_list[i])) + return 0; + } + + return 1; +} + + +static uint8_t unpack_ul_tti_request_prach_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_prach_pdu_t *prach_pdu = (nfapi_nr_prach_pdu_t *)tlv; + return( + pull16(ppReadPackedMsg, &prach_pdu->phys_cell_id, end) && + pull8(ppReadPackedMsg, &prach_pdu->num_prach_ocas, end) && + pull8(ppReadPackedMsg, &prach_pdu->prach_format, end) && + pull8(ppReadPackedMsg, &prach_pdu->num_ra, end) && + pull8(ppReadPackedMsg, &prach_pdu->prach_start_symbol, end) && + pull16(ppReadPackedMsg, &prach_pdu->num_cs, end) + // TODO: ignoring beamforming tlv for now + ); +} + + +static uint8_t unpack_ul_tti_request_pucch_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_pucch_pdu_t *pucch_pdu = (nfapi_nr_pucch_pdu_t *)tlv; + return( + pull16(ppReadPackedMsg, &pucch_pdu->rnti, end) && + pull32(ppReadPackedMsg, &pucch_pdu->handle, end) && + pull16(ppReadPackedMsg, &pucch_pdu->bwp_size, end) && + pull16(ppReadPackedMsg, &pucch_pdu->bwp_start, end) && + pull8(ppReadPackedMsg, &pucch_pdu->subcarrier_spacing, end) && + pull8(ppReadPackedMsg, &pucch_pdu->cyclic_prefix, end) && + pull8(ppReadPackedMsg, &pucch_pdu->format_type, end) && + pull8(ppReadPackedMsg, &pucch_pdu->multi_slot_tx_indicator, end) && + pull16(ppReadPackedMsg, &pucch_pdu->prb_start, end) && + pull16(ppReadPackedMsg, &pucch_pdu->prb_size, end) && + pull8(ppReadPackedMsg, &pucch_pdu->start_symbol_index, end) && + pull8(ppReadPackedMsg, &pucch_pdu->nr_of_symbols, end) && + pull8(ppReadPackedMsg, &pucch_pdu->freq_hop_flag, end) && + pull16(ppReadPackedMsg, &pucch_pdu->second_hop_prb, end) && + pull8(ppReadPackedMsg, &pucch_pdu->group_hop_flag, end) && + pull8(ppReadPackedMsg, &pucch_pdu->sequence_hop_flag, end) && + pull16(ppReadPackedMsg, &pucch_pdu->hopping_id, end) && + pull16(ppReadPackedMsg, &pucch_pdu->initial_cyclic_shift, end) && + pull16(ppReadPackedMsg, &pucch_pdu->data_scrambling_id, end) && + pull8(ppReadPackedMsg, &pucch_pdu->time_domain_occ_idx, end) && + pull8(ppReadPackedMsg, &pucch_pdu->pre_dft_occ_idx, end) && + pull8(ppReadPackedMsg, &pucch_pdu->pre_dft_occ_len, end) && + pull8(ppReadPackedMsg, &pucch_pdu->add_dmrs_flag, end) && + pull16(ppReadPackedMsg, &pucch_pdu->dmrs_scrambling_id, end) && + pull8(ppReadPackedMsg, &pucch_pdu->dmrs_cyclic_shift, end) && + pull8(ppReadPackedMsg, &pucch_pdu->sr_flag, end) && + pull8(ppReadPackedMsg, &pucch_pdu->bit_len_harq, end) && + pull16(ppReadPackedMsg, &pucch_pdu->bit_len_csi_part1, end) && + pull16(ppReadPackedMsg, &pucch_pdu->bit_len_csi_part2, end) + // TODO: ignoring beamforming tlv for now + ); +} + + +static uint8_t unpack_ul_tti_request_pusch_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_pusch_pdu_t *pusch_pdu = (nfapi_nr_pusch_pdu_t *)tlv; + + if (!( + pull16(ppReadPackedMsg, &pusch_pdu->pdu_bit_map, end) && + pull16(ppReadPackedMsg, &pusch_pdu->rnti, end) && + pull32(ppReadPackedMsg, &pusch_pdu->handle, end) && + pull16(ppReadPackedMsg, &pusch_pdu->bwp_size, end) && + pull16(ppReadPackedMsg, &pusch_pdu->bwp_start, end) && + pull8(ppReadPackedMsg, &pusch_pdu->subcarrier_spacing, end) && + pull8(ppReadPackedMsg, &pusch_pdu->cyclic_prefix, end) && + pull16(ppReadPackedMsg, &pusch_pdu->target_code_rate, end) && + pull8(ppReadPackedMsg, &pusch_pdu->qam_mod_order, end) && + pull8(ppReadPackedMsg, &pusch_pdu->mcs_index, end) && + pull8(ppReadPackedMsg, &pusch_pdu->mcs_table, end) && + pull8(ppReadPackedMsg, &pusch_pdu->transform_precoding, end) && + pull16(ppReadPackedMsg, &pusch_pdu->data_scrambling_id, end) && + pull8(ppReadPackedMsg, &pusch_pdu->nrOfLayers, end) && + pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_symb_pos, end) && + pull8(ppReadPackedMsg, &pusch_pdu->dmrs_config_type, end) && + pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_scrambling_id, end) && + pull8(ppReadPackedMsg, &pusch_pdu->scid, end) && + pull8(ppReadPackedMsg, &pusch_pdu->num_dmrs_cdm_grps_no_data, end) && + pull16(ppReadPackedMsg, &pusch_pdu->dmrs_ports, end) && + pull8(ppReadPackedMsg, &pusch_pdu->resource_alloc, end) && + pull8(ppReadPackedMsg, &pusch_pdu->resource_alloc,end) && + pull16(ppReadPackedMsg, &pusch_pdu->dmrs_ports, end) && + pull16(ppReadPackedMsg, &pusch_pdu->rb_start, end) && + pull16(ppReadPackedMsg, &pusch_pdu->rb_size, end) && + pull8(ppReadPackedMsg, &pusch_pdu->vrb_to_prb_mapping, end) && + pull8(ppReadPackedMsg, &pusch_pdu->frequency_hopping, end) && + pull16(ppReadPackedMsg, &pusch_pdu->tx_direct_current_location, end) && + pull8(ppReadPackedMsg, &pusch_pdu->uplink_frequency_shift_7p5khz, end) && + pull8(ppReadPackedMsg, &pusch_pdu->start_symbol_index, end) && + pull8(ppReadPackedMsg, &pusch_pdu->nr_of_symbols, end) + // TODO: ignoring beamforming tlv for now + )) + return 0; + + //Pack Optional Data only included if indicated in pduBitmap + switch(pusch_pdu->pdu_bit_map) { + case PUSCH_PDU_BITMAP_PUSCH_DATA: { + // pack optional TLVs + return( + pull8(ppReadPackedMsg, &pusch_pdu->pusch_data.rv_index, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_data.harq_process_id, end) && + pull32(ppReadPackedMsg, &pusch_pdu->pusch_data.tb_size, end) && + pull16(ppReadPackedMsg, &pusch_pdu->pusch_data.num_cb, end) && + pullarray8(ppReadPackedMsg, pusch_pdu->pusch_data.cb_present_and_position,1,1,end) + ); + } + break; + + case PUSCH_PDU_BITMAP_PUSCH_UCI: { + return( + pull16(ppReadPackedMsg, &pusch_pdu->pusch_uci.harq_ack_bit_length, end) && + pull16(ppReadPackedMsg, &pusch_pdu->pusch_uci.csi_part1_bit_length, end) && + pull16(ppReadPackedMsg, &pusch_pdu->pusch_uci.csi_part2_bit_length, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.alpha_scaling, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.beta_offset_harq_ack, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.beta_offset_csi1, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_uci.beta_offset_csi2, end) + ); + } + break; + + case PUSCH_PDU_BITMAP_PUSCH_PTRS: { + return( + pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.num_ptrs_ports, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_dmrs_port, end) && + + pull16(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_port_index, end) && + + pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_ports_list->ptrs_re_offset, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_time_density, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ptrs_freq_density, end) && + pull8(ppReadPackedMsg, &pusch_pdu->pusch_ptrs.ul_ptrs_power, end) + ); + } + break; + + case PUSCH_PDU_BITMAP_DFTS_OFDM: { + return( + pull8(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.low_papr_group_number, end) && + pull16(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.low_papr_sequence_number, end) && + pull8(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.ul_ptrs_sample_density, end) && + pull8(ppReadPackedMsg, &pusch_pdu->dfts_ofdm.ul_ptrs_time_density_transform_precoding, end) + ); + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_INFO, "Invalid pdu bitmap %d \n", pusch_pdu->pdu_bit_map ); + } + } + + return 1; +} + + +static uint8_t unpack_ul_tti_request_srs_pdu(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_srs_pdu_t *srs_pdu = (nfapi_nr_srs_pdu_t *)tlv; + return( + pull16(ppReadPackedMsg, &srs_pdu->rnti, end) && + pull32(ppReadPackedMsg, &srs_pdu->handle, end) && + pull16(ppReadPackedMsg, &srs_pdu->bwp_size, end) && + pull16(ppReadPackedMsg, &srs_pdu->bwp_start, end) && + pull8(ppReadPackedMsg, &srs_pdu->subcarrier_spacing, end) && + pull8(ppReadPackedMsg, &srs_pdu->cyclic_prefix, end) && + pull8(ppReadPackedMsg, &srs_pdu->num_ant_ports, end) && + pull8(ppReadPackedMsg, &srs_pdu->num_symbols, end) && + pull8(ppReadPackedMsg, &srs_pdu->num_repetitions, end) && + pull8(ppReadPackedMsg, &srs_pdu->time_start_position, end) && + pull8(ppReadPackedMsg, &srs_pdu->config_index, end) && + pull16(ppReadPackedMsg, &srs_pdu->sequence_id, end) && + pull8(ppReadPackedMsg, &srs_pdu->bandwidth_index, end) && + pull8(ppReadPackedMsg, &srs_pdu->comb_size, end) && + pull8(ppReadPackedMsg, &srs_pdu->comb_offset, end) && + pull8(ppReadPackedMsg, &srs_pdu->cyclic_shift, end) && + pull8(ppReadPackedMsg, &srs_pdu->frequency_position, end) && + pull8(ppReadPackedMsg, &srs_pdu->frequency_shift, end) && + pull8(ppReadPackedMsg, &srs_pdu->frequency_hopping, end) && + pull8(ppReadPackedMsg, &srs_pdu->group_or_sequence_hopping, end) && + pull8(ppReadPackedMsg, &srs_pdu->resource_type, end) && + pull16(ppReadPackedMsg, &srs_pdu->t_srs, end) && + pull16(ppReadPackedMsg, &srs_pdu->t_offset, end) + // TODO: ignoring beamforming tlv for now + ); +} + + +static uint8_t unpack_ul_tti_pdu_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { + nfapi_nr_ul_tti_request_number_of_pdus_t *pNfapiMsg = (nfapi_nr_ul_tti_request_number_of_pdus_t *)msg; + + if(!(pull16(ppReadPackedMsg, &pNfapiMsg->pdu_size, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->pdu_type, end) )) + return 0; + + // first natch the pdu type, then call the respective function + switch(pNfapiMsg->pdu_type) { + case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: { + if(!unpack_ul_tti_request_prach_pdu(&pNfapiMsg->prach_pdu, ppReadPackedMsg, end)) + return 0; + } + break; + + case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE: { + if(!unpack_ul_tti_request_pucch_pdu(&pNfapiMsg->pucch_pdu, ppReadPackedMsg, end)) + return 0; + } + break; + + case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: { + if(!unpack_ul_tti_request_pusch_pdu(&pNfapiMsg->pusch_pdu, ppReadPackedMsg, end)) + return 0; + } + break; + + case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: { + if(!unpack_ul_tti_request_srs_pdu(&pNfapiMsg->srs_pdu, ppReadPackedMsg, end)) + return 0; + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid UL_TTI pdu type %d \n", pNfapiMsg->pdu_type ); + } + break; + } + + return 1; +} + + +static uint8_t unpack_ul_tti_groups_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { + nfapi_nr_ul_tti_request_number_of_groups_t *pNfapiMsg = (nfapi_nr_ul_tti_request_number_of_groups_t *)msg; + + if(!pull8(ppReadPackedMsg, &pNfapiMsg->n_ue, end)) + return 0; + + for (int i = 0; i < pNfapiMsg->n_ue; i++) { + if(!pull8(ppReadPackedMsg, &pNfapiMsg->ue_list[i].pdu_idx,end) ) + return 0; + } + + return 1; +} + + +static uint8_t unpack_ul_tti_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nr_ul_tti_request_t *pNfapiMsg = (nfapi_nr_ul_tti_request_t *)msg; + + if (!( + pull16(ppReadPackedMsg, &pNfapiMsg->SFN, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->n_pdus, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->n_group, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->rach_present, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->n_ulcch, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->n_ulsch, end) )) + return 0; + + for(int i=0; i< pNfapiMsg->n_pdus; i++) { + if (!unpack_ul_tti_pdu_list_value(ppReadPackedMsg, end, &pNfapiMsg->pdus_list[i])) + return 0; + } + + for(int i=0; i< pNfapiMsg->n_group; i++) { + if (!unpack_ul_tti_groups_list_value(ppReadPackedMsg, end, &pNfapiMsg->groups_list[i])) + return 0; + } + + return 1; +} + + + +static uint8_t unpack_dl_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_dl_config_request_t *pNfapiMsg = (nfapi_dl_config_request_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->dl_config_request_body, &unpack_dl_config_request_body_value}, + }; + return ( pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_ul_config_ulsch_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel8_t *ulsch_pdu_rel8 = (nfapi_ul_config_ulsch_pdu_rel8_t *)tlv; + return (pull32(ppReadPackedMsg, &ulsch_pdu_rel8->handle, end) && + pull16(ppReadPackedMsg, &ulsch_pdu_rel8->size, end) && + pull16(ppReadPackedMsg, &ulsch_pdu_rel8->rnti, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->resource_block_start, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->number_of_resource_blocks, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->modulation_type, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->cyclic_shift_2_for_drms, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->frequency_hopping_enabled_flag, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->frequency_hopping_bits, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->new_data_indication, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->redundancy_version, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->harq_process_number, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->ul_tx_mode, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->current_tx_nb, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel8->n_srs, end )); +} + +static uint8_t unpack_ul_config_ulsch_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel10_t *ulsch_pdu_rel10 = (nfapi_ul_config_ulsch_pdu_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &ulsch_pdu_rel10->resource_allocation_type, end) && + pull32(ppReadPackedMsg, &ulsch_pdu_rel10->resource_block_coding, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel10->transport_blocks, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel10->transmission_scheme, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel10->number_of_layers, end) & + pull8(ppReadPackedMsg, &ulsch_pdu_rel10->codebook_index, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel10->disable_sequence_hopping_flag, end)); +} +static uint8_t unpack_ul_config_ulsch_pdu_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel11_t *ulsch_pdu_rel11 = (nfapi_ul_config_ulsch_pdu_rel11_t *)tlv; + return ( pull8(ppReadPackedMsg, &ulsch_pdu_rel11->virtual_cell_id_enabled_flag, end) && + pull16(ppReadPackedMsg, &ulsch_pdu_rel11->npusch_identity, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel11->dmrs_config_flag, end) && + pull16(ppReadPackedMsg, &ulsch_pdu_rel11->ndmrs_csh_identity, end)); +} +static uint8_t unpack_ul_config_ulsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_pdu_rel13_t *ulsch_pdu_rel13 = (nfapi_ul_config_ulsch_pdu_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &ulsch_pdu_rel13->ue_type, end) && + pull16(ppReadPackedMsg, &ulsch_pdu_rel13->total_number_of_repetitions, end) && + pull16(ppReadPackedMsg, &ulsch_pdu_rel13->repetition_number, end) && + pull16(ppReadPackedMsg, &ulsch_pdu_rel13->initial_transmission_sf_io, end) && + pull8(ppReadPackedMsg, &ulsch_pdu_rel13->empty_symbols_due_to_re_tunning, end)); +} +static uint8_t unpack_ul_config_cqi_ri_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_ri_information_rel8_t *cqi_ri_info_rel8 = (nfapi_ul_config_cqi_ri_information_rel8_t *)tlv; + return (pull8(ppReadPackedMsg, &cqi_ri_info_rel8->dl_cqi_pmi_size_rank_1, end) && + pull8(ppReadPackedMsg, &cqi_ri_info_rel8->dl_cqi_pmi_size_rank_greater_1, end) && + pull8(ppReadPackedMsg, &cqi_ri_info_rel8->ri_size, end) && + pull8(ppReadPackedMsg, &cqi_ri_info_rel8->delta_offset_cqi, end) && + pull8(ppReadPackedMsg, &cqi_ri_info_rel8->delta_offset_ri, end)); +} + +static uint8_t unpack_ul_config_cqi_ri_info_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_ri_information_rel9_t *cqi_ri_info_rel9 = (nfapi_ul_config_cqi_ri_information_rel9_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->report_type, end) && + pull8(ppReadPackedMsg, &cqi_ri_info_rel9->delta_offset_cqi, end) && + pull8(ppReadPackedMsg, &cqi_ri_info_rel9->delta_offset_ri, end))) + return 0; + + switch(cqi_ri_info_rel9->report_type) { + case NFAPI_CSI_REPORT_TYPE_PERIODIC: { + if(!(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size, end) && + pull8(ppReadPackedMsg, &cqi_ri_info_rel9->periodic_cqi_pmi_ri_report.control_type, end))) + return 0; + } + break; + + case NFAPI_CSI_REPORT_TYPE_APERIODIC: { + if(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc, end) ==0) + return 0; + + uint8_t i; + + for(i = 0; i < cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.number_of_cc; ++i) { + if(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].ri_size, end) == 0) + return 0; + + uint8_t j; + + for(j = 0; j < 8; ++j) { + if(pull8(ppReadPackedMsg, &cqi_ri_info_rel9->aperiodic_cqi_pmi_ri_report.cc[i].dl_cqi_pmi_size[j], end) == 0) + return 0; + } + } + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid report type %d \n", cqi_ri_info_rel9->report_type ); + return 0; + } + break; + }; + + return 1; +} + +// NOTE : This function is a little unconventional as we uese the side to +// determine the report type +static uint8_t unpack_ul_config_cqi_ri_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_ri_information_rel13_t *cqi_ri_info_rel13 = (nfapi_ul_config_cqi_ri_information_rel13_t *)tlv; + + if(cqi_ri_info_rel13->tl.length == 0) { + cqi_ri_info_rel13->report_type = NFAPI_CSI_REPORT_TYPE_APERIODIC; + } else { + cqi_ri_info_rel13->report_type = NFAPI_CSI_REPORT_TYPE_PERIODIC; + + if(pull16(ppReadPackedMsg, &cqi_ri_info_rel13->periodic_cqi_pmi_ri_report.dl_cqi_pmi_ri_size_2, end) == 0) + return 0; + } + + return 1; +} +static uint8_t unpack_ul_config_cqi_init_tx_params_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_initial_transmission_parameters_rel8_t *init_tx_params_rel8 = (nfapi_ul_config_initial_transmission_parameters_rel8_t *)tlv; + return (pull8(ppReadPackedMsg, &init_tx_params_rel8->n_srs_initial, end) && + pull8(ppReadPackedMsg, &init_tx_params_rel8->initial_number_of_resource_blocks, end)); +} +static uint8_t unpack_ul_config_ulsch_harq_info_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_harq_information_rel10_t *harq_info_rel10 = (nfapi_ul_config_ulsch_harq_information_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &harq_info_rel10->harq_size, end) && + pull8(ppReadPackedMsg, &harq_info_rel10->delta_offset_harq, end) && + pull8(ppReadPackedMsg, &harq_info_rel10->ack_nack_mode, end)); +} + +static uint8_t unpack_ul_config_ulsch_harq_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ulsch_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_ulsch_harq_information_rel13_t *)tlv; + return (pull16(ppReadPackedMsg, &harq_info_rel13->harq_size_2, end) && + pull8(ppReadPackedMsg, &harq_info_rel13->delta_offset_harq_2, end)); +} + +static uint8_t unpack_ul_config_ue_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ue_information_rel8_t *ue_info_rel8 = (nfapi_ul_config_ue_information_rel8_t *)tlv; + return (pull32(ppReadPackedMsg, &ue_info_rel8->handle, end) && + pull16(ppReadPackedMsg, (uint16_t *)&ue_info_rel8->rnti, end)); +} +static uint8_t unpack_ul_config_ue_info_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ue_information_rel11_t *ue_info_rel11 = (nfapi_ul_config_ue_information_rel11_t *)tlv; + return (pull8(ppReadPackedMsg, &ue_info_rel11->virtual_cell_id_enabled_flag, end) && + pull16(ppReadPackedMsg, &ue_info_rel11->npusch_identity, end)); +} +static uint8_t unpack_ul_config_ue_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_ue_information_rel13_t *ue_info_rel13 = (nfapi_ul_config_ue_information_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &ue_info_rel13->ue_type, end) && + pull8(ppReadPackedMsg, &ue_info_rel13->empty_symbols, end) && + pull16(ppReadPackedMsg, &ue_info_rel13->total_number_of_repetitions, end) && + pull16(ppReadPackedMsg, &ue_info_rel13->repetition_number, end)); +} + +static uint8_t unpack_ul_config_cqi_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_information_rel8_t *cqi_info_rel8 = (nfapi_ul_config_cqi_information_rel8_t *)tlv; + return ( pull16(ppReadPackedMsg, &cqi_info_rel8->pucch_index, end) && + pull8(ppReadPackedMsg, &cqi_info_rel8->dl_cqi_pmi_size, end)); +} +static uint8_t unpack_ul_config_cqi_info_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_information_rel10_t *cqi_info_rel10 = (nfapi_ul_config_cqi_information_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &cqi_info_rel10->number_of_pucch_resource, end) && + pull16(ppReadPackedMsg, &cqi_info_rel10->pucch_index_p1, end)); +} +static uint8_t unpack_ul_config_cqi_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_cqi_information_rel13_t *cqi_info_rel13 = (nfapi_ul_config_cqi_information_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &cqi_info_rel13->csi_mode, end) && + pull16(ppReadPackedMsg, &cqi_info_rel13->dl_cqi_pmi_size_2, end) && + pull8(ppReadPackedMsg, &cqi_info_rel13->starting_prb, end) && + pull8(ppReadPackedMsg, &cqi_info_rel13->n_prb, end) && + pull8(ppReadPackedMsg, &cqi_info_rel13->cdm_index, end) && + pull8(ppReadPackedMsg, &cqi_info_rel13->n_srs, end)); +} + +static uint8_t unpack_ul_config_sr_info_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_sr_information_rel8_t *sr_info_rel8 = (nfapi_ul_config_sr_information_rel8_t *)tlv; + return ( pull16(ppReadPackedMsg, &sr_info_rel8->pucch_index, end)); +} + +static uint8_t unpack_ul_config_sr_info_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_sr_information_rel10_t *sr_info_rel10 = (nfapi_ul_config_sr_information_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &sr_info_rel10->number_of_pucch_resources, end) && + pull16(ppReadPackedMsg, &sr_info_rel10->pucch_index_p1, end)); +} + +static uint8_t unpack_ul_config_harq_info_rel10_tdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel10_tdd_t *harq_info_tdd_rel10 = (nfapi_ul_config_harq_information_rel10_tdd_t *)tlv; + return (pull8(ppReadPackedMsg, &harq_info_tdd_rel10->harq_size, end) && + pull8(ppReadPackedMsg, &harq_info_tdd_rel10->ack_nack_mode, end) && + pull8(ppReadPackedMsg, &harq_info_tdd_rel10->number_of_pucch_resources, end) && + pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_0, end) && + pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_1, end) && + pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_2, end) && + pull16(ppReadPackedMsg, &harq_info_tdd_rel10->n_pucch_1_3, end)); +} + +static uint8_t unpack_ul_config_harq_info_rel8_fdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel8_fdd_t *harq_info_fdd_rel8 = (nfapi_ul_config_harq_information_rel8_fdd_t *)tlv; + return (pull16(ppReadPackedMsg, &harq_info_fdd_rel8->n_pucch_1_0, end) && + pull8(ppReadPackedMsg, &harq_info_fdd_rel8->harq_size, end)); +} + +static uint8_t unpack_ul_config_harq_info_rel9_fdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel9_fdd_t *harq_info_fdd_rel9 = (nfapi_ul_config_harq_information_rel9_fdd_t *)tlv; + return (pull8(ppReadPackedMsg, &harq_info_fdd_rel9->harq_size, end) && + pull8(ppReadPackedMsg, &harq_info_fdd_rel9->ack_nack_mode, end) && + pull8(ppReadPackedMsg, &harq_info_fdd_rel9->number_of_pucch_resources, end) && + pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_0, end) && + pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_1, end) && + pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_2, end) && + pull16(ppReadPackedMsg, &harq_info_fdd_rel9->n_pucch_1_3, end)); +} + +static uint8_t unpack_ul_config_harq_info_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel11_t *harq_info_rel11 = (nfapi_ul_config_harq_information_rel11_t *)tlv; + return (pull8(ppReadPackedMsg, &harq_info_rel11->num_ant_ports, end) && + pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_0, end) && + pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_1, end) && + pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_2, end) && + pull16(ppReadPackedMsg, &harq_info_rel11->n_pucch_2_3, end)); +} + +static uint8_t unpack_ul_config_harq_info_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_harq_information_rel13_t *harq_info_rel13 = (nfapi_ul_config_harq_information_rel13_t *)tlv; + return (pull16(ppReadPackedMsg, &harq_info_rel13->harq_size_2, end) && + pull8(ppReadPackedMsg, &harq_info_rel13->starting_prb, end) && + pull8(ppReadPackedMsg, &harq_info_rel13->n_prb, end) && + pull8(ppReadPackedMsg, &harq_info_rel13->cdm_index, end) && + pull8(ppReadPackedMsg, &harq_info_rel13->n_srs, end)); +} + + +static uint8_t unpack_ul_config_srs_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_srs_pdu_rel8_t *srs_pdu_rel8 = (nfapi_ul_config_srs_pdu_rel8_t *)tlv; + return (pull32(ppReadPackedMsg, &srs_pdu_rel8->handle, end) && + pull16(ppReadPackedMsg, &srs_pdu_rel8->size, end) && + pull16(ppReadPackedMsg, &srs_pdu_rel8->rnti, end) && + pull8(ppReadPackedMsg, &srs_pdu_rel8->srs_bandwidth, end) && + pull8(ppReadPackedMsg, &srs_pdu_rel8->frequency_domain_position, end) && + pull8(ppReadPackedMsg, &srs_pdu_rel8->srs_hopping_bandwidth, end) && + pull8(ppReadPackedMsg, &srs_pdu_rel8->transmission_comb, end) && + pull16(ppReadPackedMsg, &srs_pdu_rel8->i_srs, end) && + pull8(ppReadPackedMsg, &srs_pdu_rel8->sounding_reference_cyclic_shift, end)); +} + +static uint8_t unpack_ul_config_srs_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_srs_pdu_rel10_t *srs_pdu_rel10 = (nfapi_ul_config_srs_pdu_rel10_t *)tlv; + return pull8(ppReadPackedMsg, &srs_pdu_rel10->antenna_port, end); +} + +static uint8_t unpack_ul_config_srs_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_srs_pdu_rel13_t *srs_pdu_rel13 = (nfapi_ul_config_srs_pdu_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &srs_pdu_rel13->number_of_combs, end)); +} + +static uint8_t unpack_ul_nb_harq_info_rel13_fdd_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_nb_harq_information_rel13_fdd_t *nb_harq_info_fdd_rel13 = (nfapi_ul_config_nb_harq_information_rel13_fdd_t *)tlv; + return (pull8(ppReadPackedMsg, &nb_harq_info_fdd_rel13->harq_ack_resource, end)); +} + +static uint8_t unpack_ul_config_nulsch_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_nulsch_pdu_rel13_t *nulsch_pdu_rel13 = (nfapi_ul_config_nulsch_pdu_rel13_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &nulsch_pdu_rel13->nulsch_format, end) && + pull32(ppReadPackedMsg, &nulsch_pdu_rel13->handle, end) && + pull16(ppReadPackedMsg, &nulsch_pdu_rel13->size, end) && + pull16(ppReadPackedMsg, &nulsch_pdu_rel13->rnti, end) && + pull8(ppReadPackedMsg, &nulsch_pdu_rel13->subcarrier_indication, end) && + pull8(ppReadPackedMsg, &nulsch_pdu_rel13->resource_assignment, end) && + pull8(ppReadPackedMsg, &nulsch_pdu_rel13->mcs, end) && + pull8(ppReadPackedMsg, &nulsch_pdu_rel13->redudancy_version, end) && + pull8(ppReadPackedMsg, &nulsch_pdu_rel13->repetition_number, end) && + pull8(ppReadPackedMsg, &nulsch_pdu_rel13->new_data_indication, end) && + pull8(ppReadPackedMsg, &nulsch_pdu_rel13->n_srs, end) && + pull16(ppReadPackedMsg, &nulsch_pdu_rel13->scrambling_sequence_initialization_cinit, end) && + pull16(ppReadPackedMsg, &nulsch_pdu_rel13->sf_idx, end))) + return 0; + + unpack_tlv_t unpack_fns[] = { + { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL8_TAG, &nulsch_pdu_rel13->ue_information.ue_information_rel8, &unpack_ul_config_ue_info_rel8_value}, + { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL11_TAG, &nulsch_pdu_rel13->ue_information.ue_information_rel11, &unpack_ul_config_ue_info_rel11_value}, + { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL13_TAG, &nulsch_pdu_rel13->ue_information.ue_information_rel13, &unpack_ul_config_ue_info_rel13_value}, + { NFAPI_UL_CONFIG_REQUEST_NB_HARQ_INFORMATION_REL13_FDD_TAG, &nulsch_pdu_rel13->nb_harq_information.nb_harq_information_rel13_fdd, &unpack_ul_nb_harq_info_rel13_fdd_value}, + }; + return unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, 0, 0); +} + +static uint8_t unpack_ul_config_nrach_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_config_nrach_pdu_rel13_t *nrach_pdu_rel13 = (nfapi_ul_config_nrach_pdu_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &nrach_pdu_rel13->nprach_config_0, end) && + pull8(ppReadPackedMsg, &nrach_pdu_rel13->nprach_config_1, end) && + pull8(ppReadPackedMsg, &nrach_pdu_rel13->nprach_config_2, end)); +} + + +static uint8_t unpack_ul_config_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { +#define UL_CONFIG_ULSCH_PDU_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL8_TAG, &_pdu.ulsch_pdu_rel8, &unpack_ul_config_ulsch_pdu_rel8_value}, \ + { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL10_TAG, &_pdu.ulsch_pdu_rel10, &unpack_ul_config_ulsch_pdu_rel10_value}, \ + { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL11_TAG, &_pdu.ulsch_pdu_rel11, &unpack_ul_config_ulsch_pdu_rel11_value}, \ + { NFAPI_UL_CONFIG_REQUEST_ULSCH_PDU_REL13_TAG, &_pdu.ulsch_pdu_rel13, &unpack_ul_config_ulsch_pdu_rel13_value}, +#define UL_CONFIG_CQI_RI_INFO_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL8_TAG, &_pdu.cqi_ri_information_rel8, &unpack_ul_config_cqi_ri_info_rel8_value}, \ + { NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL9_TAG, &_pdu.cqi_ri_information_rel9, &unpack_ul_config_cqi_ri_info_rel9_value}, \ + { NFAPI_UL_CONFIG_REQUEST_CQI_RI_INFORMATION_REL13_TAG, &_pdu.cqi_ri_information_rel13, &unpack_ul_config_cqi_ri_info_rel13_value}, +#define UL_CONFIG_ULSCH_HARQ_INFO_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL10_TAG, &_pdu.harq_information_rel10, &unpack_ul_config_ulsch_harq_info_rel10_value},\ + { NFAPI_UL_CONFIG_REQUEST_ULSCH_HARQ_INFORMATION_REL13_TAG, &_pdu.harq_information_rel13, &unpack_ul_config_ulsch_harq_info_rel13_value}, +#define UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_INITIAL_TRANSMISSION_PARAMETERS_REL8_TAG, &_pdu.initial_transmission_parameters_rel8, &unpack_ul_config_cqi_init_tx_params_rel8_value}, +#define UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL8_TAG, &_pdu.ue_information_rel8, &unpack_ul_config_ue_info_rel8_value}, \ + { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL11_TAG, &_pdu.ue_information_rel11, &unpack_ul_config_ue_info_rel11_value}, \ + { NFAPI_UL_CONFIG_REQUEST_UE_INFORMATION_REL13_TAG, &_pdu.ue_information_rel13, &unpack_ul_config_ue_info_rel13_value}, +#define UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL8_TAG, &_pdu.cqi_information_rel8, &unpack_ul_config_cqi_info_rel8_value}, \ + { NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL10_TAG, &_pdu.cqi_information_rel10, &unpack_ul_config_cqi_info_rel10_value}, \ + { NFAPI_UL_CONFIG_REQUEST_CQI_INFORMATION_REL13_TAG, &_pdu.cqi_information_rel13, &unpack_ul_config_cqi_info_rel13_value}, +#define UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL8_TAG, &_pdu.sr_information_rel8, &unpack_ul_config_sr_info_rel8_value}, \ + { NFAPI_UL_CONFIG_REQUEST_SR_INFORMATION_REL10_TAG, &_pdu.sr_information_rel10, &unpack_ul_config_sr_info_rel10_value}, +#define UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL10_TDD_TAG, &_pdu.harq_information_rel10_tdd, &unpack_ul_config_harq_info_rel10_tdd_value}, \ + { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL8_FDD_TAG, &_pdu.harq_information_rel8_fdd, &unpack_ul_config_harq_info_rel8_fdd_value}, \ + { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL9_FDD_TAG, &_pdu.harq_information_rel9_fdd, &unpack_ul_config_harq_info_rel9_fdd_value}, \ + { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL11_TAG, &_pdu.harq_information_rel11, &unpack_ul_config_harq_info_rel11_value}, \ + { NFAPI_UL_CONFIG_REQUEST_HARQ_INFORMATION_REL13_TAG, &_pdu.harq_information_rel13, &unpack_ul_config_harq_info_rel13_value}, +#define UL_CONFIG_SRS_PDU_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL8_TAG, &_pdu.srs_pdu_rel8, &unpack_ul_config_srs_pdu_rel8_value}, \ + { NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL10_TAG, &_pdu.srs_pdu_rel10, &unpack_ul_config_srs_pdu_rel10_value}, \ + { NFAPI_UL_CONFIG_REQUEST_SRS_PDU_REL13_TAG, &_pdu.srs_pdu_rel13, &unpack_ul_config_srs_pdu_rel13_value}, +#define UL_CONFIG_NULSCH_PDU_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_NULSCH_PDU_REL13_TAG, &_pdu.nulsch_pdu_rel13, &unpack_ul_config_nulsch_pdu_rel13_value}, +#define UL_CONFIG_NRACH_PDU_UNPACK_FNS(_pdu) \ + { NFAPI_UL_CONFIG_REQUEST_NRACH_PDU_REL13_TAG, &_pdu.nrach_pdu_rel13, &unpack_ul_config_nrach_pdu_rel13_value}, + nfapi_ul_config_request_body_t *value = (nfapi_ul_config_request_body_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &value->number_of_pdus, end) && + pull8(ppReadPackedMsg, &value->rach_prach_frequency_resources, end) && + pull8(ppReadPackedMsg, &value->srs_present, end))) + return 0; + + if(value->number_of_pdus > NFAPI_UL_CONFIG_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of ul config pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_UL_CONFIG_MAX_PDU); + return 0; + } + + if(value->number_of_pdus > 0) { + value->ul_config_pdu_list = (nfapi_ul_config_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_ul_config_request_pdu_t) * value->number_of_pdus, config); + + if(value->ul_config_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate ul config pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); + return 0; + } + } else { + value->ul_config_pdu_list = 0; + } + + uint16_t i; + uint16_t total_number_of_pdus = value->number_of_pdus; + + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_ul_config_request_pdu_t *pdu = &(value->ul_config_pdu_list[i]); + + if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && + pull8(ppReadPackedMsg, &pdu->pdu_size, end))) + return 0; + + uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; + + if(packedPduEnd > end) { + // pdu end is past buffer end + return 0; + } + + switch(pdu->pdu_type) { + case NFAPI_UL_CONFIG_ULSCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_pdu) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_cqi_ri_pdu.ulsch_pdu) + UL_CONFIG_CQI_RI_INFO_UNPACK_FNS(pdu->ulsch_cqi_ri_pdu.cqi_ri_information) + UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(pdu->ulsch_cqi_ri_pdu.initial_transmission_parameters) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_harq_pdu.ulsch_pdu) + UL_CONFIG_ULSCH_HARQ_INFO_UNPACK_FNS(pdu->ulsch_harq_pdu.harq_information) + UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(pdu->ulsch_harq_pdu.initial_transmission_parameters) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu) + UL_CONFIG_CQI_RI_INFO_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.cqi_ri_information) + UL_CONFIG_ULSCH_HARQ_INFO_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.harq_information) + UL_CONFIG_INIT_TX_PARAMS_UNPACK_FNS(pdu->ulsch_cqi_harq_ri_pdu.initial_transmission_parameters) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_pdu.ue_information) + UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_pdu.cqi_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_sr_pdu.ue_information) + UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_sr_pdu.sr_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_harq_pdu.ue_information) + UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_harq_pdu.harq_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_sr_harq_pdu.ue_information) + UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_sr_harq_pdu.sr_information) + UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_sr_harq_pdu.harq_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_harq_pdu.ue_information) + UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_harq_pdu.cqi_information) + UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_cqi_harq_pdu.harq_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_sr_pdu.ue_information) + UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_sr_pdu.cqi_information) + UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_cqi_sr_pdu.sr_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.ue_information) + UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.cqi_information) + UL_CONFIG_UCI_SR_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.sr_information) + UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->uci_cqi_sr_harq_pdu.harq_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_SRS_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_SRS_PDU_UNPACK_FNS(pdu->srs_pdu) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_HARQ_BUFFER_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_UCI_UE_INFO_UNPACK_FNS(pdu->harq_buffer_pdu.ue_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_ULSCH_UCI_CSI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_uci_csi_pdu.ulsch_pdu) + UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->ulsch_uci_csi_pdu.csi_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_uci_harq_pdu.ulsch_pdu) + UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->ulsch_uci_harq_pdu.harq_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_ULSCH_PDU_UNPACK_FNS(pdu->ulsch_csi_uci_harq_pdu.ulsch_pdu) + UL_CONFIG_UCI_CQI_INFO_UNPACK_FNS(pdu->ulsch_csi_uci_harq_pdu.csi_information) + UL_CONFIG_UCI_HARQ_INFO_UNPACK_FNS(pdu->ulsch_csi_uci_harq_pdu.harq_information) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_NULSCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_NULSCH_PDU_UNPACK_FNS(pdu->nulsch_pdu) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_UL_CONFIG_NRACH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + UL_CONFIG_NRACH_PDU_UNPACK_FNS(pdu->nrach_pdu) + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + } + } + + return 1; +} + + +static uint8_t unpack_ul_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_ul_config_request_t *pNfapiMsg = (nfapi_ul_config_request_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_UL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->ul_config_request_body, &unpack_ul_config_request_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_hi_dci0_hi_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_hi_dci0_hi_pdu_rel8_t *hi_pdu_rel8 = (nfapi_hi_dci0_hi_pdu_rel8_t *)tlv; + return( pull8(ppReadPackedMsg, &hi_pdu_rel8->resource_block_start, end) && + pull8(ppReadPackedMsg, &hi_pdu_rel8->cyclic_shift_2_for_drms, end) && + pull8(ppReadPackedMsg, &hi_pdu_rel8->hi_value, end) && + pull8(ppReadPackedMsg, &hi_pdu_rel8->i_phich, end) && + pull16(ppReadPackedMsg, &hi_pdu_rel8->transmission_power, end)); +} + +static uint8_t unpack_hi_dci0_hi_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_hi_dci0_hi_pdu_rel10_t *hi_pdu_rel10 = (nfapi_hi_dci0_hi_pdu_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &hi_pdu_rel10->flag_tb2, end) && + pull8(ppReadPackedMsg, &hi_pdu_rel10->hi_value_2, end)); +} + +static uint8_t unpack_hi_dci0_dci_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_hi_dci0_dci_pdu_rel8_t *dci_pdu_rel8 = (nfapi_hi_dci0_dci_pdu_rel8_t *)tlv; + return (pull8(ppReadPackedMsg, &dci_pdu_rel8->dci_format, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->cce_index, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->aggregation_level, end) && + pull16(ppReadPackedMsg, &dci_pdu_rel8->rnti, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->resource_block_start, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->number_of_resource_block, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->mcs_1, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->cyclic_shift_2_for_drms, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->frequency_hopping_enabled_flag, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->frequency_hopping_bits, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->new_data_indication_1, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->ue_tx_antenna_seleciton, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->tpc, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->cqi_csi_request, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->ul_index, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel8->dl_assignment_index, end) && + pull32(ppReadPackedMsg, &dci_pdu_rel8->tpc_bitmap, end) && + pull16(ppReadPackedMsg, &dci_pdu_rel8->transmission_power, end)); +} + +static uint8_t unpack_hi_dci0_dci_pdu_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_hi_dci0_dci_pdu_rel10_t *dci_pdu_rel10 = (nfapi_hi_dci0_dci_pdu_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &dci_pdu_rel10->cross_carrier_scheduling_flag, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->carrier_indicator, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->size_of_cqi_csi_feild, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->srs_flag, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->srs_request, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->resource_allocation_flag, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->resource_allocation_type, end) && + pull32(ppReadPackedMsg, &dci_pdu_rel10->resource_block_coding, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->mcs_2, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->new_data_indication_2, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->number_of_antenna_ports, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->tpmi, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->total_dci_length_including_padding, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel10->n_ul_rb, end)); +} + +static uint8_t unpack_hi_dci0_dci_pdu_rel12_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_hi_dci0_dci_pdu_rel12_t *dci_pdu_rel12 = (nfapi_hi_dci0_dci_pdu_rel12_t *)tlv; + return ( pull8(ppReadPackedMsg, &dci_pdu_rel12->pscch_resource, end) && + pull8(ppReadPackedMsg, &dci_pdu_rel12->time_resource_pattern, end)); +} + +static uint8_t unpack_hi_dci0_mpdcch_dci_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *value = (nfapi_hi_dci0_mpdcch_dci_pdu_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &value->mpdcch_narrowband, end) && + pull8(ppReadPackedMsg, &value->number_of_prb_pairs, end) && + pull8(ppReadPackedMsg, &value->resource_block_assignment, end) && + pull8(ppReadPackedMsg, &value->mpdcch_transmission_type, end) && + pull8(ppReadPackedMsg, &value->start_symbol, end) && + pull8(ppReadPackedMsg, &value->ecce_index, end) && + pull8(ppReadPackedMsg, &value->aggreagation_level, end) && + pull8(ppReadPackedMsg, &value->rnti_type, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull8(ppReadPackedMsg, &value->ce_mode, end) && + pull16(ppReadPackedMsg, &value->drms_scrambling_init, end) && + pull16(ppReadPackedMsg, &value->initial_transmission_sf_io, end) && + pull16(ppReadPackedMsg, &value->transmission_power, end) && + pull8(ppReadPackedMsg, &value->dci_format, end) && + pull8(ppReadPackedMsg, &value->resource_block_start, end) && + pull8(ppReadPackedMsg, &value->number_of_resource_blocks, end) && + pull8(ppReadPackedMsg, &value->mcs, end) && + pull8(ppReadPackedMsg, &value->pusch_repetition_levels, end) && + pull8(ppReadPackedMsg, &value->frequency_hopping_flag, end) && + pull8(ppReadPackedMsg, &value->new_data_indication, end) && + pull8(ppReadPackedMsg, &value->harq_process, end) && + pull8(ppReadPackedMsg, &value->redudency_version, end) && + pull8(ppReadPackedMsg, &value->tpc, end) && + pull8(ppReadPackedMsg, &value->csi_request, end) && + pull8(ppReadPackedMsg, &value->ul_inex, end) && + pull8(ppReadPackedMsg, &value->dai_presence_flag, end) && + pull8(ppReadPackedMsg, &value->dl_assignment_index, end) && + pull8(ppReadPackedMsg, &value->srs_request, end) && + pull8(ppReadPackedMsg, &value->dci_subframe_repetition_number, end) && + pull32(ppReadPackedMsg, &value->tcp_bitmap, end) && + pull8(ppReadPackedMsg, &value->total_dci_length_include_padding, end) && + pull8(ppReadPackedMsg, &value->number_of_tx_antenna_ports, end) && + pullarray16(ppReadPackedMsg, value->precoding_value, NFAPI_MAX_TX_PHYSICAL_ANTENNA_PORTS, value->number_of_tx_antenna_ports, end)); +} + +static uint8_t unpack_hi_dci0_npdcch_dci_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *value = (nfapi_hi_dci0_npdcch_dci_pdu_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &value->ncce_index, end) && + pull8(ppReadPackedMsg, &value->aggregation_level, end) && + pull8(ppReadPackedMsg, &value->start_symbol, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull8(ppReadPackedMsg, &value->scrambling_reinitialization_batch_index, end) && + pull8(ppReadPackedMsg, &value->nrs_antenna_ports_assumed_by_the_ue, end) && + pull8(ppReadPackedMsg, &value->subcarrier_indication, end) && + pull8(ppReadPackedMsg, &value->resource_assignment, end) && + pull8(ppReadPackedMsg, &value->scheduling_delay, end) && + pull8(ppReadPackedMsg, &value->mcs, end) && + pull8(ppReadPackedMsg, &value->redudancy_version, end) && + pull8(ppReadPackedMsg, &value->repetition_number, end) && + pull8(ppReadPackedMsg, &value->new_data_indicator, end) && + pull8(ppReadPackedMsg, &value->dci_subframe_repetition_number, end)); +} + +static uint8_t unpack_hi_dci0_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_hi_dci0_request_body_t *value = (nfapi_hi_dci0_request_body_t *)tlv; + + if(!(pull16(ppReadPackedMsg, &value->sfnsf, end) && + pull8(ppReadPackedMsg, &value->number_of_dci, end) && + pull8(ppReadPackedMsg, &value->number_of_hi, end))) + return 0; + + uint8_t totalNumPdus = value->number_of_hi + value->number_of_dci; + + if(totalNumPdus > NFAPI_HI_DCI0_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of dci0 pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, totalNumPdus, NFAPI_HI_DCI0_MAX_PDU); + return 0; + } + + if(totalNumPdus > 0) { + value->hi_dci0_pdu_list = (nfapi_hi_dci0_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_hi_dci0_request_pdu_t) * totalNumPdus, config); + + if(value->hi_dci0_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate hi dci0 pdu list (count:%d)\n", __FUNCTION__, totalNumPdus); + return 0; + } + } else { + value->hi_dci0_pdu_list = 0; + } + + uint8_t i; + + for(i = 0; i < totalNumPdus; ++i) { + nfapi_hi_dci0_request_pdu_t *pdu = &(value->hi_dci0_pdu_list[i]); + + if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && + pull8(ppReadPackedMsg, &pdu->pdu_size, end))) + return 0; + + uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; + + if(packedPduEnd > end) { + // pdu end if past buffer end + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s pdu size to big %d %d\n", __FUNCTION__, packedPduEnd, end); + return 0; + } + + switch(pdu->pdu_type) { + case NFAPI_HI_DCI0_HI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_HI_DCI0_REQUEST_HI_PDU_REL8_TAG, &pdu->hi_pdu.hi_pdu_rel8, &unpack_hi_dci0_hi_pdu_rel8_value}, + { NFAPI_HI_DCI0_REQUEST_HI_PDU_REL10_TAG, &pdu->hi_pdu.hi_pdu_rel10, &unpack_hi_dci0_hi_pdu_rel10_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_HI_DCI0_DCI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL8_TAG, &pdu->dci_pdu.dci_pdu_rel8, &unpack_hi_dci0_dci_pdu_rel8_value}, + { NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL10_TAG, &pdu->dci_pdu.dci_pdu_rel10, &unpack_hi_dci0_dci_pdu_rel10_value}, + { NFAPI_HI_DCI0_REQUEST_DCI_PDU_REL12_TAG, &pdu->dci_pdu.dci_pdu_rel12, &unpack_hi_dci0_dci_pdu_rel12_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_HI_DCI0_EPDCCH_DCI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL8_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel8, &unpack_hi_dci0_dci_pdu_rel8_value}, + { NFAPI_HI_DCI0_REQUEST_EPDCCH_DCI_PDU_REL10_TAG, &pdu->epdcch_dci_pdu.epdcch_dci_pdu_rel10, &unpack_hi_dci0_dci_pdu_rel10_value}, + { NFAPI_HI_DCI0_REQUEST_EPDCCH_PARAMETERS_REL11_TAG, &pdu->epdcch_dci_pdu.epdcch_parameters_rel11, &unpack_dl_config_epdcch_params_rel11_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_HI_DCI0_MPDCCH_DCI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_HI_DCI0_REQUEST_MPDCCH_DCI_PDU_REL13_TAG, &pdu->mpdcch_dci_pdu.mpdcch_dci_pdu_rel13, &unpack_hi_dci0_mpdcch_dci_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_HI_DCI0_NPDCCH_DCI_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_HI_DCI0_REQUEST_NPDCCH_DCI_PDU_REL13_TAG, &pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13, &unpack_hi_dci0_npdcch_dci_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid pdu type %d \n", pdu->pdu_type ); + } + break; + }; + } + + return 1; +} +//unpack_ul_dci_pdu_list_value + +static uint8_t unpack_ul_dci_pdu_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) +{ + nfapi_nr_ul_dci_request_pdus_t* value = (nfapi_nr_ul_dci_request_pdus_t*)msg; + for(uint8_t i = 0; i < MAX_DCI_CORESET; ++i) + { + if(!(pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].RNTI, end) && + pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingId, end) && + + pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].ScramblingRNTI, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].CceIndex, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].AggregationLevel, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].beta_PDCCH_1_0, end) && + + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].powerControlOffsetSS, end) && + pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, end) && + + pullarray8(ppReadPackedMsg, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].Payload, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, value->pdcch_pdu.pdcch_pdu_rel15.dci_pdu[i].PayloadSizeBits, end))) + + return 0; + } + + return (pull16(ppReadPackedMsg, &value->PDUType, end) && + pull16(ppReadPackedMsg, &value->PDUSize, end) && + pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.BWPSize, end) && + pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.BWPStart, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.SubcarrierSpacing, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.CyclicPrefix, end) && + + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.StartSymbolIndex, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.DurationSymbols, end) && + pullarray8(ppReadPackedMsg, value->pdcch_pdu.pdcch_pdu_rel15.FreqDomainResource, 6, 6, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.CceRegMappingType, end) && + + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.RegBundleSize, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.InterleaverSize, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.CoreSetType, end) && + pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.ShiftIndex, end) && + pull8(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.precoderGranularity, end) && + pull16(ppReadPackedMsg, &value->pdcch_pdu.pdcch_pdu_rel15.numDlDci, end)); + +} + +static uint8_t unpack_ul_dci_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nr_ul_dci_request_t *pNfapiMsg = (nfapi_nr_ul_dci_request_t *)msg; + + if (!(pull16(ppReadPackedMsg, &pNfapiMsg->SFN, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && + pull8(ppReadPackedMsg, &pNfapiMsg->numPdus, end) + )) + return 0; + + for(int i=0; i< pNfapiMsg->numPdus; i++) { + if (!unpack_ul_dci_pdu_list_value(ppReadPackedMsg, end, &pNfapiMsg->ul_dci_pdu_list[i])) + return 0; + } + + return 1; +} + +static uint8_t unpack_hi_dci0_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_hi_dci0_request_t *pNfapiMsg = (nfapi_hi_dci0_request_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_HI_DCI0_REQUEST_BODY_TAG, &pNfapiMsg->hi_dci0_request_body, &unpack_hi_dci0_request_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} +static uint8_t unpack_tx_data_pdu_list_value(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg) { + nfapi_nr_pdu_t *pNfapiMsg = (nfapi_nr_pdu_t *)msg; + + if(!(pull32(ppReadPackedMsg, &pNfapiMsg->num_TLV, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->PDU_index, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->PDU_length, end) + )) + return 0; + + uint16_t i = 0; + uint16_t total_number_of_tlvs = pNfapiMsg->num_TLV; + + for(; i < total_number_of_tlvs; ++i) { + if (!(pull16(ppReadPackedMsg, &pNfapiMsg->TLVs[i].length, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->TLVs[i].tag, end))) + return 0; + + switch(pNfapiMsg->TLVs[i].tag) { + case 0: { + if(!pullarray32(ppReadPackedMsg, pNfapiMsg->TLVs[i].value.direct, 16384, pNfapiMsg->TLVs[i].length, end)) + return 0; + + break; + } + + case 1: { + if(!pullarray32(ppReadPackedMsg, pNfapiMsg->TLVs[i].value.ptr, pNfapiMsg->TLVs[i].length, pNfapiMsg->TLVs[i].length, end)) + return 0; + + break; + } + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : Invalid tag value %d \n", pNfapiMsg->TLVs[i].tag ); + break; + } + } + } + + return 1; +} + +static uint8_t unpack_tx_data_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nr_tx_data_request_t *pNfapiMsg = (nfapi_nr_tx_data_request_t *)msg; + + if(!(pull16(ppReadPackedMsg, &pNfapiMsg->SFN, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->Slot, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->Number_of_PDUs, end))) + return 0; + + for(int i=0; i< pNfapiMsg->Number_of_PDUs; i++) { + if (!unpack_tx_data_pdu_list_value(ppReadPackedMsg, end, &pNfapiMsg->pdu_list[i])) + return 0; + } + + return 1; +} + +static uint8_t unpack_tx_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + uint8_t proceed = 1; + nfapi_tx_request_t *pNfapiMsg = (nfapi_tx_request_t *)msg; + + if(pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) == 0) + return 0; + + while (((uint8_t *)(*ppReadPackedMsg) < end) && proceed) { + nfapi_tl_t generic_tl; + + if(unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) + return 0; + + switch(generic_tl.tag) { + case NFAPI_TX_REQUEST_BODY_TAG: { + pNfapiMsg->tx_request_body.tl = generic_tl; + + if( pull16(ppReadPackedMsg, &pNfapiMsg->tx_request_body.number_of_pdus, end) == 0) + return 0; + + if(pNfapiMsg->tx_request_body.number_of_pdus > NFAPI_TX_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of tx pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, pNfapiMsg->tx_request_body.number_of_pdus, NFAPI_TX_MAX_PDU); + return 0; + } + + if(pNfapiMsg->tx_request_body.number_of_pdus > 0) { + pNfapiMsg->tx_request_body.tx_pdu_list = (nfapi_tx_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_tx_request_pdu_t) * pNfapiMsg->tx_request_body.number_of_pdus, config); + + if(pNfapiMsg->tx_request_body.tx_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate tx pdu list (count:%d)\n", __FUNCTION__, pNfapiMsg->tx_request_body.number_of_pdus); + return 0; + } + } else { + pNfapiMsg->tx_request_body.tx_pdu_list = 0; + } + + uint16_t i; + uint16_t totalNumPdus = pNfapiMsg->tx_request_body.number_of_pdus; + + for(i = 0; i < totalNumPdus; ++i) { + nfapi_tx_request_pdu_t *pdu = &(pNfapiMsg->tx_request_body.tx_pdu_list[i]); + + if (pdu) { + uint16_t length = 0; + uint16_t index = 0; + + if(!(pull16(ppReadPackedMsg, &length, end) && + pull16(ppReadPackedMsg, &index, end))) + return 0; + + pdu->pdu_length = length; + pdu->pdu_index = index; + // TODO : May need to rethink this bit + pdu->num_segments = 1; + pdu->segments[0].segment_length = pdu->pdu_length; + pdu->segments[0].segment_data = nfapi_p7_allocate(pdu->pdu_length, config); + + if(pdu->segments[0].segment_data) { + if(!pullarray8(ppReadPackedMsg, pdu->segments[0].segment_data, pdu->segments[0].segment_length, pdu->segments[0].segment_length, end)) + return 0; + + if (pdu->segments[0].segment_length == 3) { + NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() BCH? segment_data:%x %x %x\n", __FUNCTION__, + pdu->segments[0].segment_data[0], + pdu->segments[0].segment_data[1], + pdu->segments[0].segment_data[2] + ); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "unpack_tx_request: Failed to allocate pdu (len:%d) %d/%d %d\n", pdu->pdu_length, totalNumPdus, i, pdu->pdu_index); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "NULL pdu\n"); + } + } + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "unpack_tx_request FIXME : Invalid pdu type %d \n", generic_tl.tag ); + } + break; + }; + } + + return 1; +} + +//UNPACK NR UPLINK INDICATION FUNCTIONS + +//SLOT INDICATION + +static uint8_t unpack_nr_slot_indication(uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_nr_slot_indication_scf_t *msg, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_slot_indication_scf_t *pNfapiMsg = (nfapi_nr_slot_indication_scf_t*)msg; + + if (!(pull16(ppReadPackedMsg, &pNfapiMsg->sfn , end) && + pull16(ppReadPackedMsg, &pNfapiMsg->slot , end) + )) + return 0; + +return 1; +} + +//RX DATA INDICATION + +static uint8_t unpack_nr_rx_data_indication_body(void* tlv, uint8_t **ppReadPackedMsg, uint8_t *end) +{ + nfapi_nr_rx_data_pdu_t* value = (nfapi_nr_rx_data_pdu_t*)tlv; + + if(!(pull32(ppReadPackedMsg, &value->handle, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull8(ppReadPackedMsg, &value->harq_id, end) && + pull16(ppReadPackedMsg, &value->pdu_length, end) && + pull8(ppReadPackedMsg, &value->ul_cqi, end) && + pull16(ppReadPackedMsg, &value->timing_advance, end) && + pull16(ppReadPackedMsg, &value->rssi, end) + )) + return 0; + return 1; +} + + +static uint8_t unpack_nr_rx_data_indication(uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_nr_rx_data_indication_t *msg, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_rx_data_indication_t *pNfapiMsg = (nfapi_nr_rx_data_indication_t*)msg; + + if (!(pull16(ppReadPackedMsg, &pNfapiMsg->sfn , end) && + pull16(ppReadPackedMsg, &pNfapiMsg->slot , end) && + pull16(ppReadPackedMsg, &pNfapiMsg->number_of_pdus, end) + )) + return 0; + + for(int i=0; inumber_of_pdus;i++) + { + if(!unpack_nr_rx_data_indication_body(pNfapiMsg->pdu_list, ppReadPackedMsg, end)) + return 0; + } + +return 1; +} + +//NR CRC INDICATION + +static uint8_t unpack_nr_crc_indication_body(nfapi_nr_crc_t* value, uint8_t **ppReadPackedMsg, uint8_t *end) +{ + if(!(pull32(ppReadPackedMsg, &value->handle, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull8(ppReadPackedMsg, &value->harq_id, end) && + pull8(ppReadPackedMsg, &value->tb_crc_status, end) && + pull16(ppReadPackedMsg, &value->num_cb, end) && + //pullarray8(ppReadPackedMsg, value->cb_crc_status, (int)(value->num_cb / 8) + 1, (int)(value->num_cb / 8) + 1, end) && //length is ceil(NumCb/8) + pull8(ppReadPackedMsg, &value->ul_cqi, end) && + pull16(ppReadPackedMsg, &value->timing_advance, end) && + pull16(ppReadPackedMsg, &value->rssi, end) + )) + return 0; + + //memcpy((nfapi_nr_crc_t *)tlv,value,sizeof(nfapi_nr_crc_t)); + + return 1; +} + +static uint8_t unpack_nr_crc_indication(uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_nr_crc_indication_t *msg, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_crc_indication_t *pNfapiMsg = (nfapi_nr_crc_indication_t *) msg; + // pNfapiMsg = (nfapi_nr_crc_indication_t *) malloc(sizeof(nfapi_nr_crc_indication_t)); + // pNfapiMsg->crc_list = (nfapi_nr_crc_t *) malloc(sizeof(nfapi_nr_crc_t)); + + if (!(pull16(ppReadPackedMsg, &pNfapiMsg->sfn, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->slot, end) && + pull16(ppReadPackedMsg, &pNfapiMsg->number_crcs, end) + )) + return 0; + + for(int i=0; inumber_crcs;i++) + { + if(!unpack_nr_crc_indication_body(pNfapiMsg->crc_list,ppReadPackedMsg,end)) + //if(!unpack_nr_crc_indication_body(value,ppReadPackedMsg,end)) + return 0; + } + +return 1; +} + +//SRS INDICATION + +static uint8_t unpack_nr_srs_indication_body(void* tlv, uint8_t **ppReadPackedMsg, uint8_t *end) +{ + nfapi_nr_srs_indication_pdu_t* value = (nfapi_nr_srs_indication_pdu_t*)tlv; + + if(!(pull32(ppReadPackedMsg, &value->handle, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull16(ppReadPackedMsg, &value->timing_advance, end) && + pull8(ppReadPackedMsg, &value->num_symbols, end) && + pull8(ppReadPackedMsg, &value->wide_band_snr, end) && + pull8(ppReadPackedMsg, &value->num_reported_symbols, end) && + pull16(ppReadPackedMsg, &value->reported_symbol_list->num_rbs, end) + )) + return 0; + for(int i = 0; i < value->reported_symbol_list->num_rbs; i++) + { + if(!(pull8(ppReadPackedMsg, &value->reported_symbol_list->rb_list->rb_snr, end) + )) + return 0; + } + return 1; +} + +static uint8_t unpack_nr_srs_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_srs_indication_t *pNfapiMsg = (nfapi_nr_srs_indication_t*)msg; + + if (!(pull16(ppReadPackedMsg,&pNfapiMsg->sfn , end) && + pull16(ppReadPackedMsg,&pNfapiMsg->slot , end) && + pull8(ppReadPackedMsg,&pNfapiMsg->number_of_pdus, end) + )) + return 0; + + for(int i=0; inumber_of_pdus;i++) + { + if(!unpack_nr_srs_indication_body(&pNfapiMsg->pdu_list,ppReadPackedMsg,end)) + return 0; + } + +return 1; +} + +//NR RACH + +static uint8_t unpack_nr_rach_indication_body(nfapi_nr_prach_indication_pdu_t* tlv, uint8_t **ppReadPackedMsg, uint8_t *end) +{ + nfapi_nr_prach_indication_pdu_t* value = (nfapi_nr_prach_indication_pdu_t*)tlv; + + if(!(pull16(ppReadPackedMsg, &value->phy_cell_id, end) && + pull8(ppReadPackedMsg, &value->symbol_index, end) && + pull8(ppReadPackedMsg, &value->slot_index, end) && + pull8(ppReadPackedMsg, &value->freq_index, end) && + pull8(ppReadPackedMsg, &value->avg_rssi, end) && + pull8(ppReadPackedMsg, &value->avg_snr, end) && + pull8(ppReadPackedMsg, &value->num_preamble, end) + )) + return 0; + value->preamble_list = (nfapi_nr_prach_indication_preamble_t*) malloc(sizeof(nfapi_nr_prach_indication_preamble_t) * value->num_preamble); + for(int i = 0; i < value->num_preamble; i++) + { + if(!(pull8(ppReadPackedMsg, &value->preamble_list->preamble_index, end) && + pull16(ppReadPackedMsg, &value->preamble_list->timing_advance, end) && + pull32(ppReadPackedMsg, &value->preamble_list->preamble_pwr, end) + )) + return 0; + } + return 1; +} + +static uint8_t unpack_nr_rach_indication(uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_nr_rach_indication_t* msg, nfapi_p7_codec_config_t* config) { + + nfapi_nr_rach_indication_t *pNfapiMsg = (nfapi_nr_rach_indication_t*)msg; + if (!(pull16(ppReadPackedMsg, &pNfapiMsg->sfn , end) && + pull16(ppReadPackedMsg, &pNfapiMsg->slot , end) && + pull8(ppReadPackedMsg, &pNfapiMsg->number_of_pdus, end) + )) + return 0; + pNfapiMsg->pdu_list = (nfapi_nr_prach_indication_pdu_t*) malloc(sizeof(nfapi_nr_prach_indication_pdu_t) * pNfapiMsg->number_of_pdus); + for(int i=0; i< pNfapiMsg->number_of_pdus;i++) + { + if(!unpack_nr_rach_indication_body(pNfapiMsg->pdu_list,ppReadPackedMsg,end)) + return 0; + } + +return 1; +} + +//NR UCI + +static uint8_t unpack_nr_uci_pucch_0_1(nfapi_nr_uci_pucch_pdu_format_0_1_t* tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_uci_pucch_pdu_format_0_1_t* value = (nfapi_nr_uci_pucch_pdu_format_0_1_t*)tlv; + + // uint8_t *ptr = *ppReadPackedMsg; + // printf("\n Read P7 message uci_0_1 indication unpack: "); + // while(ptr < end){ + // printf(" %d ", *ptr); + // ptr++; + // } + // printf("\n"); + + if(!(pull8(ppReadPackedMsg, &value->pduBitmap, end) && + pull32(ppReadPackedMsg, &value->handle, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull8(ppReadPackedMsg, &value->pucch_format, end) && + pull8(ppReadPackedMsg, &value->ul_cqi, end) && + pull16(ppReadPackedMsg, &value->timing_advance, end) && + pull16(ppReadPackedMsg, &value->rssi, end) + )) + return 0; + if (value->pduBitmap & 0x01) { //SR + value->sr = (nfapi_nr_sr_pdu_0_1_t*) malloc(sizeof(nfapi_nr_sr_pdu_0_1_t)); + if(!(pull8(ppReadPackedMsg, &value->sr->sr_indication, end) && + pull8(ppReadPackedMsg, &value->sr->sr_confidence_level, end) + )) + return 0; + } + + if (((value->pduBitmap >> 1) & 0x01)) { //HARQ + value->harq = (nfapi_nr_harq_pdu_0_1_t*) malloc(sizeof(nfapi_nr_harq_pdu_0_1_t)); + if(!(pull8(ppReadPackedMsg, &value->harq->num_harq, end) && + pull8(ppReadPackedMsg, &value->harq->harq_confidence_level, end) + )) + return 0; + value->harq->harq_list = (nfapi_nr_harq_t*) malloc(sizeof(nfapi_nr_harq_t*) * value->harq->num_harq); + for(int i=0; iharq->num_harq;i++) + { + if(!(pull8(ppReadPackedMsg, &value->harq->harq_list->harq_value, end) + )) + return 0; + } + } + + return 1; +} + + +static uint8_t unpack_nr_uci_pucch_2_3_4(nfapi_nr_uci_pucch_pdu_format_2_3_4_t* tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nr_uci_pucch_pdu_format_2_3_4_t* value = (nfapi_nr_uci_pucch_pdu_format_2_3_4_t*) tlv; + + if(!(pull8(ppReadPackedMsg, &value->pduBitmap, end) && + pull32(ppReadPackedMsg, &value->handle, end) && + pull16(ppReadPackedMsg, &value->rnti, end) && + pull8(ppReadPackedMsg, &value->pucch_format, end) && + pull8(ppReadPackedMsg, &value->ul_cqi, end) && + pull16(ppReadPackedMsg, &value->timing_advance, end) && + pull16(ppReadPackedMsg, &value->rssi, end) + )) + return 0; + if (value->pduBitmap & 0x01) { //SR + if(!(pull16(ppReadPackedMsg, &value->sr.sr_bit_len, end))) + return 0; + + value->sr.sr_payload = (uint8_t*) malloc(sizeof(uint8_t) * ((value->sr.sr_bit_len/8))); + + if(!(pullarray8(ppReadPackedMsg, &value->sr.sr_payload[0], (int)(value->sr.sr_bit_len / 8), (int)(value->sr.sr_bit_len / 8), end))) + return 0; + } + + if (((value->pduBitmap >> 1) & 0x01)) { //HARQ + if(!(pull8(ppReadPackedMsg, &value->harq.harq_crc, end)) && + (pull16(ppReadPackedMsg, &value->harq.harq_bit_len, end))) + return 0; + + value->harq.harq_payload = (uint8_t*) malloc(sizeof(uint8_t) * ((value->harq.harq_bit_len/8 ))); + + if(!(pullarray8(ppReadPackedMsg, value->harq.harq_payload, (int)(value->harq.harq_bit_len / 8), (int)(value->harq.harq_bit_len / 8), end))) + return 0; + } + + if (((value->pduBitmap >> 2) & 0x01)) { //CSI-1 + if(!(pull8(ppReadPackedMsg, &value->csi_part1.csi_part1_crc, end)) && + (pull16(ppReadPackedMsg, &value->csi_part1.csi_part1_bit_len, end))) + return 0; + + value->csi_part1.csi_part1_payload = (uint8_t*) malloc(sizeof(uint8_t) * ((value->csi_part1.csi_part1_bit_len/8))); + +// if(!(pullarray8(ppReadPackedMsg, value->csi_part1.csi_part1_payload, (int)(value->csi_part1.csi_part1_bit_len / 8), (int)(value->csi_part1.csi_part1_bit_len / 8), end))) +// return 0; + } + + if (((value->pduBitmap >> 3) & 0x01)) { //CSI-2 + if(!(pull8(ppReadPackedMsg, &value->csi_part2.csi_part2_crc, end)) && + (pull16(ppReadPackedMsg, &value->csi_part2.csi_part2_bit_len, end))) + return 0; + + value->csi_part2.csi_part2_payload = (uint8_t*) malloc(sizeof(uint8_t) * ((value->csi_part2.csi_part2_bit_len/8 ))); + + if(!(pullarray8(ppReadPackedMsg, value->csi_part2.csi_part2_payload, (int)(value->csi_part2.csi_part2_bit_len / 8) , (int)(value->csi_part2.csi_part2_bit_len / 8) , end))) + return 0; + } + + return 1; +} + +static uint8_t unpack_nr_uci_indication_body(nfapi_nr_uci_t* value, uint8_t **ppReadPackedMsg, uint8_t *end) +{ + if(!(pull16(ppReadPackedMsg, &value->pdu_type, end) && + pull16(ppReadPackedMsg, &value->pdu_size, end) + )) + return 0; + + switch (value->pdu_type) { + case NFAPI_NR_UCI_PUSCH_PDU_TYPE: + printf("Unhandled NFAPI_NR_UCI_PUSCH_PDU_TYPE \n"); + break; + + case NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE: { + nfapi_nr_uci_pucch_pdu_format_0_1_t* uci_pdu = &value->pucch_pdu_format_0_1; + unpack_nr_uci_pucch_0_1(uci_pdu, ppReadPackedMsg, end); + break; + } + case NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE: { + nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu = &value->pucch_pdu_format_2_3_4; + unpack_nr_uci_pucch_2_3_4(uci_pdu, ppReadPackedMsg, end); + break; + } + } + + return 1; +} + +static uint8_t unpack_nr_uci_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t* config) +{ + nfapi_nr_uci_indication_t *pNfapiMsg = (nfapi_nr_uci_indication_t*)msg; + + if (!(pull16(ppReadPackedMsg, &pNfapiMsg->sfn , end) && + pull16(ppReadPackedMsg, &pNfapiMsg->slot , end) && + pull16(ppReadPackedMsg, &pNfapiMsg->num_ucis, end) + )) + return 0; + + for(int i=0; inum_ucis;i++) + { + if(!unpack_nr_uci_indication_body(pNfapiMsg->uci_list,ppReadPackedMsg,end)) + return 0; + } + +return 1; +} + +static uint8_t unpack_ue_release_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + uint8_t proceed = 1; + nfapi_ue_release_request_t *pNfapiMsg = (nfapi_ue_release_request_t *)msg; + + if(pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) == 0) + return 0; + + while (((uint8_t *)(*ppReadPackedMsg) < end) && proceed) { + nfapi_tl_t generic_tl; + + if(unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) + return 0; + + switch(generic_tl.tag) { + case NFAPI_UE_RELEASE_BODY_TAG: { + pNfapiMsg->ue_release_request_body.tl = generic_tl; + + if( pull16(ppReadPackedMsg, &pNfapiMsg->ue_release_request_body.number_of_TLVs, end) == 0) + return 0; + + if(pNfapiMsg->ue_release_request_body.number_of_TLVs > NFAPI_RELEASE_MAX_RNTI) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of relese rnti's exceed maxium (count:%d max:%d)\n", __FUNCTION__, pNfapiMsg->ue_release_request_body.number_of_TLVs, NFAPI_RELEASE_MAX_RNTI); + return 0; + } else { + uint8_t j; + uint16_t num = pNfapiMsg->ue_release_request_body.number_of_TLVs; + + for(j = 0; j < num; ++j) { + if(pull16(ppReadPackedMsg, &pNfapiMsg->ue_release_request_body.ue_release_request_TLVs_list[j].rnti, end) == 0) { + return 0; + } + } + } + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "unpack_ue_release_request FIXME : Invalid type %d \n", generic_tl.tag ); + } + break; + }; + } + + return 1; +} + +static uint8_t unpack_harq_indication_tdd_harq_data_bundling(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_harq_data_bundling_t *value = (nfapi_harq_indication_tdd_harq_data_bundling_t *)tlv; + return (pull8(ppReadPackedMsg, &value->value_0, end) && + pull8(ppReadPackedMsg, &value->value_1, end)); +} + +static uint8_t unpack_harq_indication_tdd_harq_data_multiplexing(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_harq_data_multiplexing_t *value = (nfapi_harq_indication_tdd_harq_data_multiplexing_t *)tlv; + return (pull8(ppReadPackedMsg, &value->value_0, end) && + pull8(ppReadPackedMsg, &value->value_1, end) && + pull8(ppReadPackedMsg, &value->value_2, end) && + pull8(ppReadPackedMsg, &value->value_3, end)); +} +static uint8_t unpack_harq_indication_tdd_harq_data_special_bundling(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_harq_data_special_bundling_t *value = (nfapi_harq_indication_tdd_harq_data_special_bundling_t *)tlv; + return ( pull8(ppReadPackedMsg, &value->value_0, end)); +} +static uint8_t unpack_harq_indication_tdd_harq_data(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_harq_data_t *value = (nfapi_harq_indication_tdd_harq_data_t *)tlv; + return (pull8(ppReadPackedMsg, &value->value_0, end)); +} + +static uint8_t unpack_harq_indication_tdd_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_rel8_t *value = (nfapi_harq_indication_tdd_rel8_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &value->mode, end) && + pull8(ppReadPackedMsg, &value->number_of_ack_nack, end))) + return 0; + + uint8_t result = 0; + + switch(value->mode) { + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: + result = unpack_harq_indication_tdd_harq_data_bundling(&value->harq_data.bundling, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: + result = unpack_harq_indication_tdd_harq_data_multiplexing(&value->harq_data.multiplex, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: + result = unpack_harq_indication_tdd_harq_data_special_bundling(&value->harq_data.special_bundling, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: + result = 1; + break; + + default: + // TODO add error message + return 0; + break; + } + + return result; +} + +static uint8_t unpack_harq_indication_tdd_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_rel9_t *value = (nfapi_harq_indication_tdd_rel9_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &value->mode, end) && + pull8(ppReadPackedMsg, &value->number_of_ack_nack, end))) + return 0; + + if(value->number_of_ack_nack > NFAPI_MAX_NUMBER_ACK_NACK_TDD) { + // TODO : add error message + return 0; + } + + uint16_t idx = 0; + + for(idx = 0; idx < value->number_of_ack_nack; ++idx) { + uint8_t result = 0; + + switch(value->mode) { + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].bundling, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].multiplex, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: + result = unpack_harq_indication_tdd_harq_data_special_bundling(&value->harq_data[idx].special_bundling, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].channel_selection, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_3, ppReadPackedMsg, end); + break; + + default: + // TODO add error message + return 0; + break; + } + + if(result == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_harq_indication_tdd_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_tdd_rel13_t *value = (nfapi_harq_indication_tdd_rel13_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &value->mode, end) && + pull16(ppReadPackedMsg, &value->number_of_ack_nack, end))) + return 0; + + if(value->number_of_ack_nack > NFAPI_MAX_NUMBER_ACK_NACK_TDD) { + // TODO : add error message + return 0; + } + + uint16_t idx = 0; + + for(idx = 0; idx < value->number_of_ack_nack; ++idx) { + uint8_t result = 0; + + switch(value->mode) { + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_BUNDLING: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].bundling, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_MULIPLEXING: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].multiplex, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_SPECIAL_BUNDLING: + result = unpack_harq_indication_tdd_harq_data_special_bundling(&value->harq_data[idx].special_bundling, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_CHANNEL_SELECTION: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].channel_selection, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_3: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_3, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_4: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_4, ppReadPackedMsg, end); + break; + + case NFAPI_HARQ_INDICATION_TDD_HARQ_ACK_NACK_FORMAT_FORMAT_5: + result = unpack_harq_indication_tdd_harq_data(&value->harq_data[idx].format_5, ppReadPackedMsg, end); + break; + + default: + // TODO add error message + return 0; + break; + } + + if(result == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_harq_indication_fdd_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_fdd_rel8_t *value = (nfapi_harq_indication_fdd_rel8_t *)tlv; + return (pull8(ppReadPackedMsg, &value->harq_tb1, end) && + pull8(ppReadPackedMsg, &value->harq_tb2, end)); +} + +static uint8_t unpack_harq_indication_fdd_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_fdd_rel9_t *value = (nfapi_harq_indication_fdd_rel9_t *)tlv; + return (pull8(ppReadPackedMsg, &value->mode, end) && + pull8(ppReadPackedMsg, &value->number_of_ack_nack, end) && + pullarray8(ppReadPackedMsg, value->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL9_MAX, value->number_of_ack_nack, end)); +} + +static uint8_t unpack_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_harq_indication_fdd_rel13_t *value = (nfapi_harq_indication_fdd_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &value->mode, end) && + pull16(ppReadPackedMsg, &value->number_of_ack_nack, end) && + pullarray8(ppReadPackedMsg, value->harq_tb_n, NFAPI_HARQ_ACK_NACK_REL13_MAX, value->number_of_ack_nack, end)); +} + +static uint8_t unpack_ul_cqi_information_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_ul_cqi_information_t *value = (nfapi_ul_cqi_information_t *)tlv; + return (pull8(ppReadPackedMsg, &value->ul_cqi, end) && + pull8(ppReadPackedMsg, &value->channel, end)); +} + + + +static uint8_t unpack_harq_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_harq_indication_body_t *value = (nfapi_harq_indication_body_t *)tlv; + uint8_t *harqBodyEnd = *ppReadPackedMsg + value->tl.length; + + if(harqBodyEnd > end) + return 0; + + if(pull16(ppReadPackedMsg, &value->number_of_harqs, end) == 0) + return 0; + + if(value->number_of_harqs > NFAPI_HARQ_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of harq ind pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_harqs, NFAPI_HARQ_IND_MAX_PDU); + return 0; + } + + value->harq_pdu_list = (nfapi_harq_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_harq_indication_pdu_t) * value->number_of_harqs, config); + + if(value->harq_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate harq ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_harqs); + return 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_harqs; ++i) { + nfapi_harq_indication_pdu_t *pdu = &(value->harq_pdu_list[i]); + + if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) + return 0; + + uint8_t *harqPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; + unpack_tlv_t unpack_fns[] = { + { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, + { NFAPI_HARQ_INDICATION_TDD_REL8_TAG, &pdu->harq_indication_tdd_rel8, &unpack_harq_indication_tdd_rel8_value}, + { NFAPI_HARQ_INDICATION_TDD_REL9_TAG, &pdu->harq_indication_tdd_rel9, &unpack_harq_indication_tdd_rel9_value}, + { NFAPI_HARQ_INDICATION_TDD_REL13_TAG, &pdu->harq_indication_tdd_rel13, &unpack_harq_indication_tdd_rel13_value}, + { NFAPI_HARQ_INDICATION_FDD_REL8_TAG, &pdu->harq_indication_fdd_rel8, &unpack_harq_indication_fdd_rel8_value}, + { NFAPI_HARQ_INDICATION_FDD_REL9_TAG, &pdu->harq_indication_fdd_rel9, &unpack_harq_indication_fdd_rel9_value}, + { NFAPI_HARQ_INDICATION_FDD_REL13_TAG, &pdu->harq_indication_fdd_rel13, &unpack_harq_indication_fdd_rel13_value}, + { NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, &unpack_ul_cqi_information_value} + }; + + if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, harqPduInstanceEnd, 0, 0) == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_harq_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_harq_indication_t *pNfapiMsg = (nfapi_harq_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->harq_indication_body, &unpack_harq_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_crc_indication_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_crc_indication_rel8_t *crc_pdu_rel8 = (nfapi_crc_indication_rel8_t *)tlv; + return ( pull8(ppReadPackedMsg, &crc_pdu_rel8->crc_flag, end) ); +} + +static uint8_t unpack_crc_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_crc_indication_body_t *value = (nfapi_crc_indication_body_t *)tlv; + uint8_t *crcBodyEnd = *ppReadPackedMsg + value->tl.length; + + if(crcBodyEnd > end) + return 0; + + if(pull16(ppReadPackedMsg, &value->number_of_crcs, end) == 0) + return 0; + + if(value->number_of_crcs > NFAPI_CRC_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of crc ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_crcs, NFAPI_CRC_IND_MAX_PDU); + return 0; + } + + if(value->number_of_crcs > 0) { + value->crc_pdu_list = (nfapi_crc_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_crc_indication_pdu_t) * value->number_of_crcs, config); + + if(value->crc_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate crc ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_crcs); + return 0; + } + } else { + value->crc_pdu_list = 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_crcs; ++i) { + nfapi_crc_indication_pdu_t *pdu = &(value->crc_pdu_list[i]); + + if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) + return 0; + + uint8_t *crcPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; + unpack_tlv_t unpack_fns[] = { + { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, + { NFAPI_CRC_INDICATION_REL8_TAG, &pdu->crc_indication_rel8, unpack_crc_indication_rel8_value }, + }; + + if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, crcPduInstanceEnd, 0, 0) == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_crc_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_crc_indication_t *pNfapiMsg = (nfapi_crc_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_CRC_INDICATION_BODY_TAG, &pNfapiMsg->crc_indication_body, &unpack_crc_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_rx_indication_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_rx_indication_rel8_t *value = (nfapi_rx_indication_rel8_t *)tlv; + return (pull16(ppReadPackedMsg, &value->length, end) && + pull16(ppReadPackedMsg, &value->offset, end) && + pull8(ppReadPackedMsg, &value->ul_cqi, end) && + pull16(ppReadPackedMsg, &value->timing_advance, end)); +} +static uint8_t unpack_rx_indication_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_rx_indication_rel9_t *value = (nfapi_rx_indication_rel9_t *)tlv; + return (pull16(ppReadPackedMsg, &value->timing_advance_r9, end)); +} + +static uint8_t unpack_rx_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_rx_indication_body_t *value = (nfapi_rx_indication_body_t *)tlv; + // the rxBodyEnd points to the end of the cqi PDU's + uint8_t *rxBodyEnd = *ppReadPackedMsg + value->tl.length; + uint8_t *rxPduEnd = rxBodyEnd; + uint8_t *numberOfPdusAddress = *ppReadPackedMsg; + + if(rxBodyEnd > end) { + // pdu end is past buffer end + return 0; + } + + if(pull16(ppReadPackedMsg, &value->number_of_pdus, end) == 0) + return 0; + + if(value->number_of_pdus > NFAPI_RX_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of rx ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_RX_IND_MAX_PDU); + return 0; + } + + if(value->number_of_pdus > 0) { + value->rx_pdu_list = (nfapi_rx_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_rx_indication_pdu_t) * value->number_of_pdus, config); + + if(value->rx_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate rx ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); + return 0; + } + } else { + value->rx_pdu_list = 0; + } + + uint8_t i = 0; + nfapi_rx_indication_pdu_t *pdu = 0; + + while((uint8_t *)(*ppReadPackedMsg) < rxBodyEnd && (uint8_t *)(*ppReadPackedMsg) < rxPduEnd) { + nfapi_tl_t generic_tl; + + if( unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) + return 0; + + switch(generic_tl.tag) { + case NFAPI_RX_UE_INFORMATION_TAG: { + pdu = &(value->rx_pdu_list[i++]); + pdu->rx_ue_information.tl = generic_tl; + + if(unpack_rx_ue_information_value(&pdu->rx_ue_information, ppReadPackedMsg, end) == 0) + return 0; + } + break; + + case NFAPI_RX_INDICATION_REL8_TAG: { + if(pdu != 0) { + pdu->rx_indication_rel8.tl = generic_tl; + + if(unpack_rx_indication_rel8_value(&pdu->rx_indication_rel8, ppReadPackedMsg, end) == 0) + return 0; + + if(pdu->rx_indication_rel8.offset > 0) { + // Need to check that the data is within the tlv + if(numberOfPdusAddress + pdu->rx_indication_rel8.offset + pdu->rx_indication_rel8.length <= rxBodyEnd) { + // If this the first pdu set the rxPduEnd + if(numberOfPdusAddress + pdu->rx_indication_rel8.offset < rxPduEnd) { + rxPduEnd = numberOfPdusAddress + pdu->rx_indication_rel8.offset; + + if(rxPduEnd > end) { + // pdu end is past buffer end + return 0; + } + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME: the rx data is outside of the tlv\n"); + } + } + } + } + break; + + case NFAPI_RX_INDICATION_REL9_TAG: { + if(pdu != 0) { + pdu->rx_indication_rel9.tl = generic_tl; + + if(unpack_rx_indication_rel9_value(&pdu->rx_indication_rel9, ppReadPackedMsg, end) == 0) + return 0; + } + } + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "RX_ULSCH.indication Invalid pdu type %d \n", generic_tl.tag ); + } + break; + } + } + + uint8_t idx = 0; + + for(idx = 0; idx < value->number_of_pdus; ++idx) { + if(value->rx_pdu_list[idx].rx_indication_rel8.tl.tag == NFAPI_RX_INDICATION_REL8_TAG) { + uint32_t length = value->rx_pdu_list[idx].rx_indication_rel8.length; + value->rx_pdu_list[idx].data = nfapi_p7_allocate(length, config); + + if(pullarray8(ppReadPackedMsg, value->rx_pdu_list[idx].data, length, length, end) == 0) { + return 0; + } + } + } + + return 1; +} + +static uint8_t unpack_rx_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_rx_indication_t *pNfapiMsg = (nfapi_rx_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_RX_INDICATION_BODY_TAG, &pNfapiMsg->rx_indication_body, &unpack_rx_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_preamble_pdu_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_preamble_pdu_rel8_t *preamble_pdu_rel8 = (nfapi_preamble_pdu_rel8_t *)tlv; + return (pull16(ppReadPackedMsg, &preamble_pdu_rel8->rnti, end) && + pull8(ppReadPackedMsg, &preamble_pdu_rel8->preamble, end) && + pull16(ppReadPackedMsg, &preamble_pdu_rel8->timing_advance, end)); +} + +static uint8_t unpack_preamble_pdu_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_preamble_pdu_rel9_t *preamble_pdu_rel9 = (nfapi_preamble_pdu_rel9_t *)tlv; + return pull16(ppReadPackedMsg, &preamble_pdu_rel9->timing_advance_r9, end); +} + +static uint8_t unpack_preamble_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_preamble_pdu_rel13_t *preamble_pdu_rel13 = (nfapi_preamble_pdu_rel13_t *)tlv; + return pull8(ppReadPackedMsg, &preamble_pdu_rel13->rach_resource_type, end); +} + +static uint8_t unpack_rach_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_rach_indication_body_t *value = (nfapi_rach_indication_body_t *)tlv; + uint8_t *rachBodyEnd = *ppReadPackedMsg + value->tl.length; + + if(rachBodyEnd > end) + return 0; + + if(pull16(ppReadPackedMsg, &value->number_of_preambles, end) == 0) + return 0; + + if(value->number_of_preambles > NFAPI_PREAMBLE_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of preamble du's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_preambles, NFAPI_PREAMBLE_MAX_PDU); + return 0; + } + + if(value->number_of_preambles > 0) { + value->preamble_list = (nfapi_preamble_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_preamble_pdu_t) * value->number_of_preambles, config); + + if(value->preamble_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate preamble pdu list (count:%d)\n", __FUNCTION__, value->number_of_preambles); + return 0; + } + } else { + value->preamble_list = 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_preambles; ++i) { + nfapi_preamble_pdu_t *pdu = &(value->preamble_list[i]); + + if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) + return 0; + + uint8_t *preamblePduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; + unpack_tlv_t unpack_fns[] = { + { NFAPI_PREAMBLE_REL8_TAG, &pdu->preamble_rel8, unpack_preamble_pdu_rel8_value }, + { NFAPI_PREAMBLE_REL9_TAG, &pdu->preamble_rel9, unpack_preamble_pdu_rel9_value }, + { NFAPI_PREAMBLE_REL13_TAG, &pdu->preamble_rel13, unpack_preamble_pdu_rel13_value }, + }; + + if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, preamblePduInstanceEnd, 0, 0) == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_rach_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_rach_indication_t *pNfapiMsg = (nfapi_rach_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_RACH_INDICATION_BODY_TAG, &pNfapiMsg->rach_indication_body, &unpack_rach_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_srs_indication_fdd_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_srs_indication_fdd_rel8_t *srs_pdu_fdd_rel8 = (nfapi_srs_indication_fdd_rel8_t *)tlv; + + if(!(pull16(ppReadPackedMsg, &srs_pdu_fdd_rel8->doppler_estimation, end) && + pull16(ppReadPackedMsg, &srs_pdu_fdd_rel8->timing_advance, end) && + pull8(ppReadPackedMsg, &srs_pdu_fdd_rel8->number_of_resource_blocks, end) && + pull8(ppReadPackedMsg, &srs_pdu_fdd_rel8->rb_start, end) && + pullarray8(ppReadPackedMsg, srs_pdu_fdd_rel8->snr, NFAPI_NUM_RB_MAX, srs_pdu_fdd_rel8->number_of_resource_blocks, end))) + return 0; + + return 1; +} + +static uint8_t unpack_srs_indication_fdd_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_srs_indication_fdd_rel9_t *srs_pdu_fdd_rel9 = (nfapi_srs_indication_fdd_rel9_t *)tlv; + return (pull16(ppReadPackedMsg, &srs_pdu_fdd_rel9->timing_advance_r9, end)); +} + +static uint8_t unpack_srs_indication_tdd_rel10_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_srs_indication_ttd_rel10_t *srs_pdu_tdd_rel10 = (nfapi_srs_indication_ttd_rel10_t *)tlv; + return (pull8(ppReadPackedMsg, &srs_pdu_tdd_rel10->uppts_symbol, end)); +} + +static uint8_t unpack_srs_indication_fdd_rel11_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_srs_indication_fdd_rel11_t *srs_pdu_fdd_rel11 = (nfapi_srs_indication_fdd_rel11_t *)tlv; + return ( pull16(ppReadPackedMsg, &srs_pdu_fdd_rel11->ul_rtoa, end)); +} + +static uint8_t unpack_tdd_channel_measurement_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_tdd_channel_measurement_t *value = (nfapi_tdd_channel_measurement_t *)tlv; + + if(!(pull8(ppReadPackedMsg, &value->num_prb_per_subband, end) && + pull8(ppReadPackedMsg, &value->number_of_subbands, end) && + pull8(ppReadPackedMsg, &value->num_atennas, end))) + return 0; + + if(value->number_of_subbands > NFAPI_MAX_NUM_SUBBANDS) { + // todo : add error + return 0; + } + + if(value->num_atennas > NFAPI_MAX_NUM_PHYSICAL_ANTENNAS) { + // todo : add error + return 0; + } + + uint8_t idx = 0; + + for(idx = 0; idx < value->number_of_subbands; ++idx) { + if(!(pull8(ppReadPackedMsg, &value->subands[idx].subband_index, end) && + pullarray16(ppReadPackedMsg, value->subands[idx].channel, NFAPI_MAX_NUM_PHYSICAL_ANTENNAS, value->num_atennas, end))) + return 0; + } + + return 1; +} + + +static uint8_t unpack_srs_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_srs_indication_body_t *value = (nfapi_srs_indication_body_t *)tlv; + uint8_t *srsBodyEnd = *ppReadPackedMsg + value->tl.length; + + if(srsBodyEnd > end) + return 0; + + if(pull8(ppReadPackedMsg, &value->number_of_ues, end) == 0) + return 0; + + if(value->number_of_ues > NFAPI_SRS_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of srs ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_ues, NFAPI_SRS_IND_MAX_PDU); + return 0; + } + + if(value->number_of_ues > 0) { + value->srs_pdu_list = (nfapi_srs_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_srs_indication_pdu_t) * value->number_of_ues, config); + + if(value->srs_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate srs ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_ues); + return 0; + } + } else { + value->srs_pdu_list = 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_ues; ++i) { + nfapi_srs_indication_pdu_t *pdu = &(value->srs_pdu_list[i]); + + if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) + return 0; + + uint8_t *srsPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; + unpack_tlv_t unpack_fns[] = { + { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, + { NFAPI_SRS_INDICATION_FDD_REL8_TAG, &pdu->srs_indication_fdd_rel8, unpack_srs_indication_fdd_rel8_value}, + { NFAPI_SRS_INDICATION_FDD_REL9_TAG, &pdu->srs_indication_fdd_rel9, unpack_srs_indication_fdd_rel9_value}, + { NFAPI_SRS_INDICATION_TDD_REL10_TAG, &pdu->srs_indication_tdd_rel10, unpack_srs_indication_tdd_rel10_value}, + { NFAPI_SRS_INDICATION_FDD_REL11_TAG, &pdu->srs_indication_fdd_rel11, unpack_srs_indication_fdd_rel11_value}, + { NFAPI_TDD_CHANNEL_MEASUREMENT_TAG, &pdu->tdd_channel_measurement, unpack_tdd_channel_measurement_value}, + }; + + if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, srsPduInstanceEnd, 0, 0) == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_srs_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_srs_indication_t *pNfapiMsg = (nfapi_srs_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_SRS_INDICATION_BODY_TAG, &pNfapiMsg->srs_indication_body, &unpack_srs_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_sr_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_sr_indication_body_t *value = (nfapi_sr_indication_body_t *)tlv; + uint8_t *srBodyEnd = *ppReadPackedMsg + value->tl.length; + + if(srBodyEnd > end) + return 0; + + if(pull16(ppReadPackedMsg, &value->number_of_srs, end) == 0) + return 0; + + if(value->number_of_srs > NFAPI_SR_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of sr ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_srs, NFAPI_SR_IND_MAX_PDU); + return 0; + } + + if(value->number_of_srs > 0) { + value->sr_pdu_list = (nfapi_sr_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_sr_indication_pdu_t) * value->number_of_srs, config); + + if(value->sr_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate sr ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_srs); + return 0; + } + } else { + value->sr_pdu_list = 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_srs; ++i) { + nfapi_sr_indication_pdu_t *pdu = &(value->sr_pdu_list[i]); + + if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) + return 0; + + uint8_t *srPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; + unpack_tlv_t unpack_fns[] = { + { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, + { NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, unpack_ul_cqi_information_value }, + }; + + if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, srPduInstanceEnd, 0, 0) == 0) + return 0; + } + + return 1; +} + +static int unpack_sr_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_sr_indication_t *pNfapiMsg = (nfapi_sr_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_SR_INDICATION_BODY_TAG, &pNfapiMsg->sr_indication_body, &unpack_sr_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} +static uint8_t unpack_cqi_indication_rel8_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_cqi_indication_rel8_t *cqi_pdu_rel8 = (nfapi_cqi_indication_rel8_t *)tlv; + return (pull16(ppReadPackedMsg, &cqi_pdu_rel8->length, end) && + pull16(ppReadPackedMsg, &cqi_pdu_rel8->data_offset, end) && + pull8(ppReadPackedMsg, &cqi_pdu_rel8->ul_cqi, end) && + pull8(ppReadPackedMsg, &cqi_pdu_rel8->ri, end) && + pull16(ppReadPackedMsg, &cqi_pdu_rel8->timing_advance, end)); +} + +static uint8_t unpack_cqi_indication_rel9_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_cqi_indication_rel9_t *cqi_pdu_rel9 = (nfapi_cqi_indication_rel9_t *)tlv; + + if(!(pull16(ppReadPackedMsg, &cqi_pdu_rel9->length, end) && + pull16(ppReadPackedMsg, &cqi_pdu_rel9->data_offset, end) && + pull8(ppReadPackedMsg, &cqi_pdu_rel9->ul_cqi, end) && + pull8(ppReadPackedMsg, &cqi_pdu_rel9->number_of_cc_reported, end))) + return 0; + + if(cqi_pdu_rel9->number_of_cc_reported > NFAPI_CC_MAX) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "FIXME : out of bound array\n"); + return 0; + } + + if(!(pullarray8(ppReadPackedMsg, cqi_pdu_rel9->ri, NFAPI_CC_MAX, cqi_pdu_rel9->number_of_cc_reported, end) && + pull16(ppReadPackedMsg, &cqi_pdu_rel9->timing_advance, end) && + pull16(ppReadPackedMsg, &cqi_pdu_rel9->timing_advance_r9, end))) + return 0; + + return 1; +} + +static uint8_t unpack_cqi_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_cqi_indication_body_t *value = (nfapi_cqi_indication_body_t *)tlv; + // the cqiBodyEnd points to the end of the cqi PDU's + uint8_t *cqiBodyEnd = *ppReadPackedMsg + value->tl.length; + + //uint8_t* cqiPduEnd = cqiBodyEnd; + //uint8_t* numberOfPdusAddress = *ppReadPackedMsg; + + if(cqiBodyEnd > end) + return 0; + + if(pull16(ppReadPackedMsg, &value->number_of_cqis, end) == 0) + return 0; + + if(value->number_of_cqis > NFAPI_CQI_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of cqi ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_cqis, NFAPI_CQI_IND_MAX_PDU); + return -1; + } + + if(value->number_of_cqis > 0) { + value->cqi_pdu_list = (nfapi_cqi_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_cqi_indication_pdu_t) * value->number_of_cqis, config); + + if(value->cqi_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate cqi ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_cqis); + return 0; + } + } else { + value->cqi_pdu_list = 0; + } + + if(value->number_of_cqis > 0) { + value->cqi_raw_pdu_list = (nfapi_cqi_indication_raw_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_cqi_indication_raw_pdu_t) * value->number_of_cqis, config); + + if(value->cqi_raw_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate raw cqi ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_cqis); + return 0; + } + } else { + value->cqi_raw_pdu_list = 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_cqis; ++i) { + nfapi_cqi_indication_pdu_t *pdu = &(value->cqi_pdu_list[i]); + memset(pdu, 0, sizeof(nfapi_cqi_indication_pdu_t)); + + if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) + return 0; + + uint8_t *cqiPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; + + while((uint8_t *)(*ppReadPackedMsg) < cqiPduInstanceEnd) { + nfapi_tl_t generic_tl; + + if(unpack_tl(ppReadPackedMsg, &generic_tl, end) == 0) + return 0; + + switch(generic_tl.tag) { + case NFAPI_RX_UE_INFORMATION_TAG: + pdu->rx_ue_information.tl = generic_tl; + + if(unpack_rx_ue_information_value(&pdu->rx_ue_information, ppReadPackedMsg, end) == 0) + return 0; + + break; + + case NFAPI_CQI_INDICATION_REL8_TAG: + pdu->cqi_indication_rel8.tl = generic_tl; + + if(unpack_cqi_indication_rel8_value(&pdu->cqi_indication_rel8, ppReadPackedMsg, end) == 0) + return 0; + + break; + + case NFAPI_CQI_INDICATION_REL9_TAG: + pdu->cqi_indication_rel9.tl = generic_tl; + + if(unpack_cqi_indication_rel9_value(&pdu->cqi_indication_rel9, ppReadPackedMsg, end) == 0) + return 0; + + break; + + case NFAPI_UL_CQI_INFORMATION_TAG: + pdu->ul_cqi_information.tl = generic_tl; + + if(unpack_ul_cqi_information_value(&pdu->ul_cqi_information, ppReadPackedMsg, end) == 0) + return 0; + + break; + + default: { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "RX_CQI.indication Invalid pdu type %d \n", generic_tl.tag ); + } + break; + }; + } + } + + uint8_t idx = 0; + + for(idx = 0; idx < value->number_of_cqis; ++idx) { + if(value->cqi_pdu_list[idx].cqi_indication_rel8.tl.tag == NFAPI_CQI_INDICATION_REL8_TAG) { + if(pullarray8(ppReadPackedMsg, &(value->cqi_raw_pdu_list[idx].pdu[0]), NFAPI_CQI_RAW_MAX_LEN, value->cqi_pdu_list[idx].cqi_indication_rel8.length, end) == 0) + return 0; + } else if(value->cqi_pdu_list[idx].cqi_indication_rel9.tl.tag == NFAPI_CQI_INDICATION_REL9_TAG) { + if(pullarray8(ppReadPackedMsg, &(value->cqi_raw_pdu_list[idx].pdu[0]), NFAPI_CQI_RAW_MAX_LEN, value->cqi_pdu_list[idx].cqi_indication_rel9.length, end) == 0) + return 0; + } + } + + return 1; +} + +static uint8_t unpack_cqi_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_cqi_indication_t *pNfapiMsg = (nfapi_cqi_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_CQI_INDICATION_BODY_TAG, &pNfapiMsg->cqi_indication_body, &unpack_cqi_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} +static uint8_t unpack_lbt_pdsch_req_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_lbt_pdsch_req_pdu_rel13_t *value = (nfapi_lbt_pdsch_req_pdu_rel13_t *)tlv; + return (pull32(ppReadPackedMsg, &value->handle, end) && + pull32(ppReadPackedMsg, &value->mp_cca, end) && + pull32(ppReadPackedMsg, &value->n_cca, end) && + pull32(ppReadPackedMsg, &value->offset, end) && + pull32(ppReadPackedMsg, &value->lte_txop_sf, end) && + pull16(ppReadPackedMsg, &value->txop_sfn_sf_end, end) && + pull32(ppReadPackedMsg, &value->lbt_mode, end)); +} + +static uint8_t unpack_lbt_drs_req_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_lbt_drs_req_pdu_rel13_t *value = (nfapi_lbt_drs_req_pdu_rel13_t *)tlv; + return (pull32(ppReadPackedMsg, &value->handle, end) && + pull32(ppReadPackedMsg, &value->offset, end) && + pull16(ppReadPackedMsg, &value->sfn_sf_end, end) && + pull32(ppReadPackedMsg, &value->lbt_mode, end)); +} + + +static uint8_t unpack_lbt_config_request_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_lbt_dl_config_request_body_t *value = (nfapi_lbt_dl_config_request_body_t *)tlv; + + if(pull16(ppReadPackedMsg, &value->number_of_pdus, end) == 0) + return 0; + + if(value->number_of_pdus > NFAPI_LBT_DL_CONFIG_REQ_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of lbt dl config pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_LBT_DL_CONFIG_REQ_MAX_PDU); + return 0; + } + + if(value->number_of_pdus) { + value->lbt_dl_config_req_pdu_list = (nfapi_lbt_dl_config_request_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_lbt_dl_config_request_pdu_t) * value->number_of_pdus, config); + + if(value->lbt_dl_config_req_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate lbt dl config pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); + return 0; + } + } else { + value->lbt_dl_config_req_pdu_list = 0; + } + + uint16_t i; + uint16_t total_number_of_pdus = value->number_of_pdus; + + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_lbt_dl_config_request_pdu_t *pdu = &(value->lbt_dl_config_req_pdu_list[i]); + + if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && + pull8(ppReadPackedMsg, &pdu->pdu_size, end))) + return 0; + + uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; + + if(packedPduEnd > end) + return 0; + + switch(pdu->pdu_type) { + case NFAPI_LBT_DL_CONFIG_REQUEST_PDSCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_LBT_PDSCH_REQ_PDU_REL13_TAG, &pdu->lbt_pdsch_req_pdu.lbt_pdsch_req_pdu_rel13, &unpack_lbt_pdsch_req_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_LBT_DL_CONFIG_REQUEST_DRS_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_LBT_DRS_REQ_PDU_REL13_TAG, &pdu->lbt_drs_req_pdu.lbt_drs_req_pdu_rel13, &unpack_lbt_drs_req_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + default: + NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL_CONFIG.request body invalid pdu type %d\n", pdu->pdu_type); + return 0; + } + } + + return 1; +} +static uint8_t unpack_lbt_dl_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_lbt_dl_config_request_t *pNfapiMsg = (nfapi_lbt_dl_config_request_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_LBT_DL_CONFIG_REQUEST_BODY_TAG, &pNfapiMsg->lbt_dl_config_request_body, &unpack_lbt_config_request_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_lbt_pdsch_rsp_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_lbt_pdsch_rsp_pdu_rel13_t *value = (nfapi_lbt_pdsch_rsp_pdu_rel13_t *)tlv; + return (pull32(ppReadPackedMsg, &value->handle, end) && + pull32(ppReadPackedMsg, &value->result, end) && + pull32(ppReadPackedMsg, &value->lte_txop_symbols, end) && + pull32(ppReadPackedMsg, &value->initial_partial_sf, end)); +} +static uint8_t unpack_lbt_drs_rsp_pdu_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_lbt_drs_rsp_pdu_rel13_t *value = (nfapi_lbt_drs_rsp_pdu_rel13_t *)tlv; + return (pull32(ppReadPackedMsg, &value->handle, end) && + pull32(ppReadPackedMsg, &value->result, end)); +} + +static uint8_t unpack_lbt_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_lbt_dl_indication_body_t *value = (nfapi_lbt_dl_indication_body_t *)tlv; + + if(pull16(ppReadPackedMsg, &value->number_of_pdus, end) == 0) + return 0; + + if(value->number_of_pdus > NFAPI_LBT_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of lbt dl ind pdu's exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_pdus, NFAPI_LBT_IND_MAX_PDU); + return 0; + } + + if(value->number_of_pdus > 0) { + value->lbt_indication_pdu_list = (nfapi_lbt_dl_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_lbt_dl_indication_pdu_t) * value->number_of_pdus, config); + + if(value->lbt_indication_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate lbt dl ind config pdu list (count:%d)\n", __FUNCTION__, value->number_of_pdus); + return 0; + } + } else { + value->lbt_indication_pdu_list = 0; + } + + uint16_t i; + uint16_t total_number_of_pdus = value->number_of_pdus; + + for(i = 0; i < total_number_of_pdus; ++i) { + nfapi_lbt_dl_indication_pdu_t *pdu = &(value->lbt_indication_pdu_list[i]); + + if(!(pull8(ppReadPackedMsg, &pdu->pdu_type, end) && + pull8(ppReadPackedMsg, &pdu->pdu_size, end))) + return 0; + + uint8_t *packedPduEnd = (*ppReadPackedMsg) + pdu->pdu_size - 2; + + if(packedPduEnd > end) + return 0; + + switch(pdu->pdu_type) { + case NFAPI_LBT_DL_RSP_PDSCH_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_LBT_PDSCH_RSP_PDU_REL13_TAG, &pdu->lbt_pdsch_rsp_pdu.lbt_pdsch_rsp_pdu_rel13, &unpack_lbt_pdsch_rsp_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + case NFAPI_LBT_DL_RSP_DRS_PDU_TYPE: { + unpack_tlv_t unpack_fns[] = { + { NFAPI_LBT_DRS_RSP_PDU_REL13_TAG, &pdu->lbt_drs_rsp_pdu.lbt_drs_rsp_pdu_rel13, &unpack_lbt_drs_rsp_pdu_rel13_value}, + }; + unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, packedPduEnd, 0, 0); + } + break; + + default: + NFAPI_TRACE(NFAPI_TRACE_ERROR, "LBT_DL.indication body invalid pdu type %d\n", pdu->pdu_type); + return 0; + } + } + + return 1; +} +static uint8_t unpack_lbt_dl_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_lbt_dl_indication_t *pNfapiMsg = (nfapi_lbt_dl_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_LBT_DL_INDICATION_BODY_TAG, &pNfapiMsg->lbt_dl_indication_body, &unpack_lbt_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_nb_harq_indication_fdd_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nb_harq_indication_fdd_rel13_t *value = (nfapi_nb_harq_indication_fdd_rel13_t *)tlv; + return (pull8(ppReadPackedMsg, &value->harq_tb1, end)); +} + + +static uint8_t unpack_nb_harq_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nb_harq_indication_body_t *value = (nfapi_nb_harq_indication_body_t *)tlv; + uint8_t *nbharqBodyEnd = *ppReadPackedMsg + value->tl.length; + + if(nbharqBodyEnd > end) + return 0; + + if(pull16(ppReadPackedMsg, &value->number_of_harqs, end) == 0) + return 0; + + if(value->number_of_harqs > NFAPI_HARQ_IND_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of harq ind pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_harqs, NFAPI_HARQ_IND_MAX_PDU); + return 0; + } + + value->nb_harq_pdu_list = (nfapi_nb_harq_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_nb_harq_indication_pdu_t) * value->number_of_harqs, config); + + if(value->nb_harq_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate harq ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_harqs); + return 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_harqs; ++i) { + nfapi_nb_harq_indication_pdu_t *pdu = &(value->nb_harq_pdu_list[i]); + + if(pull16(ppReadPackedMsg, &pdu->instance_length, end) == 0) + return 0; + + uint8_t *harqPduInstanceEnd = *ppReadPackedMsg + pdu->instance_length; + unpack_tlv_t unpack_fns[] = { + { NFAPI_RX_UE_INFORMATION_TAG, &pdu->rx_ue_information, unpack_rx_ue_information_value }, + { NFAPI_NB_HARQ_INDICATION_FDD_REL13_TAG, &pdu->nb_harq_indication_fdd_rel13, &unpack_nb_harq_indication_fdd_rel13_value}, + { NFAPI_UL_CQI_INFORMATION_TAG, &pdu->ul_cqi_information, &unpack_ul_cqi_information_value} + }; + + if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, harqPduInstanceEnd, 0, 0) == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_nb_harq_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nb_harq_indication_t *pNfapiMsg = (nfapi_nb_harq_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_NB_HARQ_INDICATION_BODY_TAG, &pNfapiMsg->nb_harq_indication_body, &unpack_nb_harq_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_nrach_indication_rel13_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end) { + nfapi_nrach_indication_pdu_rel13_t *value = (nfapi_nrach_indication_pdu_rel13_t *)tlv; + return (pull16(ppReadPackedMsg, &value->rnti, end) && + pull8(ppReadPackedMsg, &value->initial_sc, end) && + pull16(ppReadPackedMsg, &value->timing_advance, end) && + pull8(ppReadPackedMsg, &value->nrach_ce_level, end)); +} + +static uint8_t unpack_ue_release_resp(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_ue_release_response_t *pNfapiMsg = (nfapi_ue_release_response_t *)msg; + + if(pull32(ppReadPackedMsg, &pNfapiMsg->error_code, end) == 0) { + return 0; + } else { + NFAPI_TRACE(NFAPI_TRACE_INFO, "ue_release_response:error_code = %d\n", pNfapiMsg->error_code); + } + + return 1; +} + +static uint8_t unpack_nrach_indication_body_value(void *tlv, uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config) { + nfapi_nrach_indication_body_t *value = (nfapi_nrach_indication_body_t *)tlv; + uint8_t *nrachBodyEnd = *ppReadPackedMsg + value->tl.length; + + if(nrachBodyEnd > end) + return 0; + + if(pull8(ppReadPackedMsg, &value->number_of_initial_scs_detected, end) == 0) + return 0; + + if(value->number_of_initial_scs_detected > NFAPI_PREAMBLE_MAX_PDU) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s number of detected scs ind pdus exceed maxium (count:%d max:%d)\n", __FUNCTION__, value->number_of_initial_scs_detected, NFAPI_PREAMBLE_MAX_PDU); + return 0; + } + + value->nrach_pdu_list = (nfapi_nrach_indication_pdu_t *)nfapi_p7_allocate(sizeof(nfapi_nrach_indication_pdu_t) * value->number_of_initial_scs_detected, config); + + if(value->nrach_pdu_list == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s failed to allocate nrach ind pdu list (count:%d)\n", __FUNCTION__, value->number_of_initial_scs_detected); + return 0; + } + + uint8_t i = 0; + + for(i = 0; i < value->number_of_initial_scs_detected; ++i) { + nfapi_nrach_indication_pdu_t *pdu = &(value->nrach_pdu_list[i]); + uint8_t *nrachPduInstanceEnd = *ppReadPackedMsg + 4 + 6; + unpack_tlv_t unpack_fns[] = { + { NFAPI_NRACH_INDICATION_REL13_TAG, &pdu->nrach_indication_rel13, &unpack_nrach_indication_rel13_value}, + }; + + if(unpack_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, nrachPduInstanceEnd, 0, 0) == 0) + return 0; + } + + return 1; +} + +static uint8_t unpack_nrach_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nrach_indication_t *pNfapiMsg = (nfapi_nrach_indication_t *)msg; + unpack_p7_tlv_t unpack_fns[] = { + { NFAPI_NRACH_INDICATION_BODY_TAG, &pNfapiMsg->nrach_indication_body, &unpack_nrach_indication_body_value}, + }; + return (pull16(ppReadPackedMsg, &pNfapiMsg->sfn_sf, end) && + unpack_p7_tlv_list(unpack_fns, sizeof(unpack_fns)/sizeof(unpack_tlv_t), ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_nr_dl_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nr_dl_node_sync_t *pNfapiMsg = (nfapi_nr_dl_node_sync_t *)msg; + return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->delta_sfn_slot, end) && + unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_dl_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_dl_node_sync_t *pNfapiMsg = (nfapi_dl_node_sync_t *)msg; + return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->delta_sfn_sf, end) && + unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_nr_ul_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nr_ul_node_sync_t *pNfapiMsg = (nfapi_nr_ul_node_sync_t *)msg; + return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->t2, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->t3, end) && + unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_ul_node_sync(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_ul_node_sync_t *pNfapiMsg = (nfapi_ul_node_sync_t *)msg; + return (pull32(ppReadPackedMsg, &pNfapiMsg->t1, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->t2, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->t3, end) && + unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + +static uint8_t unpack_timing_info(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_timing_info_t *pNfapiMsg = (nfapi_timing_info_t *)msg; + return (pull32(ppReadPackedMsg, &pNfapiMsg->last_sfn_sf, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->time_since_last_timing_info, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->dl_config_jitter, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->tx_request_jitter, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->ul_config_jitter, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->hi_dci0_jitter, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->dl_config_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->tx_request_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->ul_config_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->hi_dci0_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->dl_config_earliest_arrival, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->tx_request_earliest_arrival, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->ul_config_earliest_arrival, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->hi_dci0_earliest_arrival, end) && + unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + + +static uint8_t unpack_nr_timing_info(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config) { + nfapi_nr_timing_info_t *pNfapiMsg = (nfapi_nr_timing_info_t *)msg; + return (pull32(ppReadPackedMsg, &pNfapiMsg->last_sfn, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->last_slot, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->time_since_last_timing_info, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->dl_tti_jitter, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_jitter, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->ul_tti_jitter, end) && + pull32(ppReadPackedMsg, &pNfapiMsg->ul_dci_jitter, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->dl_tti_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->ul_tti_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->ul_dci_latest_delay, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->dl_tti_earliest_arrival, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->tx_data_request_earliest_arrival, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->ul_tti_earliest_arrival, end) && + pulls32(ppReadPackedMsg, &pNfapiMsg->ul_dci_earliest_arrival, end) && + unpack_p7_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &pNfapiMsg->vendor_extension)); +} + + + +// unpack length check + +static int check_unpack_length(nfapi_message_id_e msgId, uint32_t unpackedBufLen) { + int retLen = 0; + + switch (msgId) { + case NFAPI_DL_CONFIG_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_dl_config_request_t)) + retLen = sizeof(nfapi_dl_config_request_t); + + break; + + case NFAPI_UL_CONFIG_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_ul_config_request_t)) + retLen = sizeof(nfapi_ul_config_request_t); + + break; + + case NFAPI_SUBFRAME_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_subframe_indication_t)) + retLen = sizeof(nfapi_subframe_indication_t); + + break; + + case NFAPI_HI_DCI0_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_hi_dci0_request_t)) + retLen = sizeof(nfapi_hi_dci0_request_t); + + break; + + case NFAPI_TX_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_tx_request_t)) + retLen = sizeof(nfapi_tx_request_t); + + break; + + case NFAPI_HARQ_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_harq_indication_t)) + retLen = sizeof(nfapi_harq_indication_t); + + break; + + case NFAPI_CRC_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_crc_indication_t)) + retLen = sizeof(nfapi_crc_indication_t); + + break; + + case NFAPI_RX_ULSCH_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_rx_indication_t)) + retLen = sizeof(nfapi_rx_indication_t); + + break; + + case NFAPI_RACH_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_rach_indication_t)) + retLen = sizeof(nfapi_rach_indication_t); + + break; + + case NFAPI_SRS_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_srs_indication_t)) + retLen = sizeof(nfapi_srs_indication_t); + + break; + + case NFAPI_RX_SR_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_sr_indication_t)) + retLen = sizeof(nfapi_sr_indication_t); + + break; + + case NFAPI_RX_CQI_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_cqi_indication_t)) + retLen = sizeof(nfapi_cqi_indication_t); + + break; + + case NFAPI_LBT_DL_CONFIG_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_lbt_dl_config_request_t)) + retLen = sizeof(nfapi_lbt_dl_config_request_t); + + break; + + case NFAPI_LBT_DL_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_lbt_dl_indication_t)) + retLen = sizeof(nfapi_lbt_dl_indication_t); + + break; + + case NFAPI_NB_HARQ_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_nb_harq_indication_t)) + retLen = sizeof(nfapi_nb_harq_indication_t); + + break; + + case NFAPI_NRACH_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_nrach_indication_t)) + retLen = sizeof(nfapi_nrach_indication_t); + + break; + + case NFAPI_DL_NODE_SYNC: + if (unpackedBufLen >= sizeof(nfapi_dl_node_sync_t)) + retLen = sizeof(nfapi_dl_node_sync_t); + + break; + + case NFAPI_UL_NODE_SYNC: + if (unpackedBufLen >= sizeof(nfapi_ul_node_sync_t)) + retLen = sizeof(nfapi_ul_node_sync_t); + + break; + + case NFAPI_TIMING_INFO: + if (unpackedBufLen >= sizeof(nfapi_timing_info_t)) + retLen = sizeof(nfapi_timing_info_t); + + break; + + case NFAPI_UE_RELEASE_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_ue_release_request_t)) + retLen = sizeof(nfapi_ue_release_request_t); + + break; + + case NFAPI_UE_RELEASE_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_ue_release_response_t)) + retLen = sizeof(nfapi_ue_release_response_t); + + break; + + default: + NFAPI_TRACE(NFAPI_TRACE_ERROR, "Unknown message ID %d\n", msgId); + break; + } + + return retLen; +} + +static int check_nr_unpack_length(nfapi_message_id_e msgId, uint32_t unpackedBufLen) +{ + int retLen = 0; + + switch (msgId) + { + case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_dl_tti_request_t)) + retLen = sizeof(nfapi_nr_dl_tti_request_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_ul_tti_request_t)) + retLen = sizeof(nfapi_nr_ul_tti_request_t); + break; + + case NFAPI_SUBFRAME_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_subframe_indication_t)) + retLen = sizeof(nfapi_subframe_indication_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_ul_dci_request_t)) + retLen = sizeof(nfapi_nr_ul_dci_request_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_nr_tx_data_request_t)) + retLen = sizeof(nfapi_nr_tx_data_request_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_nr_rx_data_indication_t)) + retLen = sizeof(nfapi_nr_rx_data_indication_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_CRC_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_nr_crc_indication_t)) + retLen = sizeof(nfapi_nr_crc_indication_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_nr_rach_indication_t)) + retLen = sizeof(nfapi_nr_rach_indication_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UCI_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_nr_uci_indication_t)) + retLen = sizeof(nfapi_nr_uci_indication_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_SRS_INDICATION: + if (unpackedBufLen >= sizeof(nfapi_nr_srs_indication_t)) + retLen = sizeof(nfapi_nr_srs_indication_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC: + if (unpackedBufLen >= sizeof(nfapi_nr_dl_node_sync_t)) + retLen = sizeof(nfapi_nr_dl_node_sync_t); + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC: + if (unpackedBufLen >= sizeof(nfapi_nr_ul_node_sync_t)) + retLen = sizeof(nfapi_nr_ul_node_sync_t); + break; + + case NFAPI_TIMING_INFO: + if (unpackedBufLen >= sizeof(nfapi_timing_info_t)) + retLen = sizeof(nfapi_timing_info_t); + break; + + case NFAPI_UE_RELEASE_REQUEST: + if (unpackedBufLen >= sizeof(nfapi_ue_release_request_t)) + retLen = sizeof(nfapi_ue_release_request_t); + break; + + case NFAPI_UE_RELEASE_RESPONSE: + if (unpackedBufLen >= sizeof(nfapi_ue_release_response_t)) + retLen = sizeof(nfapi_ue_release_response_t); + break; + + default: + NFAPI_TRACE(NFAPI_TRACE_ERROR, "Unknown message ID %d\n", msgId); + break; + } + + return retLen; +} + + + +// Main unpack functions - public + +int nfapi_p7_message_header_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p7_codec_config_t *config) { + nfapi_p7_message_header_t *pMessageHeader = pUnpackedBuf; + uint8_t *pReadPackedMessage = pMessageBuf; + uint8_t *end = pMessageBuf + messageBufLen; + + if (pMessageBuf == NULL || pUnpackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 header unpack supplied pointers are null\n"); + return -1; + } + + if (messageBufLen < NFAPI_P7_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p7_message_header_t)) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 header unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); + return -1; + } + + // process the header + if(!(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && + pull16(&pReadPackedMessage, &pMessageHeader->m_segment_sequence, end) && + pull32(&pReadPackedMessage, &pMessageHeader->checksum, end) && + pull32(&pReadPackedMessage, &pMessageHeader->transmit_timestamp, end))) + return -1; + + return 0; +} + +int nfapi_p7_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p7_codec_config_t *config) { + int result = 0; + nfapi_p7_message_header_t *pMessageHeader = (nfapi_p7_message_header_t *)pUnpackedBuf; + uint8_t *pReadPackedMessage = pMessageBuf; + uint8_t *end = pMessageBuf + messageBufLen; + + if (pMessageBuf == NULL || pUnpackedBuf == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied pointers are null\n"); + return -1; + } + + if (messageBufLen < NFAPI_P7_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p7_message_header_t)) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); + return -1; + } + + /* + uint8_t *ptr = pMessageBuf; + printf("\n Read P7 message unpack: "); + while(ptr < end){ + printf(" %d ", *ptr); + ptr++; + } + printf("\n"); + */ + // clean the supplied buffer for - tag value blanking + (void)memset(pUnpackedBuf, 0, unpackedBufLen); + + // process the header + if(!(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && + pull16(&pReadPackedMessage, &pMessageHeader->m_segment_sequence, end) && + pull32(&pReadPackedMessage, &pMessageHeader->checksum, end) && + pull32(&pReadPackedMessage, &pMessageHeader->transmit_timestamp, end))) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack header failed\n"); + return -1; + } + + if((uint8_t *)(pMessageBuf + pMessageHeader->message_length) > end) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack message length is greater than the message buffer \n"); + return -1; + } + + /* + if(check_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack unpack buffer is not large enough \n"); + return -1; + } + */ + + // look for the specific message + switch (pMessageHeader->message_id) { + case NFAPI_DL_CONFIG_REQUEST: + if (check_unpack_length(NFAPI_DL_CONFIG_REQUEST, unpackedBufLen)) + result = unpack_dl_config_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_UL_CONFIG_REQUEST: + if (check_unpack_length(NFAPI_UL_CONFIG_REQUEST, unpackedBufLen)) + result = unpack_ul_config_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_TX_REQUEST: + if (check_unpack_length(NFAPI_TX_REQUEST, unpackedBufLen)) + result = unpack_tx_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_HI_DCI0_REQUEST: + if (check_unpack_length(NFAPI_HI_DCI0_REQUEST, unpackedBufLen)) + result = unpack_hi_dci0_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_UE_RELEASE_REQUEST: + if (check_unpack_length(NFAPI_UE_RELEASE_REQUEST, unpackedBufLen)) + result = unpack_ue_release_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_HARQ_INDICATION: + if (check_unpack_length(NFAPI_HARQ_INDICATION, unpackedBufLen)) + result = unpack_harq_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_CRC_INDICATION: + if (check_unpack_length(NFAPI_CRC_INDICATION, unpackedBufLen)) + result = unpack_crc_indication(&pReadPackedMessage,end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_RX_ULSCH_INDICATION: + if (check_unpack_length(NFAPI_RX_ULSCH_INDICATION, unpackedBufLen)) + result = unpack_rx_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_RACH_INDICATION: + if (check_unpack_length(NFAPI_RACH_INDICATION, unpackedBufLen)) + result = unpack_rach_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_SRS_INDICATION: + if (check_unpack_length(NFAPI_SRS_INDICATION, unpackedBufLen)) + result = unpack_srs_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_RX_SR_INDICATION: + if (check_unpack_length(NFAPI_RX_SR_INDICATION, unpackedBufLen)) + result = unpack_sr_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_RX_CQI_INDICATION: + if (check_unpack_length(NFAPI_RX_CQI_INDICATION, unpackedBufLen)) + result = unpack_cqi_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_LBT_DL_CONFIG_REQUEST: + if (check_unpack_length(NFAPI_LBT_DL_CONFIG_REQUEST, unpackedBufLen)) + result = unpack_lbt_dl_config_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_LBT_DL_INDICATION: + if (check_unpack_length(NFAPI_LBT_DL_INDICATION, unpackedBufLen)) + result = unpack_lbt_dl_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_NB_HARQ_INDICATION: + if (check_unpack_length(NFAPI_NB_HARQ_INDICATION, unpackedBufLen)) + result = unpack_nb_harq_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_NRACH_INDICATION: + if (check_unpack_length(NFAPI_NRACH_INDICATION, unpackedBufLen)) + result = unpack_nrach_indication(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_DL_NODE_SYNC: + if (check_unpack_length(NFAPI_DL_NODE_SYNC, unpackedBufLen)) + result = unpack_dl_node_sync(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_UL_NODE_SYNC: + if (check_unpack_length(NFAPI_UL_NODE_SYNC, unpackedBufLen)) + result = unpack_ul_node_sync(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_TIMING_INFO: + if (check_unpack_length(NFAPI_TIMING_INFO, unpackedBufLen)) + result = unpack_timing_info(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + case NFAPI_UE_RELEASE_RESPONSE: + if (check_unpack_length(NFAPI_UE_RELEASE_RESPONSE, unpackedBufLen)) + result = unpack_ue_release_resp(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + + break; + + default: + if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) { + if(config && config->unpack_p7_vendor_extension) { + result = (config->unpack_p7_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); + } + + break; + } + + if(result == 0) + return -1; + else + return 0; +} + +int nfapi_nr_p7_message_unpack(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen, nfapi_p7_codec_config_t* config) +{ + int result = 0; + nfapi_p7_message_header_t *pMessageHeader = (nfapi_p7_message_header_t*)pUnpackedBuf; + uint8_t *pReadPackedMessage = pMessageBuf; + uint8_t *end = pMessageBuf + messageBufLen; + + if (pMessageBuf == NULL || pUnpackedBuf == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied pointers are null\n"); + return -1; + } + + if (messageBufLen < NFAPI_P7_HEADER_LENGTH || unpackedBufLen < sizeof(nfapi_p7_message_header_t)) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack supplied message buffer is too small %d, %d\n", messageBufLen, unpackedBufLen); + return -1; + } + + // uint8_t *ptr = pMessageBuf; + // printf("\n Read P7 message unpack: "); + // while(ptr < end){ + // printf(" %d ", *ptr); + // ptr++; + // } + // printf("\n"); + + // clean the supplied buffer for - tag value blanking + (void)memset(pUnpackedBuf, 0, unpackedBufLen); + + // process the header + if(!(pull16(&pReadPackedMessage, &pMessageHeader->phy_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_id, end) && + pull16(&pReadPackedMessage, &pMessageHeader->message_length, end) && + pull16(&pReadPackedMessage, &pMessageHeader->m_segment_sequence, end) && + pull32(&pReadPackedMessage, &pMessageHeader->checksum, end) && + pull32(&pReadPackedMessage, &pMessageHeader->transmit_timestamp, end))) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack header failed\n"); + return -1; + } + + if((uint8_t*)(pMessageBuf + pMessageHeader->message_length) > end) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack message length is greater than the message buffer \n"); + return -1; + } + + /* + if(check_unpack_length(pMessageHeader->message_id, unpackedBufLen) == 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "P7 unpack unpack buffer is not large enough \n"); + return -1; + } + */ + + // look for the specific message + switch (pMessageHeader->message_id) + { + case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST, unpackedBufLen)) + result = unpack_dl_tti_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST, unpackedBufLen)) + result = unpack_ul_tti_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + case NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST, unpackedBufLen)) + result = unpack_tx_data_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + case NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST, unpackedBufLen)) + result = unpack_ul_dci_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + + case NFAPI_UE_RELEASE_REQUEST: + if (check_nr_unpack_length(NFAPI_UE_RELEASE_REQUEST, unpackedBufLen)) + result = unpack_ue_release_request(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + case NFAPI_NR_PHY_MSG_TYPE_SLOT_INDICATION: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_SLOT_INDICATION, unpackedBufLen)){ + nfapi_nr_slot_indication_scf_t* msg = (nfapi_nr_slot_indication_scf_t*) pMessageHeader; + result = unpack_nr_slot_indication(&pReadPackedMessage, end, msg, config); + } + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION, unpackedBufLen)){ + nfapi_nr_rx_data_indication_t* msg = (nfapi_nr_rx_data_indication_t*) pMessageHeader; + msg->pdu_list = (nfapi_nr_rx_data_pdu_t*) malloc(sizeof(nfapi_nr_rx_data_pdu_t)); + msg->pdu_list->pdu = (uint8_t *) malloc(sizeof(uint8_t)); + result = unpack_nr_rx_data_indication(&pReadPackedMessage, end, msg, config); + } + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_CRC_INDICATION: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_CRC_INDICATION, unpackedBufLen)){ + + nfapi_nr_crc_indication_t* msg = (nfapi_nr_crc_indication_t*) pMessageHeader; + msg->crc_list = (nfapi_nr_crc_t*) malloc(sizeof(nfapi_nr_crc_t)); + result = unpack_nr_crc_indication(&pReadPackedMessage,end , msg, config); + } + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_UCI_INDICATION: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_UCI_INDICATION, unpackedBufLen)){ + nfapi_nr_uci_indication_t* msg = (nfapi_nr_uci_indication_t*) pMessageHeader; + msg->uci_list = (nfapi_nr_uci_t*) malloc(sizeof(nfapi_nr_uci_t)); + result = unpack_nr_uci_indication(&pReadPackedMessage, end, msg, config); + } + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_SRS_INDICATION: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_SRS_INDICATION, unpackedBufLen)){ + nfapi_nr_srs_indication_t* msg = (nfapi_nr_srs_indication_t*) pMessageHeader; + msg->pdu_list = (nfapi_nr_srs_indication_pdu_t*) malloc(sizeof(nfapi_nr_srs_indication_pdu_t)); + result = unpack_nr_srs_indication(&pReadPackedMessage, end, msg, config); + } + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION, unpackedBufLen)){ + nfapi_nr_rach_indication_t* msg = (nfapi_nr_rach_indication_t*) pMessageHeader; + result = unpack_nr_rach_indication(&pReadPackedMessage, end, msg, config); + } + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC, unpackedBufLen)) + result = unpack_nr_dl_node_sync(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + + case NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC: + if (check_nr_unpack_length(NFAPI_NR_PHY_MSG_TYPE_UL_NODE_SYNC, unpackedBufLen)) + result = unpack_nr_ul_node_sync(&pReadPackedMessage, end , pMessageHeader, config); + else + return -1; + break; + + case NFAPI_TIMING_INFO: + if (check_nr_unpack_length(NFAPI_TIMING_INFO, unpackedBufLen)) + result = unpack_nr_timing_info(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + + case NFAPI_UE_RELEASE_RESPONSE: + if (check_nr_unpack_length(NFAPI_UE_RELEASE_RESPONSE, unpackedBufLen)) + result = unpack_ue_release_resp(&pReadPackedMessage, end, pMessageHeader, config); + else + return -1; + break; + + default: + + if(pMessageHeader->message_id >= NFAPI_VENDOR_EXT_MSG_MIN && + pMessageHeader->message_id <= NFAPI_VENDOR_EXT_MSG_MAX) + { + if(config && config->unpack_p7_vendor_extension) + { + result = (config->unpack_p7_vendor_extension)(pMessageHeader, &pReadPackedMessage, end, config); + } + else + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s VE NFAPI message ID %d. No ve decoder provided\n", __FUNCTION__, pMessageHeader->message_id); + } + } + else + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s NFAPI Unknown message ID %d\n", __FUNCTION__, pMessageHeader->message_id); + } + break; + } + + if(result == 0) + return -1; + else + return 0; +} + + diff --git a/nfapi/open-nFAPI/pnf/inc/pnf_p7.h b/nfapi/open-nFAPI/pnf/inc/pnf_p7.h index 1e2e2fa21d..763b5c9abe 100644 --- a/nfapi/open-nFAPI/pnf/inc/pnf_p7.h +++ b/nfapi/open-nFAPI/pnf/inc/pnf_p7.h @@ -153,10 +153,17 @@ int pnf_p7_send_message(pnf_p7_t* pnf_p7, uint8_t* msg, uint32_t msg_len); int pnf_p7_slot_ind(pnf_p7_t* config, uint16_t phy_id, uint16_t sfn, uint16_t slot); int pnf_p7_subframe_ind(pnf_p7_t* config, uint16_t phy_id, uint16_t sfn_sf); - +int nfapi_pnf_p7_nr_slot_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_slot_indication_scf_t* ind); +int nfapi_pnf_p7_nr_rx_data_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_rx_data_indication_t* ind); +int nfapi_pnf_p7_nr_crc_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_crc_indication_t* ind); +int nfapi_pnf_p7_nr_srs_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_srs_indication_t* ind); +int nfapi_pnf_p7_nr_uci_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_uci_indication_t* ind); +int nfapi_pnf_p7_nr_rach_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_rach_indication_t* ind); pnf_p7_rx_message_t* pnf_p7_rx_reassembly_queue_add_segment(pnf_p7_t* pnf_p7, pnf_p7_rx_reassembly_queue_t* queue, uint32_t rx_hr_time, uint16_t sequence_number, uint16_t segment_number, uint8_t m, uint8_t* data, uint16_t data_len); void pnf_p7_rx_reassembly_queue_remove_msg(pnf_p7_t* pnf_p7, pnf_p7_rx_reassembly_queue_t* queue, pnf_p7_rx_message_t* msg); void pnf_p7_rx_reassembly_queue_remove_old_msgs(pnf_p7_t* pnf_p7, pnf_p7_rx_reassembly_queue_t* queue, uint32_t rx_hr_time, uint32_t delta); +int pnf_nr_p7_pack_and_send_p7_message(pnf_p7_t* pnf_p7, nfapi_p7_message_header_t* header, uint32_t msg_len); + #endif /* _PNF_P7_H_ */ diff --git a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h index 8520a5d162..fc9cd66077 100644 --- a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h +++ b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h @@ -27,6 +27,7 @@ extern "C" { #include #include "nfapi_nr_interface.h" #include "nfapi_nr_interface_scf.h" + #include #include "openair1/PHY/defs_gNB.h" diff --git a/nfapi/open-nFAPI/pnf/src/pnf_p7.c b/nfapi/open-nFAPI/pnf/src/pnf_p7.c index 18891cdee9..092b967189 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_p7.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_p7.c @@ -156,17 +156,16 @@ nfapi_dl_config_request_t* allocate_nfapi_dl_config_request(pnf_p7_t* pnf_p7) void deallocate_nfapi_dl_tti_request(nfapi_nr_dl_tti_request_t* req, pnf_p7_t* pnf_p7) { //printf("%s() SFN/SF:%d %s req:%p pdu_list:%p\n", __FUNCTION__, NFAPI_SFNSF2DEC(req->sfn_sf), pnf_p7->_public.codec_config.deallocate ? "DEALLOCATE" : "FREE", req, req->dl_config_request_body.dl_config_pdu_list); - /* - if(pnf_p7->_public.codec_config.deallocate) - { - //nfapi_nr_dl_tti_request_pdu_t *temp = &req->dl_tti_pdu_list; - (pnf_p7->_public.codec_config.deallocate)(req); - } - else - { - free(req); - } -*/ + + // if(pnf_p7->_public.codec_config.deallocate) + // { + // (pnf_p7->_public.codec_config.deallocate)(req); + // } + // else + // { + // free(req); + // } + pnf_p7_free(pnf_p7, req); } @@ -915,14 +914,13 @@ void send_dummy_subframe(pnf_p7_t* pnf_p7, uint16_t sfn_sf) int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t slot) -{ +{ + //This function is aligned with rx sfn/slot + // We could either send an event to the p7 thread have have it run the // subframe or we could handle it here and lock access to the subframe // buffers. If we do it on the p7 thread then we run the risk of blocking // on the udp send. - // - // todo : start a timer to give us more of the 1 ms tick before send back - // the frame // todo : consider a more efficent lock mechasium //uint16_t NUM_SLOTS = 20;//10* 2^mu @@ -935,21 +933,21 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl // save the curren time, sfn and slot pnf_p7->slot_start_time_hr = pnf_get_current_time_hr(); - pnf_p7->sfn = sfn; - - pnf_p7->slot = slot; - - - + slot_ahead = 6; uint32_t sfn_slot_tx = sfnslot_add_slot(sfn, slot, slot_ahead); - uint16_t sfn_tx = sfn_slot_tx>>6; - uint16_t slot_tx = sfn_slot_tx & 0X3F; + uint16_t sfn_tx = NFAPI_SFNSLOT2SFN(sfn_slot_tx); + uint8_t slot_tx = NFAPI_SFNSLOT2SLOT(sfn_slot_tx); - // uint32_t tx_sfn_slot_dec = NFAPI_SFNSLOT2DEC(sfn,slot); - uint32_t tx_slot_dec = NFAPI_SFNSLOT2DEC(sfn,slot); + //We align the pnf_p7 sfn/slot with tx sfn/slot, and vnf is synced with pnf_p7 sfn/slot. This is so that the scheduler runs slot_ahead from rx thread. + pnf_p7->sfn = sfn_tx; + pnf_p7->slot = slot_tx; - //uint32_t tx_sfn_slot_dec = NFAPI_SFNSLOT2DEC(sfn_slot_tx); + uint32_t rx_slot_dec = NFAPI_SFNSLOT2DEC(sfn, slot); + uint8_t buffer_index_rx = rx_slot_dec % 20; + + uint32_t tx_slot_dec = NFAPI_SFNSLOT2DEC(sfn_tx,slot_tx); + uint8_t buffer_index_tx = tx_slot_dec % 20; // If the subframe_buffer has been configured if(pnf_p7->_public.slot_buffer_size!= 0) // for now value is same as sf_buffer_size @@ -976,28 +974,20 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl pnf_p7->slot_shift = 0; } - uint32_t slot_dec = NFAPI_SFNSLOT2DEC(sfn, slot); - uint8_t buffer_index = slot_dec % pnf_p7->_public.slot_buffer_size; + nfapi_pnf_p7_slot_buffer_t* rx_slot_buffer = &(pnf_p7->slot_buffer[buffer_index_rx]); - nfapi_pnf_p7_slot_buffer_t* slot_buffer = &(pnf_p7->slot_buffer[buffer_index]); - // see where the PNF_P7 slot buffer its getting filled + nfapi_pnf_p7_slot_buffer_t* tx_slot_buffer = &(pnf_p7->slot_buffer[buffer_index_tx]); - uint8_t tx_buffer_index = tx_slot_dec % pnf_p7->_public.slot_buffer_size; - nfapi_pnf_p7_slot_buffer_t* tx_slot_buffer = &(pnf_p7->slot_buffer[tx_buffer_index]); - - if (0) NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() shift:%d slot_buffer->sfn_sf:%d tx_slot_buffer->sfn_slot:%d sfn_sf:%d subframe_buffer[buffer_index:%u dl_config_req:%p tx_req:%p] " - "TX:sfn_sf:%d:tx_buffer_index:%d[dl_config_req:%p tx_req:%p]\n", - __FUNCTION__, - pnf_p7->slot_shift, - NFAPI_SFNSLOT2DEC(slot_buffer->sfn, slot_buffer->slot), - NFAPI_SFNSLOT2DEC(tx_slot_buffer->sfn, tx_slot_buffer->slot), - slot_dec, buffer_index, slot_buffer->dl_tti_req, slot_buffer->tx_data_req, - tx_slot_dec, tx_buffer_index, tx_slot_buffer->dl_tti_req, tx_slot_buffer->tx_data_req); + // if (0) NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() shift:%d slot_buffer->sfn_sf:%d tx_slot_buffer->sfn_slot:%d sfn_sf:%d subframe_buffer[buffer_index:%u dl_config_req:%p tx_req:%p] " + // "TX:sfn_sf:%d:tx_buffer_index:%d[dl_config_req:%p tx_req:%p]\n", + // __FUNCTION__, + // pnf_p7->slot_shift, + // NFAPI_SFNSLOT2DEC(rx_slot_buffer->sfn, rx_slot_buffer->slot), + // NFAPI_SFNSLOT2DEC(tx_slot_buffer->sfn, tx_slot_buffer->slot), + // slot_dec, buffer_index_rx, rx_slot_buffer->dl_tti_req, rx_slot_buffer->tx_data_req, + // tx_slot_dec, buffer_index_tx, tx_slot_buffer->dl_tti_req, tx_slot_buffer->tx_data_req); //TODO: Change later if required - - // if the subframe buffer sfn sf is set then we have atlease 1 message - // from the vnf. // todo : how to handle the messages we don't have, send dummies for // now @@ -1006,37 +996,39 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl //printf("tx_slot_buff_sfn - %d, tx_slot_buf_slot - %d, sfn_tx = %d, sllot_tx - %d \n",tx_slot_buffer->sfn,tx_slot_buffer->slot,sfn_tx,slot_tx); // if(tx_slot_buffer->slot == slot_tx && tx_slot_buffer->sfn == sfn_tx) // { - - if(tx_slot_buffer->tx_data_req != 0) - { - - if(pnf_p7->_public.tx_data_req_fn) - { - (pnf_p7->_public.tx_data_req_fn)(&(pnf_p7->_public), tx_slot_buffer->tx_data_req); - } - } - else - { - // send dummy - if(pnf_p7->_public.tx_data_req_fn && pnf_p7->_public.dummy_slot.tx_data_req) - { - pnf_p7->_public.dummy_slot.tx_data_req->SFN = sfn_tx; - pnf_p7->_public.dummy_slot.tx_data_req->Slot = slot_tx; - - (pnf_p7->_public.tx_data_req_fn)(&(pnf_p7->_public), pnf_p7->_public.dummy_slot.tx_data_req); - } - } - //} + + //checking in the tx slot buffers to see if a p7 msg is present. todo: what if it's a mixed slot? - if( tx_slot_buffer->dl_tti_req != 0) // ADDED & TO BYPASS ERROR + if(tx_slot_buffer->tx_data_req != 0 && tx_slot_buffer->tx_data_req->SFN == sfn_tx && tx_slot_buffer->tx_data_req->Slot == slot_tx) + { + + if(pnf_p7->_public.tx_data_req_fn) + { + //NFAPI_TRACE(NFAPI_TRACE_INFO, "Calling tx_data_req_fn in SFN/slot %d.%d \n",sfn,slot); + LOG_D(PHY, "Process tx_data SFN/slot %d.%d buffer index: %d \n",sfn_tx,slot_tx,buffer_index_tx); + (pnf_p7->_public.tx_data_req_fn)(&(pnf_p7->_public), tx_slot_buffer->tx_data_req); + } + } + else + { + // send dummy + if(pnf_p7->_public.tx_data_req_fn && pnf_p7->_public.dummy_slot.tx_data_req) + { + pnf_p7->_public.dummy_slot.tx_data_req->SFN = sfn_tx; + pnf_p7->_public.dummy_slot.tx_data_req->Slot = slot_tx; + + (pnf_p7->_public.tx_data_req_fn)(&(pnf_p7->_public), pnf_p7->_public.dummy_slot.tx_data_req); + } + } + + if(tx_slot_buffer->dl_tti_req != 0 && tx_slot_buffer->dl_tti_req->SFN == sfn_tx && tx_slot_buffer->dl_tti_req->Slot == slot_tx) { if(pnf_p7->_public.dl_tti_req_fn) { + LOG_D(PHY, "Process dl_tti SFN/slot %d.%d buffer index: %d \n",sfn_tx,slot_tx,buffer_index_tx); (pnf_p7->_public.dl_tti_req_fn)(NULL, &(pnf_p7->_public), tx_slot_buffer->dl_tti_req); } } - - else { // send dummy @@ -1048,10 +1040,13 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl } } - if(tx_slot_buffer->ul_dci_req!= 0) + + if(tx_slot_buffer->ul_dci_req!= 0 && tx_slot_buffer->ul_dci_req->SFN == sfn_tx && tx_slot_buffer->ul_dci_req->Slot == slot_tx) { if(pnf_p7->_public.ul_dci_req_fn) { + //NFAPI_TRACE(NFAPI_TRACE_INFO, "Calling UL_dci_req_fn in SFN/slot %d.%d \n",sfn,slot); + LOG_D(PHY, "Process ul_dci SFN/slot %d.%d buffer index: %d \n",sfn_tx,slot_tx,buffer_index_tx); (pnf_p7->_public.ul_dci_req_fn)(NULL, &(pnf_p7->_public), tx_slot_buffer->ul_dci_req); } } @@ -1065,10 +1060,14 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl (pnf_p7->_public.ul_dci_req_fn)(NULL, &(pnf_p7->_public), pnf_p7->_public.dummy_slot.ul_dci_req); } } + + //deallocate slot buffers after passing down the PDUs to PHY processing + if(tx_slot_buffer->dl_tti_req != 0) { deallocate_nfapi_dl_tti_request(tx_slot_buffer->dl_tti_req, pnf_p7); tx_slot_buffer->dl_tti_req = 0; + LOG_D(PHY,"SFN/slot %d.%d Buffer index : %d freed \n",sfn_tx,slot_tx,buffer_index_tx); } if(tx_slot_buffer->tx_data_req != 0) @@ -1082,73 +1081,52 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl deallocate_nfapi_ul_dci_request(tx_slot_buffer->ul_dci_req, pnf_p7); tx_slot_buffer->ul_dci_req = 0; } + + + //checking in the rx slot buffers to see if a p7 msg is present. + + if(rx_slot_buffer->ul_tti_req != 0 && rx_slot_buffer->ul_tti_req->SFN == sfn && rx_slot_buffer->ul_tti_req->Slot == slot) + { + if(pnf_p7->_public.ul_tti_req_fn) + { + //NFAPI_TRACE(NFAPI_TRACE_INFO, "Calling UL_tti_req_fn in SFN/slot %d.%d \n",sfn,slot); + (pnf_p7->_public.ul_tti_req_fn)(NULL, &(pnf_p7->_public), rx_slot_buffer->ul_tti_req); + } + } else { - // If we ever need to "send" a dummy ul_config this won't work!!! - // send_dummy_subframe(pnf_p7, sfn_sf_tx); + // send dummy + if(pnf_p7->_public.ul_tti_req_fn && pnf_p7->_public.dummy_slot.ul_tti_req) + { + pnf_p7->_public.dummy_slot.ul_tti_req->SFN = sfn; + pnf_p7->_public.dummy_slot.ul_tti_req->Slot = slot; + LOG_D(PHY, "Process ul_tti SFN/slot %d.%d buffer index: %d \n",sfn,slot,buffer_index_rx); + (pnf_p7->_public.ul_tti_req_fn)(NULL, &(pnf_p7->_public), pnf_p7->_public.dummy_slot.ul_tti_req); + } + } + if(rx_slot_buffer->ul_tti_req != 0) + { + deallocate_nfapi_ul_tti_request(rx_slot_buffer->ul_tti_req, pnf_p7); + rx_slot_buffer->ul_tti_req = 0; - // send_dummy_slot(pnf_p7, sfn_tx, slot_tx); } + //reset slot buffer - - if(slot_buffer->sfn == sfn && slot_buffer->slot == slot ) + if ( rx_slot_buffer->dl_tti_req == 0 && + rx_slot_buffer->tx_data_req == 0 && + rx_slot_buffer->ul_tti_req == 0) { - - if(slot_buffer->ul_tti_req != 0) - { - if(pnf_p7->_public.ul_tti_req_fn) - { - (pnf_p7->_public.ul_tti_req_fn)(NULL, &(pnf_p7->_public), slot_buffer->ul_tti_req); - } - //deallocate_nfapi_ul_config_request(subframe_buffer->ul_config_req, pnf_p7); - } - else - { - // send dummy - if(pnf_p7->_public.ul_tti_req_fn && pnf_p7->_public.dummy_slot.ul_tti_req) - { - pnf_p7->_public.dummy_slot.ul_tti_req->SFN = sfn; - pnf_p7->_public.dummy_slot.ul_tti_req->Slot = slot; - (pnf_p7->_public.ul_tti_req_fn)(NULL, &(pnf_p7->_public), pnf_p7->_public.dummy_slot.ul_tti_req); - } - } - //if(subframe_buffer->dl_config_req != 0) - //deallocate_nfapi_dl_config_request(subframe_buffer->dl_config_req, pnf_p7); - //if(subframe_buffer->tx_req != 0) - //deallocate_nfapi_tx_request(subframe_buffer->tx_req, pnf_p7); - if(slot_buffer->ul_tti_req != 0) - { - deallocate_nfapi_ul_tti_request(slot_buffer->ul_tti_req, pnf_p7); - slot_buffer->ul_tti_req = 0; - - } - #if 0 - if(slot_buffer->lbt_dl_config_req != 0) - { - deallocate_nfapi_lbt_dl_config_request(slot_buffer->lbt_dl_config_req, pnf_p7); - slot_buffer->lbt_dl_config_req = 0; - } - #endif - } // sfn_slot match - - - if ( slot_buffer->dl_tti_req == 0 && - slot_buffer->tx_data_req == 0 && - slot_buffer->ul_tti_req == 0) - //slot_buffer->lbt_dl_config_req == 0 && - //slot_buffer->ue_release_req == 0) - { - memset(&(pnf_p7->slot_buffer[buffer_index]), 0, sizeof(nfapi_pnf_p7_slot_buffer_t)); - pnf_p7->slot_buffer[buffer_index].sfn = -1; - pnf_p7->slot_buffer[buffer_index].slot = -1; + memset(&(pnf_p7->slot_buffer[buffer_index_rx]), 0, sizeof(nfapi_pnf_p7_slot_buffer_t)); + pnf_p7->slot_buffer[buffer_index_rx].sfn = -1; + pnf_p7->slot_buffer[buffer_index_rx].slot = -1; } //printf("pnf_p7->_public.timing_info_mode_periodic:%d pnf_p7->timing_info_period_counter:%d pnf_p7->_public.timing_info_period:%d\n", pnf_p7->_public.timing_info_mode_periodic, pnf_p7->timing_info_period_counter, pnf_p7->_public.timing_info_period); //printf("pnf_p7->_public.timing_info_mode_aperiodic:%d pnf_p7->timing_info_aperiodic_send:%d\n", pnf_p7->_public.timing_info_mode_aperiodic, pnf_p7->timing_info_aperiodic_send); //printf("pnf_p7->timing_info_ms_counter:%d\n", pnf_p7->timing_info_ms_counter); - // send the periodic timing info if configured + //send the periodic timing info if configured if(pnf_p7->_public.timing_info_mode_periodic && (pnf_p7->timing_info_period_counter++) == pnf_p7->_public.timing_info_period) { pnf_nr_pack_and_send_timing_info(pnf_p7); @@ -1167,26 +1145,6 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl } } - else - { - //send_dummy_subframe(pnf_p7, sfn_sf_tx); - } - - - //printf("pnf_p7->tick:%d\n", pnf_p7->tick); - // if(pnf_p7->tick == 1000) // why? - // { - // // TODO: change stats to nr_stats - // NFAPI_TRACE(NFAPI_TRACE_INFO, "[PNF P7:%d] (ONTIME/LATE) DL:(%d/%d) UL:(%d/%d) HI:(%d/%d) TX:(%d/%d)\n", pnf_p7->_public.phy_id, - // pnf_p7->stats.dl_conf_ontime, pnf_p7->stats.dl_conf_late, - // pnf_p7->stats.ul_conf_ontime, pnf_p7->stats.ul_conf_late, - // pnf_p7->stats.hi_dci0_ontime, pnf_p7->stats.hi_dci0_late, - // pnf_p7->stats.tx_ontime, pnf_p7->stats.tx_late); - // pnf_p7->tick = 0; - // memset(&pnf_p7->stats, 0, sizeof(pnf_p7->stats)); - // } - // pnf_p7->tick++; - if(pthread_mutex_unlock(&(pnf_p7->mutex)) != 0) { @@ -1495,7 +1453,7 @@ uint8_t is_nr_p7_request_in_window(uint16_t sfn,uint16_t slot, const char* name, { uint32_t recv_sfn_slot_dec = NFAPI_SFNSLOT2DEC(sfn,slot); uint32_t current_sfn_slot_dec = NFAPI_SFNSLOT2DEC(phy->sfn,phy->slot); - printf("p7_msg_sfn: %d, p7_msg_slot: %d, phy_sfn:%d , phy_slot:%d \n",sfn,slot,phy->sfn,phy->slot); + //printf("p7_msg_sfn: %d, p7_msg_slot: %d, phy_sfn:%d , phy_slot:%d \n",sfn,slot,phy->sfn,phy->slot); uint8_t in_window = 0; uint8_t timing_window = phy->_public.slot_buffer_size; @@ -1546,11 +1504,11 @@ uint8_t is_nr_p7_request_in_window(uint16_t sfn,uint16_t slot, const char* name, // } if(current_sfn_slot_dec <= recv_sfn_slot_dec + timing_window){ in_window = 1; - NFAPI_TRACE(NFAPI_TRACE_NOTE, "[%d] %s is in window %d\n", current_sfn_slot_dec, name, recv_sfn_slot_dec); + //NFAPI_TRACE(NFAPI_TRACE_NOTE, "[%d] %s is in window %d\n", current_sfn_slot_dec, name, recv_sfn_slot_dec); } else if(current_sfn_slot_dec + NFAPI_MAX_SFNSLOTDEC <= recv_sfn_slot_dec + timing_window){ //checking for wrap in_window = 1; - NFAPI_TRACE(NFAPI_TRACE_NOTE, "[%d] %s is in window %d\n", current_sfn_slot_dec, name, recv_sfn_slot_dec); + //NFAPI_TRACE(NFAPI_TRACE_NOTE, "[%d] %s is in window %d\n", current_sfn_slot_dec, name, recv_sfn_slot_dec); } else @@ -1630,7 +1588,6 @@ void pnf_handle_dl_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7) NFAPI_TRACE(NFAPI_TRACE_INFO, "%s failed to alloced nfapi_dl_tti_request structure\n"); return; } - int unpack_result = nfapi_nr_p7_message_unpack(pRecvMsg, recvMsgLen, req, sizeof(nfapi_nr_dl_tti_request_t), &(pnf_p7->_public.codec_config)); if(unpack_result == 0) @@ -1640,34 +1597,18 @@ void pnf_handle_dl_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7) NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to lock mutex\n"); return; } -#if 0 - if ( - 0 && - (NFAPI_SFNSF2DEC(req->sfn_sf) % 100 ==0 || - NFAPI_SFNSF2DEC(req->sfn_sf) % 105 ==0 - ) - ) - NFAPI_TRACE(NFAPI_TRACE_INFO, "DL_CONFIG.req sfn_sf:%d pdcch:%u dci:%u pdu:%u pdsch_rnti:%u pcfich:%u\n", - NFAPI_SFNSF2DEC(req->sfn_sf), - req->dl_config_request_body.number_pdcch_ofdm_symbols, - req->dl_config_request_body.number_dci, - req->dl_config_request_body.number_pdu, - req->dl_config_request_body.number_pdsch_rnti, - req->dl_config_request_body.transmission_power_pcfich - ); -#endif - if(is_nr_p7_request_in_window(req->SFN,req->Slot, "dl_tti_request", pnf_p7)) - { - uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(req->SFN,req->Slot); - uint8_t buffer_index = sfn_slot_dec % pnf_p7->_public.slot_buffer_size; + if(is_nr_p7_request_in_window(req->SFN,req->Slot, "dl_tti_request", pnf_p7)) + { + uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(req->SFN,req->Slot); + uint8_t buffer_index = sfn_slot_dec % 20; - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); - //NFAPI_TRACE(NFAPI_TRACE_INFO,"%s() %ld.%09ld POPULATE DL_TTI_REQ sfn_slot:%d buffer_index:%d\n", __FUNCTION__, t.tv_sec, t.tv_nsec, sfn_slot_dec, buffer_index); + NFAPI_TRACE(NFAPI_TRACE_INFO,"%s() %ld.%09ld POPULATE DL_TTI_REQ current tx sfn/slot:%d.%d p7 msg sfn/slot: %d.%d buffer_index:%d\n", __FUNCTION__, t.tv_sec, t.tv_nsec, pnf_p7->sfn,pnf_p7->slot, req->SFN, req->Slot, buffer_index); - // if there is already an dl_config_req make sure we free it. + // if there is already an dl_tti_req make sure we free it. if(pnf_p7->slot_buffer[buffer_index].dl_tti_req != 0) { NFAPI_TRACE(NFAPI_TRACE_NOTE, "%s() is_nr_p7_request_in_window()=TRUE buffer_index occupied - free it first sfn_slot:%d buffer_index:%d\n", __FUNCTION__, NFAPI_SFNSLOT2DEC(req->SFN,req->Slot), buffer_index); @@ -1677,7 +1618,7 @@ void pnf_handle_dl_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7) deallocate_nfapi_dl_tti_request(pnf_p7->slot_buffer[buffer_index].dl_tti_req, pnf_p7); } - // saving dl_config_request in subframe buffer + // filling dl_tti_request in slot buffer pnf_p7->slot_buffer[buffer_index].sfn = req->SFN; pnf_p7->slot_buffer[buffer_index].slot = req->Slot; pnf_p7->slot_buffer[buffer_index].dl_tti_req = req; @@ -1826,12 +1767,12 @@ void pnf_handle_ul_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7) if(is_nr_p7_request_in_window(req->SFN,req->Slot, "ul_tti_request", pnf_p7)) { uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(req->SFN,req->Slot); - uint8_t buffer_index = sfn_slot_dec % pnf_p7->_public.slot_buffer_size; + uint8_t buffer_index = (sfn_slot_dec % 20); struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - NFAPI_TRACE(NFAPI_TRACE_INFO,"%s() %ld.%09ld POPULATE UL_TTI_REQ sfn_slot:%d buffer_index:%d\n", __FUNCTION__, t.tv_sec, t.tv_nsec, sfn_slot_dec, buffer_index); + NFAPI_TRACE(NFAPI_TRACE_INFO,"%s() %ld.%09ld POPULATE UL_TTI_REQ current tx sfn/slot:%d.%d p7 msg sfn/slot: %d.%d buffer_index:%d\n", __FUNCTION__, t.tv_sec, t.tv_nsec, pnf_p7->sfn,pnf_p7->slot, req->SFN, req->Slot, buffer_index); if(pnf_p7->slot_buffer[buffer_index].ul_tti_req != 0) { @@ -1841,6 +1782,8 @@ void pnf_handle_ul_tti_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7) deallocate_nfapi_ul_tti_request(pnf_p7->slot_buffer[buffer_index].ul_tti_req, pnf_p7); } + + //filling slot buffer pnf_p7->slot_buffer[buffer_index].sfn = req->SFN; pnf_p7->slot_buffer[buffer_index].slot = req->Slot; @@ -1972,7 +1915,7 @@ void pnf_handle_ul_dci_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7) if(is_nr_p7_request_in_window(req->SFN,req->Slot,"ul_dci_request", pnf_p7)) { uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(req->SFN,req->Slot); - uint8_t buffer_index = sfn_slot_dec % pnf_p7->_public.slot_buffer_size; + uint8_t buffer_index = sfn_slot_dec % 20; if(pnf_p7->slot_buffer[buffer_index].ul_dci_req!= 0) { @@ -2010,7 +1953,7 @@ void pnf_handle_ul_dci_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7) } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack hi_dci0_req\n"); + NFAPI_TRACE(NFAPI_TRACE_ERROR, "Failed to unpack UL DCI req\n"); deallocate_nfapi_ul_dci_request(req, pnf_p7); } } @@ -2111,12 +2054,12 @@ void pnf_handle_tx_data_request(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7 if(is_nr_p7_request_in_window(req->SFN, req->Slot,"tx_request", pnf_p7)) { uint32_t sfn_slot_dec = NFAPI_SFNSLOT2DEC(req->SFN,req->Slot); - uint8_t buffer_index = sfn_slot_dec % pnf_p7->_public.slot_buffer_size; + uint8_t buffer_index = sfn_slot_dec % 20; struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - NFAPI_TRACE(NFAPI_TRACE_INFO,"%s() %ld.%09ld POPULATE TX_DATA_REQ sfn_sf:%d buffer_index:%d\n", __FUNCTION__, t.tv_sec, t.tv_nsec, sfn_slot_dec, buffer_index); + //NFAPI_TRACE(NFAPI_TRACE_INFO,"%s() %ld.%09ld POPULATE TX_DATA_REQ sfn_sf:%d buffer_index:%d\n", __FUNCTION__, t.tv_sec, t.tv_nsec, sfn_slot_dec, buffer_index); #if 0 if (0 && NFAPI_SFNSF2DEC(req->sfn_sf)%100==0) NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() TX_REQ.req sfn_sf:%d pdus:%d - TX_REQ is within window\n", __FUNCTION__, @@ -2678,9 +2621,7 @@ void pnf_nr_dispatch_p7_message(void *pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7 case NFAPI_NR_PHY_MSG_TYPE_DL_NODE_SYNC: pnf_nr_handle_dl_node_sync(pRecvMsg, recvMsgLen, pnf_p7, rx_hr_time); break; - case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST: - //printf("\nEntering pnf_handle_dl_tti_request sfn=%d,slot=%d \n",pnf_p7->sfn,pnf_p7->slot); pnf_handle_dl_tti_request(pRecvMsg, recvMsgLen, pnf_p7); break; case NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST: @@ -2989,13 +2930,11 @@ void pnf_nr_nfapi_p7_read_dispatch_message(pnf_p7_t* pnf_p7, uint32_t now_hr_tim struct sockaddr_in remote_addr; socklen_t remote_addr_size = sizeof(remote_addr); remote_addr.sin_family = 2; //hardcoded - do { // peek the header uint8_t header_buffer[NFAPI_P7_HEADER_LENGTH]; recvfrom_result = recvfrom(pnf_p7->p7_sock, header_buffer, NFAPI_P7_HEADER_LENGTH, MSG_DONTWAIT | MSG_PEEK, (struct sockaddr*)&remote_addr, &remote_addr_size); - if(recvfrom_result > 0) { // get the segment size @@ -3272,7 +3211,7 @@ int pnf_nr_p7_message_pump(pnf_p7_t* pnf_p7) FD_SET(pnf_p7->p7_sock, &rfds); struct timeval timeout; - timeout.tv_sec = 1; + timeout.tv_sec = 100; timeout.tv_usec = 0; selectRetval = select(pnf_p7->p7_sock+1, &rfds, NULL, NULL, &timeout); diff --git a/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c b/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c index efcb60ce96..0db1feb398 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c @@ -265,3 +265,77 @@ int nfapi_pnf_ue_release_resp(nfapi_pnf_p7_config_t* config, nfapi_ue_release_re return pnf_p7_pack_and_send_p7_message(_this, &(resp->header), sizeof(nfapi_ue_release_response_t)); } + +//NR UPLINK INDICATION + +int nfapi_pnf_p7_nr_slot_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_slot_indication_scf_t* ind) +{ + if(config == NULL || ind == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: invalid input params\n", __FUNCTION__); + return -1; + } + + pnf_p7_t* _this = (pnf_p7_t*)(config); + return pnf_nr_p7_pack_and_send_p7_message(_this, (nfapi_p7_message_header_t*)ind, sizeof(nfapi_nr_rx_data_indication_t)); +} + +int nfapi_pnf_p7_nr_rx_data_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_rx_data_indication_t* ind) +{ + if(config == NULL || ind == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: invalid input params\n", __FUNCTION__); + return -1; + } + + pnf_p7_t* _this = (pnf_p7_t*)(config); + return pnf_nr_p7_pack_and_send_p7_message(_this, (nfapi_p7_message_header_t*)ind, sizeof(nfapi_nr_rx_data_indication_t)); +} + +int nfapi_pnf_p7_nr_crc_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_crc_indication_t* ind) +{ + if(config == NULL || ind == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: invalid input params\n", __FUNCTION__); + return -1; + } + + pnf_p7_t* _this = (pnf_p7_t*)(config); + return pnf_nr_p7_pack_and_send_p7_message(_this, (nfapi_p7_message_header_t*)ind, sizeof(nfapi_nr_crc_indication_t)); +} + +int nfapi_pnf_p7_nr_srs_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_srs_indication_t* ind) +{ + if(config == NULL || ind == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: invalid input params\n", __FUNCTION__); + return -1; + } + + pnf_p7_t* _this = (pnf_p7_t*)(config); + return pnf_nr_p7_pack_and_send_p7_message(_this, (nfapi_p7_message_header_t*)ind, sizeof(nfapi_nr_srs_indication_t)); +} + +int nfapi_pnf_p7_nr_uci_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_uci_indication_t* ind) +{ + if(config == NULL || ind == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: invalid input params\n", __FUNCTION__); + return -1; + } + + pnf_p7_t* _this = (pnf_p7_t*)(config); + return pnf_nr_p7_pack_and_send_p7_message(_this, (nfapi_p7_message_header_t*)ind, sizeof(nfapi_nr_uci_indication_t)); +} + +int nfapi_pnf_p7_nr_rach_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_rach_indication_t* ind) +{ + if(config == NULL || ind == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: invalid input params\n", __FUNCTION__); + return -1; + } + + pnf_p7_t* _this = (pnf_p7_t*)(config); + return pnf_nr_p7_pack_and_send_p7_message(_this, (nfapi_p7_message_header_t*)ind, sizeof(nfapi_nr_rach_indication_t)); +} diff --git a/nfapi/open-nFAPI/pnf_sim/src/fapi_stub.cpp b/nfapi/open-nFAPI/pnf_sim/src/fapi_stub.cpp index 4fe5065cea..1c2f0bef80 100644 --- a/nfapi/open-nFAPI/pnf_sim/src/fapi_stub.cpp +++ b/nfapi/open-nFAPI/pnf_sim/src/fapi_stub.cpp @@ -278,7 +278,7 @@ void *fapi_thread_start(void *ptr) { if(instance->tick == 1000) { if(instance->tx_byte_count > 0) { - printf("[FAPI] Tx rate %u bytes/sec\n", instance->tx_byte_count); + printf("[FAPI] Tx rate %d bytes/sec\n", instance->tx_byte_count); instance->tx_byte_count = 0; } @@ -319,7 +319,7 @@ void *fapi_thread_start(void *ptr) { millisec = now_ts.tv_nsec / 1e6; if(last_millisec != -1 && ((last_millisec + 1 ) % 1000) != millisec) { - printf("*** missing millisec %u %u\n", last_millisec, millisec); + printf("*** missing millisec %d %d\n", last_millisec, millisec); catchup = millisec - last_millisec - 1; } diff --git a/nfapi/open-nFAPI/vnf/inc/vnf_p7.h b/nfapi/open-nFAPI/vnf/inc/vnf_p7.h index ad1c5e7944..4fcc99f79b 100644 --- a/nfapi/open-nFAPI/vnf/inc/vnf_p7.h +++ b/nfapi/open-nFAPI/vnf/inc/vnf_p7.h @@ -25,7 +25,6 @@ #define TIME2TIMEHR(_time) (((uint32_t)(_time.tv_sec) & 0xFFF) << 20 | ((uint32_t)(_time.tv_usec) & 0xFFFFF)) - typedef struct { uint8_t* buffer; uint16_t length; diff --git a/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h b/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h index 518b01864e..31328393c9 100644 --- a/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h +++ b/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h @@ -852,7 +852,15 @@ typedef struct nfapi_vnf_p7_config * use the codec_config.deallocate function to release it at a future point */ int (*nrach_indication)(struct nfapi_vnf_p7_config* config, nfapi_nrach_indication_t* ind); - + + //The NR indication functions below copy uplink information received at the VNF into the UL info struct + int (*nr_slot_indication)(nfapi_nr_slot_indication_scf_t* ind); + int (*nr_crc_indication)(nfapi_nr_crc_indication_t* ind); + int (*nr_rx_data_indication)(nfapi_nr_rx_data_indication_t* ind); + int (*nr_uci_indication)(nfapi_nr_uci_indication_t* ind); + int (*nr_rach_indication)(nfapi_nr_rach_indication_t* ind); + int (*nr_srs_indication)(nfapi_nr_srs_indication_t* ind); + /*! A callback for any vendor extension messages * \param config A pointer to the vnf p7 configuration * \param msg A data structure for the decoded vendor extention message allocated diff --git a/nfapi/open-nFAPI/vnf/src/vnf_p7.c b/nfapi/open-nFAPI/vnf/src/vnf_p7.c index ffbc5819bc..e390031554 100644 --- a/nfapi/open-nFAPI/vnf/src/vnf_p7.c +++ b/nfapi/open-nFAPI/vnf/src/vnf_p7.c @@ -22,7 +22,7 @@ #include #include #include - +#include #include "vnf_p7.h" #define SYNC_CYCLE_COUNT 2 @@ -456,7 +456,7 @@ int send_mac_subframe_indications(vnf_p7_t* vnf_p7) int vnf_send_p7_msg(vnf_p7_t* vnf_p7, nfapi_vnf_p7_connection_info_t* p7_info, uint8_t* msg, const uint32_t len) { int sendto_result = sendto(vnf_p7->socket, msg, len, 0, (struct sockaddr*)&(p7_info->remote_addr), sizeof(p7_info->remote_addr)); - //printf("\nSending p7 message sfn=%d,slot=%d\n",vnf_p7->p7_connections->sfn,vnf_p7->p7_connections->slot); + //printf("P7 msg sent \n"); if(sendto_result != len) { NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() sendto_result %d %d\n", __FUNCTION__, sendto_result, errno); @@ -533,6 +533,7 @@ int vnf_nr_p7_pack_and_send_p7_msg(vnf_p7_t* vnf_p7, nfapi_p7_message_header_t* nfapi_p7_update_transmit_timestamp(buffer, calculate_transmit_timestamp(p7_connection->sfn, p7_connection->slot, vnf_p7->slot_start_time_hr)); send_result = vnf_send_p7_msg(vnf_p7, p7_connection, &tx_buffer[0], segment_size); + } } else @@ -549,7 +550,6 @@ int vnf_nr_p7_pack_and_send_p7_msg(vnf_p7_t* vnf_p7, nfapi_p7_message_header_t* } p7_connection->sequence_number++; - return send_result; } else @@ -667,7 +667,6 @@ int vnf_build_send_dl_node_sync(vnf_p7_t* vnf_p7, nfapi_vnf_p7_connection_info_t int vnf_nr_build_send_dl_node_sync(vnf_p7_t* vnf_p7, nfapi_vnf_p7_connection_info_t* p7_info) { - nfapi_nr_dl_node_sync_t dl_node_sync; memset(&dl_node_sync, 0, sizeof(dl_node_sync)); @@ -1290,7 +1289,7 @@ void vnf_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) { if(phy->in_sync == 0) { - NFAPI_TRACE(NFAPI_TRACE_NOTE, "VNF P7 In Sync with phy (phy_id:%d)\n", phy->phy_id); + //NFAPI_TRACE(NFAPI_TRACE_NOTE, "VNF P7 In Sync with phy (phy_id:%d)\n", phy->phy_id); if(vnf_p7->_public.sync_indication) (vnf_p7->_public.sync_indication)(&(vnf_p7->_public), 1); @@ -1454,6 +1453,161 @@ void vnf_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) } } +//NR HANDLES FOR UPLINK MESSAGES +void vnf_handle_nr_slot_indication(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) +{ + // ensure it's valid + if (pRecvMsg == NULL || vnf_p7 == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + } + else + { + nfapi_nr_slot_indication_scf_t ind; + + if(nfapi_nr_p7_message_unpack(pRecvMsg, recvMsgLen, &ind, sizeof(ind), &vnf_p7->_public.codec_config) < 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__); + } + else + { + if(vnf_p7->_public.nr_slot_indication) + { + (vnf_p7->_public.nr_slot_indication)(&ind); + } + } + + } +} +void vnf_handle_nr_rx_data_indication(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) +{ + // ensure it's valid + if (pRecvMsg == NULL || vnf_p7 == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + } + else + { + nfapi_nr_rx_data_indication_t ind; + + if(nfapi_nr_p7_message_unpack(pRecvMsg, recvMsgLen, &ind, sizeof(ind), &vnf_p7->_public.codec_config) < 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__); + } + else + { + if(vnf_p7->_public.nr_rx_data_indication) + { + (vnf_p7->_public.nr_rx_data_indication)(&ind); + } + } + + } +} + +void vnf_handle_nr_crc_indication(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) +{ + // ensure it's valid + if (pRecvMsg == NULL || vnf_p7 == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + } + else + { + nfapi_nr_crc_indication_t ind; + + if(nfapi_nr_p7_message_unpack(pRecvMsg, recvMsgLen, &ind, sizeof(ind), &vnf_p7->_public.codec_config) < 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__); + } + else + { + if(vnf_p7->_public.nr_crc_indication) + { + (vnf_p7->_public.nr_crc_indication)(&ind); + } + } + + } +} + +void vnf_handle_nr_srs_indication(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) +{ + // ensure it's valid + if (pRecvMsg == NULL || vnf_p7 == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + } + else + { + nfapi_nr_srs_indication_t ind; + + if(nfapi_nr_p7_message_unpack(pRecvMsg, recvMsgLen, &ind, sizeof(ind), &vnf_p7->_public.codec_config) < 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__); + } + else + { + if(vnf_p7->_public.nr_srs_indication) + { + (vnf_p7->_public.nr_srs_indication)(&ind); + } + } + } +} + +void vnf_handle_nr_uci_indication(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) +{ + // ensure it's valid + if (pRecvMsg == NULL || vnf_p7 == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + } + else + { + nfapi_nr_uci_indication_t ind; + + if(nfapi_nr_p7_message_unpack(pRecvMsg, recvMsgLen, &ind, sizeof(ind), &vnf_p7->_public.codec_config) < 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__); + } + else + { + if(vnf_p7->_public.nr_uci_indication) + { + (vnf_p7->_public.nr_uci_indication)(&ind); + } + } + + } +} + +void vnf_handle_nr_rach_indication(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) +{ + // ensure it's valid + if (pRecvMsg == NULL || vnf_p7 == NULL) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + } + else + { + nfapi_nr_rach_indication_t ind; + + if(nfapi_nr_p7_message_unpack(pRecvMsg, recvMsgLen, &ind, sizeof(ind), &vnf_p7->_public.codec_config) < 0) + { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__); + } + else + { + if(vnf_p7->_public.nr_rach_indication) + { + (vnf_p7->_public.nr_rach_indication)(&ind); + } + } + + } +} + void vnf_nr_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7) { //printf("received UL Node sync"); @@ -1484,6 +1638,8 @@ void vnf_nr_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7 // divide by 2 using shift operator uint32_t latency = (tx_2_rx - pnf_proc_time) >> 1; + //phy->in_sync = 1; + if(!(phy->filtered_adjust)) { phy->latency[phy->min_sync_cycle_count] = latency; @@ -1533,17 +1689,24 @@ void vnf_nr_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7 struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); - //NFAPI_TRACE(NFAPI_TRACE_NOTE, "(%4d/%1d) %d.%d PNF to VNF phy_id:%2d (t1/2/3/4:%8u, %8u, %8u, %8u) txrx:%4u procT:%3u latency(us):%4d(avg:%4d) offset(us):%8d filtered(us):%8d wrap[t1:%u t2:%u]\n", - // phy->sfn, phy->slot, ts.tv_sec, ts.tv_nsec, ind.header.phy_id, - // ind.t1, ind.t2, ind.t3, t4, - // tx_2_rx, pnf_proc_time, latency, phy->average_latency, phy->slot_offset, phy->slot_offset_filtered, - // (ind.t1previous_t1), (ind.t2previous_t2)); + // NFAPI_TRACE(NFAPI_TRACE_NOTE, "(%4d/%1d) %d.%d PNF to VNF phy_id:%2d (t1/2/3/4:%8u, %8u, %8u, %8u) txrx:%4u procT:%3u latency(us):%4d(avg:%4d) offset(us):%8d filtered(us):%8d wrap[t1:%u t2:%u]\n", + // phy->sfn, phy->slot, ts.tv_sec, ts.tv_nsec, ind.header.phy_id, + // ind.t1, ind.t2, ind.t3, t4, + // tx_2_rx, pnf_proc_time, latency, phy->average_latency, phy->slot_offset, phy->slot_offset_filtered, + // (ind.t1previous_t1), (ind.t2previous_t2)); } } if (phy->filtered_adjust && (phy->slot_offset_filtered > 1e6 || phy->slot_offset_filtered < -1e6)) - { + { struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + NFAPI_TRACE(NFAPI_TRACE_NOTE, "(%4d/%1d) %d.%d PNF to VNF phy_id:%2d (t1/2/3/4:%8u, %8u, %8u, %8u) txrx:%4u procT:%3u latency(us):%4d(avg:%4d) offset(us):%8d filtered(us):%8d wrap[t1:%u t2:%u]\n", + phy->sfn, phy->slot, ts.tv_sec, ts.tv_nsec, ind.header.phy_id, + ind.t1, ind.t2, ind.t3, t4, + tx_2_rx, pnf_proc_time, latency, phy->average_latency, phy->slot_offset, phy->slot_offset_filtered, + (ind.t1previous_t1), (ind.t2previous_t2)); + phy->filtered_adjust = 0; phy->zero_count=0; phy->min_sync_cycle_count = 2; @@ -1575,7 +1738,7 @@ void vnf_nr_handle_ul_node_sync(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7 sfn_slot_dec += (phy->slot_offset / 500); - NFAPI_TRACE(NFAPI_TRACE_NOTE, "PNF to VNF slot offset:%d sfn :%d slot:%d \n",phy->slot_offset,NFAPI_SFNSLOTDEC2SFN(sfn_slot_dec),NFAPI_SFNSLOTDEC2SLOT(sfn_slot_dec) ); + //NFAPI_TRACE(NFAPI_TRACE_NOTE, "PNF to VNF slot offset:%d sfn :%d slot:%d \n",phy->slot_offset,NFAPI_SFNSLOTDEC2SFN(sfn_slot_dec),NFAPI_SFNSLOTDEC2SLOT(sfn_slot_dec) ); } @@ -2033,47 +2196,31 @@ void vnf_nr_dispatch_p7_message(void *pRecvMsg, int recvMsgLen, vnf_p7_t* vnf_p7 case NFAPI_TIMING_INFO: vnf_nr_handle_timing_info(pRecvMsg, recvMsgLen, vnf_p7); break; - - case NFAPI_HARQ_INDICATION: - vnf_handle_harq_indication(pRecvMsg, recvMsgLen, vnf_p7); + + case NFAPI_NR_PHY_MSG_TYPE_SLOT_INDICATION: + vnf_handle_nr_slot_indication(pRecvMsg, recvMsgLen, vnf_p7); + break; + + case NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION: + vnf_handle_nr_rx_data_indication(pRecvMsg, recvMsgLen, vnf_p7); break; - case NFAPI_CRC_INDICATION: - vnf_handle_crc_indication(pRecvMsg, recvMsgLen, vnf_p7); + case NFAPI_NR_PHY_MSG_TYPE_CRC_INDICATION: + vnf_handle_nr_crc_indication(pRecvMsg, recvMsgLen, vnf_p7); break; - case NFAPI_RX_ULSCH_INDICATION: - vnf_handle_rx_ulsch_indication(pRecvMsg, recvMsgLen, vnf_p7); + case NFAPI_NR_PHY_MSG_TYPE_UCI_INDICATION: + vnf_handle_nr_uci_indication(pRecvMsg, recvMsgLen, vnf_p7); break; - case NFAPI_RACH_INDICATION: - vnf_handle_rach_indication(pRecvMsg, recvMsgLen, vnf_p7); + case NFAPI_NR_PHY_MSG_TYPE_SRS_INDICATION: + vnf_handle_nr_srs_indication(pRecvMsg, recvMsgLen, vnf_p7); break; - case NFAPI_SRS_INDICATION: - vnf_handle_srs_indication(pRecvMsg, recvMsgLen, vnf_p7); + case NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION: + vnf_handle_nr_rach_indication(pRecvMsg, recvMsgLen, vnf_p7); break; - case NFAPI_RX_SR_INDICATION: - vnf_handle_rx_sr_indication(pRecvMsg, recvMsgLen, vnf_p7); - break; - - case NFAPI_RX_CQI_INDICATION: - vnf_handle_rx_cqi_indication(pRecvMsg, recvMsgLen, vnf_p7); - break; - - case NFAPI_LBT_DL_INDICATION: - vnf_handle_lbt_dl_indication(pRecvMsg, recvMsgLen, vnf_p7); - break; - - case NFAPI_NB_HARQ_INDICATION: - vnf_handle_nb_harq_indication(pRecvMsg, recvMsgLen, vnf_p7); - break; - - case NFAPI_NRACH_INDICATION: - vnf_handle_nrach_indication(pRecvMsg, recvMsgLen, vnf_p7); - break; - case NFAPI_UE_RELEASE_RESPONSE: vnf_handle_ue_release_resp(pRecvMsg, recvMsgLen, vnf_p7); break; diff --git a/nfapi/open-nFAPI/vnf/src/vnf_p7_interface.c b/nfapi/open-nFAPI/vnf/src/vnf_p7_interface.c index 3fa1f035fd..8cebf2b468 100644 --- a/nfapi/open-nFAPI/vnf/src/vnf_p7_interface.c +++ b/nfapi/open-nFAPI/vnf/src/vnf_p7_interface.c @@ -25,9 +25,14 @@ #include #include "vnf_p7.h" +#include "nfapi_vnf.h" +#include "common/ran_context.h" + +#include "openair1/PHY/defs_gNB.h" #define FAPI2_IP_DSCP 0 +extern RAN_CONTEXT_t RC; nfapi_vnf_p7_config_t* nfapi_vnf_p7_config_create() { @@ -92,7 +97,9 @@ struct timespec timespec_sub(struct timespec lhs, struct timespec rhs) // monitor the p7 endpoints and the timing loop and // send indications to mac int nfapi_nr_vnf_p7_start(nfapi_vnf_p7_config_t* config) -{ +{ + struct PHY_VARS_gNB_s *gNB = RC.gNB[0]; + uint8_t prev_slot = 0; if(config == 0) return -1; @@ -145,40 +152,14 @@ int nfapi_nr_vnf_p7_start(nfapi_vnf_p7_config_t* config) //struct timespec original_pselect_timeout; struct timespec pselect_timeout; - pselect_timeout.tv_sec = 0; - pselect_timeout.tv_nsec = 500000; // ns in a 0.5 ms - //pselect_timeout.tv_nsec = 500000; + pselect_timeout.tv_sec = 100; + pselect_timeout.tv_nsec = 0; - struct timespec pselect_start; - struct timespec pselect_stop; - - //struct timespec sf_end; - - long last_millisecond = -1; - - -// struct timespec sf_duration; //Change to slot_duration? -// sf_duration.tv_sec = 0; -// sf_duration.tv_nsec = 0.5e6; // We want 1ms pause //We want 0.5 ms pause for NR - struct timespec slot_duration; - slot_duration.tv_sec = 0; - //slot_duration.tv_nsec = 0.5e6; - slot_duration.tv_nsec = 0.5e6; - - -// struct timespec sf_start; //Change to slot_start? - struct timespec slot_start; -// clock_gettime(CLOCK_MONOTONIC, &sf_start); - clock_gettime(CLOCK_MONOTONIC, &slot_start); - long millisecond = slot_start.tv_nsec / 1e6; //Check if we have to change - //long millisecond = slot_start.tv_nsec / 0.5e6; -// sf_start = timespec_add(sf_start, sf_duration); - slot_start = timespec_add(slot_start, slot_duration); - - NFAPI_TRACE(NFAPI_TRACE_INFO, "next slot will start at %d.%d\n", slot_start.tv_sec, slot_start.tv_nsec); - + struct timespec ref_time; + clock_gettime(CLOCK_MONOTONIC, &ref_time); + uint8_t setup_time; while(vnf_p7->terminate == 0) - { + { fd_set rfds; int maxSock = 0; FD_ZERO(&rfds); @@ -188,239 +169,33 @@ int nfapi_nr_vnf_p7_start(nfapi_vnf_p7_config_t* config) FD_SET(vnf_p7->socket, &rfds); maxSock = vnf_p7->socket; - clock_gettime(CLOCK_MONOTONIC, &pselect_start); - //long millisecond = pselect_start.tv_nsec / 1e6; + struct timespec curr_time; + clock_gettime(CLOCK_MONOTONIC, &curr_time); + setup_time = curr_time.tv_sec - ref_time.tv_sec; - if((last_millisecond == -1) || (millisecond == last_millisecond) || (millisecond == (last_millisecond + 1) % 1000) ) - { - //NFAPI_TRACE(NFAPI_TRACE_INFO, "pselect_start:%d.%d sf_start:%d.%d\n", pselect_start.tv_sec, pselect_start.tv_nsec, sf_start.tv_sec, sf_start.tv_nsec); + if(setup_time > 10 && prev_slot != gNB->UL_INFO.slot){ //Give the VNF sufficient time to setup before starting scheduling - - //if((pselect_start.tv_sec > sf_start.tv_sec) || - // ((pselect_start.tv_sec == sf_start.tv_sec) && (pselect_start.tv_nsec > sf_start.tv_nsec))) - if((pselect_start.tv_sec > slot_start.tv_sec) || ((pselect_start.tv_sec == slot_start.tv_sec) && (pselect_start.tv_nsec > slot_start.tv_nsec))) - { - // overran the end of the subframe we do not want to wait - pselect_timeout.tv_sec = 0; - pselect_timeout.tv_nsec = 0; - - //struct timespec overrun = timespec_sub(pselect_start, sf_start); - //NFAPI_TRACE(NFAPI_TRACE_INFO, "Subframe overrun detected of %d.%d running to catchup\n", overrun.tv_sec, overrun.tv_nsec); - } - else - { - // still time before the end of the subframe wait - //pselect_timeout = timespec_sub(sf_start, pselect_start); - pselect_timeout = timespec_sub(slot_start, pselect_start); - } - -//original_pselect_timeout = pselect_timeout; - - // detemine how long to sleep in ns before the start of the next 1ms - //pselect_timeout.tv_nsec = 1e6 - (pselect_start.tv_nsec % 1000000); - - //uint8_t underrun_possible =0; - - // if we are not sleeping until the next milisecond due to the - // insycn minor adjment flag it so we don't consider it an error - //uint8_t underrun_possible =0; - /* - { - nfapi_vnf_p7_connection_info_t* phy = vnf_p7->p7_connections; - if(phy && phy->in_sync && phy->insync_minor_adjustment != 0 && phy->insync_minor_adjustment_duration > 0 && pselect_start.tv_nsec != 0) - { - NFAPI_TRACE(NFAPI_TRACE_NOTE, "[VNF] Subframe minor adjustment %d (%d->%d)\n", phy->insync_minor_adjustment, - pselect_timeout.tv_nsec, pselect_timeout.tv_nsec - (phy->insync_minor_adjustment * 1000)) - if(phy->insync_minor_adjustment > 0) - { - // todo check we don't go below 0 - if((phy->insync_minor_adjustment * 1000) > pselect_timeout.tv_nsec) - pselect_timeout.tv_nsec = 0; - else - pselect_timeout.tv_nsec = pselect_timeout.tv_nsec - (phy->insync_minor_adjustment * 1000); - - - //underrun_possible = 1; - } - else if(phy->insync_minor_adjustment < 0) - { - // todo check we don't go below 0 - pselect_timeout.tv_nsec = pselect_timeout.tv_nsec - (phy->insync_minor_adjustment * 1000); - - - //phy->insync_minor_adjustment = 0; - phy->insync_minor_adjustment_duration--; - } - } - */ - - -//long wraps = pselect_timeout.tv_nsec % 1e9; - - - selectRetval = pselect(maxSock+1, &rfds, NULL, NULL, &pselect_timeout, NULL); - // selectRetval = pselect(120, &rfds, NULL, NULL, &pselect_timeout, NULL); - - clock_gettime(CLOCK_MONOTONIC, &pselect_stop); - - nfapi_vnf_p7_connection_info_t* phy = vnf_p7->p7_connections; - -if (selectRetval==-1 && errno == 22) -{ -// NFAPI_TRACE(NFAPI_TRACE_ERROR, "INVAL: pselect_timeout:%d.%ld adj[dur:%d adj:%d], sf_dur:%d.%ld\n", -// pselect_timeout.tv_sec, pselect_timeout.tv_nsec, -// phy->insync_minor_adjustment_duration, phy->insync_minor_adjustment, -// sf_duration.tv_sec, sf_duration.tv_nsec); - -NFAPI_TRACE(NFAPI_TRACE_ERROR, "INVAL: pselect_timeout:%d.%ld adj[dur:%d adj:%d], sf_dur:%d.%ld\n", - pselect_timeout.tv_sec, pselect_timeout.tv_nsec, - phy->insync_minor_adjustment_duration, phy->insync_minor_adjustment, - slot_duration.tv_sec, slot_duration.tv_nsec); - -} - if(selectRetval == 0) - { - // calculate the start of the next slot - //sf_start = timespec_add(sf_start, sf_duration); - slot_start = timespec_add(slot_start, slot_duration); - //NFAPI_TRACE(NFAPI_TRACE_INFO, "next subframe will start at %d.%d\n", sf_start.tv_sec, sf_start.tv_nsec); - - if(phy && phy->in_sync && phy->insync_minor_adjustment != 0 && phy->insync_minor_adjustment_duration > 0) - { - long insync_minor_adjustment_ns = (phy->insync_minor_adjustment * 1000); - - //sf_start.tv_nsec -= insync_minor_adjustment_ns; - slot_start.tv_nsec -= insync_minor_adjustment_ns; - -#if 1 -/* if (sf_start.tv_nsec > 1e9) //Change to 0.5e6? - { - sf_start.tv_sec++; - sf_start.tv_nsec-=1e9; - } - else if (sf_start.tv_nsec < 0) - { - sf_start.tv_sec--; - sf_start.tv_nsec+=1e9; - }*/ - if (slot_start.tv_nsec > 1e9) - { - slot_start.tv_sec++; - slot_start.tv_nsec-=1e9; - } - else if (slot_start.tv_nsec < 0) - { - slot_start.tv_sec--; - slot_start.tv_nsec+=1e9; - } -#else - //NFAPI_TRACE(NFAPI_TRACE_NOTE, "[VNF] BEFORE adjustment - Subframe minor adjustment %dus sf_start.tv_nsec:%d\n", phy->insync_minor_adjustment, sf_start.tv_nsec); - if(phy->insync_minor_adjustment > 0) - { - // decrease the subframe duration a little - if (sf_start.tv_nsec > insync_minor_adjustment_ns) - sf_start.tv_nsec -= insync_minor_adjustment_ns; - else - { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "[VNF] Adjustment would make it negative sf:%d.%ld adjust:%ld\n\n\n", sf_start.tv_sec, sf_start.tv_nsec, insync_minor_adjustment_ns); - sf_start.tv_sec--; - sf_start.tv_nsec += 1e9 - insync_minor_adjustment_ns; - } - } - else if(phy->insync_minor_adjustment < 0) - { - // todo check we don't go below 0 - // increase the subframe duration a little - sf_start.tv_nsec += insync_minor_adjustment_ns; - - if (sf_start.tv_nsec < 0) - { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "[VNF] OVERFLOW %d.%ld\n\n\n\n", sf_start.tv_sec, sf_start.tv_nsec); - sf_start.tv_sec++; - sf_start.tv_nsec += 1e9; - } - } -#endif - - //phy->insync_minor_adjustment = 0; - phy->insync_minor_adjustment_duration--; - -// NFAPI_TRACE(NFAPI_TRACE_NOTE, "[VNF] AFTER adjustment - Subframe minor adjustment %dus sf_start.tv_nsec:%d duration:%u\n", -// phy->insync_minor_adjustment, sf_start.tv_nsec, phy->insync_minor_adjustment_duration); - // NFAPI_TRACE(NFAPI_TRACE_NOTE, "[VNF] AFTER adjustment - Slot minor adjustment %dus slot_start.tv_nsec:%d duration:%u\n", - // phy->insync_minor_adjustment, slot_start.tv_nsec, phy->insync_minor_adjustment_duration); - - if (phy->insync_minor_adjustment_duration==0) - { - phy->insync_minor_adjustment = 0; - } - } - /* - long pselect_stop_millisecond = pselect_stop.tv_nsec / 1e6; - if(millisecond == pselect_stop_millisecond) - { - // we have woke up in the same subframe - if(underrun_possible == 0) - NFAPI_TRACE(NFAPI_TRACE_WARN, "subframe pselect underrun %ld (%d.%d)\n", millisecond, pselect_stop.tv_sec, pselect_stop.tv_nsec); - } - else if(((millisecond + 1) % 1000) != pselect_stop_millisecond) - { - // we have overrun the subframe - NFAPI_TRACE(NFAPI_TRACE_WARN, "subframe pselect overrun %ld %ld\n", millisecond, pselect_stop_millisecond); - NFAPI_TRACE(NFAPI_TRACE_WARN, "subframe underrun %ld\n", millisecond); - } - last_millisecond = millisecond; - */ - - //millisecond ++; - millisecond = millisecond + 1; - - } + //Call the scheduler + pthread_mutex_lock(&gNB->UL_INFO_mutex); + gNB->UL_INFO.module_id = gNB->Mod_id; + gNB->UL_INFO.CC_id = gNB->CC_id; + gNB->if_inst->NR_UL_indication(&gNB->UL_INFO); + pthread_mutex_unlock(&gNB->UL_INFO_mutex); + prev_slot = gNB->UL_INFO.slot; } - else - { - // we have overrun the subframe advance to go and collect $200 - if((millisecond - last_millisecond) > 3) - NFAPI_TRACE(NFAPI_TRACE_WARN, "subframe overrun %ld %ld (%ld)\n", millisecond, last_millisecond, millisecond - last_millisecond + 1); - - last_millisecond = ( last_millisecond + 1 ) % 1000; - selectRetval = 0; - } + selectRetval = pselect(maxSock+1, &rfds, NULL, NULL, &pselect_timeout, NULL); if(selectRetval == 0) { - //vnf_p7->sf_start_time_hr = vnf_get_current_time_hr(); - vnf_p7->slot_start_time_hr = vnf_get_current_time_hr(); -struct timespec current_time; - clock_gettime(CLOCK_MONOTONIC, ¤t_time); - // pselect timed out - nfapi_vnf_p7_connection_info_t* curr = vnf_p7->p7_connections; - while(curr != 0) - { - if (curr->slot == 19) - { //curr->slot = 0; - if(curr->sfn == 1023) - curr->sfn=0; - else - curr->sfn++; - curr->slot=0; - } - else - { - curr->slot++; - } - vnf_nr_sync(vnf_p7, curr); - curr = curr->next; - } - send_mac_slot_indications(vnf_p7); + // pselect timed out, continue } else if(selectRetval > 0) { // have a p7 message if(FD_ISSET(vnf_p7->socket, &rfds)) - { - vnf_nr_p7_read_dispatch_message(vnf_p7); + { + vnf_nr_p7_read_dispatch_message(vnf_p7); } } else @@ -432,7 +207,7 @@ struct timespec current_time; } else { - NFAPI_TRACE(NFAPI_TRACE_INFO, "P7 select failed result %d errno %d timeout:%d.%d orginal:%d.%d last_ms:%ld ms:%ld\n", selectRetval, errno, pselect_timeout.tv_sec, pselect_timeout.tv_nsec, pselect_timeout.tv_sec, pselect_timeout.tv_nsec, last_millisecond, millisecond); + //NFAPI_TRACE(NFAPI_TRACE_INFO, "P7 select failed result %d errno %d timeout:%d.%d orginal:%d.%d last_ms:%ld ms:%ld\n", selectRetval, errno, pselect_timeout.tv_sec, pselect_timeout.tv_nsec, pselect_timeout.tv_sec, pselect_timeout.tv_nsec, last_millisecond, millisecond); // should we exit now? if (selectRetval == -1 && errno == 22) // invalid argument??? not sure about timeout duration { @@ -440,10 +215,7 @@ struct timespec current_time; } } } - } - - NFAPI_TRACE(NFAPI_TRACE_INFO, "Closing p7 socket\n"); close(vnf_p7->socket); @@ -862,7 +634,6 @@ int nfapi_vnf_p7_ul_tti_req(nfapi_vnf_p7_config_t* config, nfapi_nr_ul_tti_reque { if(config == 0 || req == 0) return -1; - vnf_p7_t* vnf_p7 = (vnf_p7_t*)config; return vnf_nr_p7_pack_and_send_p7_msg(vnf_p7, &req->header); } diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 7f2f9780d1..0fbaad98eb 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -495,6 +495,7 @@ void nr_phy_config_request_sim(PHY_VARS_gNB *gNB, gNB_config->carrier_config.dl_bandwidth.value = config_bandwidth(mu, N_RB_DL, fp->nr_band); nr_init_frame_parms(gNB_config, fp); + fp->ofdm_offset_divisor = UINT_MAX; gNB->configured = 1; LOG_I(PHY,"gNB configured\n"); } @@ -546,7 +547,9 @@ void nr_phy_config_request(NR_PHY_Config_t *phy_config) { // } RC.gNB[Mod_id]->configured = 1; + fp->ofdm_offset_divisor = RC.gNB[Mod_id]->ofdm_offset_divisor; init_symbol_rotation(fp); + init_timeshift_rotation(fp); LOG_I(PHY,"gNB %d configured\n",Mod_id); } diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index b29d679fe5..b60e77e814 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -124,7 +124,7 @@ void phy_init_nr_ue_PUSCH(NR_UE_PUSCH *const pusch, AssertFatal( pusch, "pusch==0" ); for (int i=0; itxdataF_layers[i] = (int32_t *)malloc16_clear((NR_MAX_PUSCH_ENCODED_LENGTH)*sizeof(int32_t *)); + pusch->txdataF_layers[i] = (int32_t *)malloc16_clear(NR_MAX_PUSCH_ENCODED_LENGTH*sizeof(int32_t)); } } diff --git a/openair1/PHY/MODULATION/modulation_common.h b/openair1/PHY/MODULATION/modulation_common.h index f550eeca5f..1a19bec78c 100644 --- a/openair1/PHY/MODULATION/modulation_common.h +++ b/openair1/PHY/MODULATION/modulation_common.h @@ -49,7 +49,7 @@ void PHY_ofdm_mod(int *input, void normal_prefix_mod(int32_t *txdataF,int32_t *txdata,uint8_t nsymb,LTE_DL_FRAME_PARMS *frame_parms); -void nr_normal_prefix_mod(int32_t *txdataF,int32_t *txdata,uint8_t nsymb,NR_DL_FRAME_PARMS *frame_parms); +void nr_normal_prefix_mod(int32_t *txdataF,int32_t *txdata,uint8_t nsymb,NR_DL_FRAME_PARMS *frame_parms, uint32_t slot); void do_OFDM_mod(int32_t **txdataF, int32_t **txdata, uint32_t frame,uint16_t next_slot, LTE_DL_FRAME_PARMS *frame_parms); diff --git a/openair1/PHY/MODULATION/nr_modulation.c b/openair1/PHY/MODULATION/nr_modulation.c index e32f6bf575..c81a88ff30 100644 --- a/openair1/PHY/MODULATION/nr_modulation.c +++ b/openair1/PHY/MODULATION/nr_modulation.c @@ -745,6 +745,23 @@ void init_symbol_rotation(NR_DL_FRAME_PARMS *fp) { } } +void init_timeshift_rotation(NR_DL_FRAME_PARMS *fp) +{ + for (int i = 0; i < fp->ofdm_symbol_size; i++) { + double poff = -i * 2.0 * M_PI * 144.0 / 2048.0 / fp->ofdm_offset_divisor; + double exp_re = cos(poff); + double exp_im = sin(-poff); + fp->timeshift_symbol_rotation[i*2] = (int16_t)round(exp_re * 32767); + fp->timeshift_symbol_rotation[i*2+1] = (int16_t)round(exp_im * 32767); + + if (i < 10) + LOG_I(PHY,"Timeshift symbol rotation %d => (%d,%d) %f\n",i, + fp->timeshift_symbol_rotation[i*2], + fp->timeshift_symbol_rotation[i*2+1], + poff); + } +} + int nr_layer_precoder(int16_t **datatx_F_precoding, char *prec_matrix, uint8_t n_layers, int32_t re_offset) { int32_t precodatatx_F = 0; diff --git a/openair1/PHY/MODULATION/nr_modulation.h b/openair1/PHY/MODULATION/nr_modulation.h index 547d20952f..75541a1361 100644 --- a/openair1/PHY/MODULATION/nr_modulation.h +++ b/openair1/PHY/MODULATION/nr_modulation.h @@ -117,6 +117,8 @@ void apply_nr_rotation(NR_DL_FRAME_PARMS *fp, void init_symbol_rotation(NR_DL_FRAME_PARMS *fp); +void init_timeshift_rotation(NR_DL_FRAME_PARMS *fp); + void apply_nr_rotation_ul(NR_DL_FRAME_PARMS *frame_parms, int32_t *rxdataF, int slot, diff --git a/openair1/PHY/MODULATION/ofdm_mod.c b/openair1/PHY/MODULATION/ofdm_mod.c index 8cf9f05b9b..2f075e8218 100644 --- a/openair1/PHY/MODULATION/ofdm_mod.c +++ b/openair1/PHY/MODULATION/ofdm_mod.c @@ -63,20 +63,62 @@ void normal_prefix_mod(int32_t *txdataF,int32_t *txdata,uint8_t nsymb,LTE_DL_FRA } -void nr_normal_prefix_mod(int32_t *txdataF,int32_t *txdata,uint8_t nsymb,NR_DL_FRAME_PARMS *frame_parms) +void nr_normal_prefix_mod(int32_t *txdataF,int32_t *txdata,uint8_t nsymb,NR_DL_FRAME_PARMS *frame_parms, uint32_t slot) { - PHY_ofdm_mod(txdataF, // input - txdata, // output - frame_parms->ofdm_symbol_size, - 1, // number of symbols - frame_parms->nb_prefix_samples0, // number of prefix samples - CYCLIC_PREFIX); - PHY_ofdm_mod(txdataF+frame_parms->ofdm_symbol_size, // input - txdata + frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples0, // output - frame_parms->ofdm_symbol_size, - nsymb - 1, - frame_parms->nb_prefix_samples, // number of prefix samples - CYCLIC_PREFIX); + // This function works only slot wise. For more generic symbol generation refer nr_feptx0() + if (frame_parms->numerology_index != 0) { // case where numerology != 0 + if (!(slot%(frame_parms->slots_per_subframe/2))) { + PHY_ofdm_mod(txdataF, + txdata, + frame_parms->ofdm_symbol_size, + 1, + frame_parms->nb_prefix_samples0, + CYCLIC_PREFIX); + PHY_ofdm_mod(txdataF+frame_parms->ofdm_symbol_size, + txdata + frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples0, + frame_parms->ofdm_symbol_size, + nsymb - 1, + frame_parms->nb_prefix_samples, + CYCLIC_PREFIX); + } + else { + PHY_ofdm_mod(txdataF, + txdata, + frame_parms->ofdm_symbol_size, + nsymb, + frame_parms->nb_prefix_samples, + CYCLIC_PREFIX); + } + } + else { // numerology = 0, longer CP for every 7th symbol + PHY_ofdm_mod(txdataF, + txdata, + frame_parms->ofdm_symbol_size, + 1, + frame_parms->nb_prefix_samples0, + CYCLIC_PREFIX); + PHY_ofdm_mod(txdataF+frame_parms->ofdm_symbol_size, + txdata + frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples0, + frame_parms->ofdm_symbol_size, + 6, + frame_parms->nb_prefix_samples, + CYCLIC_PREFIX); + PHY_ofdm_mod(txdataF + 7*frame_parms->ofdm_symbol_size, + txdata + 6*(frame_parms->ofdm_symbol_size+frame_parms->nb_prefix_samples) + + frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples0, + frame_parms->ofdm_symbol_size, + 1, + frame_parms->nb_prefix_samples0, + CYCLIC_PREFIX); + PHY_ofdm_mod(txdataF + 8*frame_parms->ofdm_symbol_size, + txdata + 6*(frame_parms->ofdm_symbol_size+frame_parms->nb_prefix_samples) + + 2*(frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples0), + frame_parms->ofdm_symbol_size, + 6, + frame_parms->nb_prefix_samples, + CYCLIC_PREFIX); + } + } void PHY_ofdm_mod(int *input, /// pointer to complex input diff --git a/openair1/PHY/MODULATION/slot_fep_nr.c b/openair1/PHY/MODULATION/slot_fep_nr.c index 39c730bfac..f3c8c108dc 100644 --- a/openair1/PHY/MODULATION/slot_fep_nr.c +++ b/openair1/PHY/MODULATION/slot_fep_nr.c @@ -107,7 +107,7 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue, rx_offset += frame_parms->ofdm_symbol_size * symbol; // use OFDM symbol from within 1/8th of the CP to avoid ISI - rx_offset -= nb_prefix_samples / 8; + rx_offset -= (nb_prefix_samples / frame_parms->ofdm_offset_divisor); #ifdef DEBUG_FEP // if (ue->frame <100) @@ -157,6 +157,15 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue, (int16_t *)&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol], frame_parms->ofdm_symbol_size, 15); + + int16_t *shift_rot = frame_parms->timeshift_symbol_rotation; + + multadd_cpx_vector((int16_t *)&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol], + shift_rot, + (int16_t *)&common_vars->common_vars_rx_data_per_thread[proc->thread_id].rxdataF[aa][frame_parms->ofdm_symbol_size*symbol], + 1, + frame_parms->ofdm_symbol_size, + 15); } #ifdef DEBUG_FEP @@ -292,14 +301,15 @@ int nr_slot_fep_ul(NR_DL_FRAME_PARMS *frame_parms, // This is for misalignment issues int32_t tmp_dft_in[8192] __attribute__ ((aligned (32))); - unsigned int slot_offset = frame_parms->get_samples_slot_timestamp(Ns,frame_parms,0); - // offset of first OFDM symbol - int32_t rxdata_offset = slot_offset + nb_prefix_samples0; - // offset of n-th OFDM symbol - rxdata_offset += symbol * (frame_parms->ofdm_symbol_size + nb_prefix_samples); + unsigned int rxdata_offset = frame_parms->get_samples_slot_timestamp(Ns,frame_parms,0); + unsigned int abs_symbol = Ns * frame_parms->symbols_per_slot + symbol; + for (int idx_symb = Ns*frame_parms->symbols_per_slot; idx_symb <= abs_symbol; idx_symb++) + rxdata_offset += (idx_symb%(0x7<numerology_index)) ? nb_prefix_samples : nb_prefix_samples0; + rxdata_offset += frame_parms->ofdm_symbol_size * symbol; + // use OFDM symbol from within 1/8th of the CP to avoid ISI - rxdata_offset -= nb_prefix_samples / 8; + rxdata_offset -= (nb_prefix_samples / frame_parms->ofdm_offset_divisor); int16_t *rxdata_ptr; @@ -333,9 +343,6 @@ int nr_slot_fep_ul(NR_DL_FRAME_PARMS *frame_parms, (int16_t *)&rxdataF[symbol * frame_parms->ofdm_symbol_size], 1); - // clear DC carrier from OFDM symbols - rxdataF[symbol * frame_parms->ofdm_symbol_size] = 0; - return 0; } @@ -349,7 +356,7 @@ void apply_nr_rotation_ul(NR_DL_FRAME_PARMS *frame_parms, int symb_offset = (slot%frame_parms->slots_per_subframe)*frame_parms->symbols_per_slot; - for (int symbol=0;symbolsymbol_rotation[1])[symbol + symb_offset]; ((int16_t*)&rot2)[1]=-((int16_t*)&rot2)[1]; @@ -359,5 +366,14 @@ void apply_nr_rotation_ul(NR_DL_FRAME_PARMS *frame_parms, (int16_t *)&rxdataF[frame_parms->ofdm_symbol_size*symbol], length, 15); + + int16_t *shift_rot = frame_parms->timeshift_symbol_rotation; + + multadd_cpx_vector((int16_t *)&rxdataF[frame_parms->ofdm_symbol_size*symbol], + shift_rot, + (int16_t *)&rxdataF[frame_parms->ofdm_symbol_size*symbol], + 1, + length, + 15); } } diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index e942ef2c6b..57470cf988 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -39,23 +39,22 @@ extern openair0_config_t openair0_cfg[MAX_CARDS]; int nr_est_timing_advance_pusch(PHY_VARS_gNB* gNB, int UE_id) { - int i, aa, max_pos = 0, max_val = 0; - + int max_pos = 0, max_val = 0; + NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; NR_gNB_PUSCH *gNB_pusch_vars = gNB->pusch_vars[UE_id]; int32_t **ul_ch_estimates_time = gNB_pusch_vars->ul_ch_estimates_time; - - int sync_pos = frame_parms->nb_prefix_samples / 8; - for (i = 0; i < frame_parms->ofdm_symbol_size; i++) { + const int sync_pos = 0; + + for (int i = 0; i < frame_parms->ofdm_symbol_size; i++) { int temp = 0; - for (aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { - short Re = ((int16_t*)ul_ch_estimates_time[aa])[(i<<1)]; - short Im = ((int16_t*)ul_ch_estimates_time[aa])[1+(i<<1)]; + for (int aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { + int Re = ((int16_t*)ul_ch_estimates_time[aa])[(i<<1)]; + int Im = ((int16_t*)ul_ch_estimates_time[aa])[1+(i<<1)]; temp += (Re*Re/2) + (Im*Im/2); } - if (temp > max_val) { max_pos = i; max_val = temp; @@ -65,7 +64,6 @@ int nr_est_timing_advance_pusch(PHY_VARS_gNB* gNB, int UE_id) if (max_pos > frame_parms->ofdm_symbol_size/2) max_pos = max_pos - frame_parms->ofdm_symbol_size; - return max_pos - sync_pos; } diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 8f9a7a5edf..960ad82696 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -31,11 +31,13 @@ #include "PHY/NR_UE_ESTIMATION/filt16a_32.h" #include "PHY/NR_REFSIG/ul_ref_seq_nr.h" +#include "executables/softmodem-common.h" //#define DEBUG_CH //#define DEBUG_PUSCH +#define NO_INTERP 1 #define dBc(x,y) (dB_fixed(((int32_t)(x))*(x) + ((int32_t)(y))*(y))) int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, @@ -187,8 +189,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, #endif //if ((gNB->frame_parms.N_RB_UL&1)==0) { - if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1){ - + if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1 && gNB->prb_interpolation == 0){ + LOG_D(PHY,"PUSCH estimation DMRS type 1, Freq-domain interpolation"); // Treat first 2 pilots specially (left edge) ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); @@ -212,7 +214,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ch, ul_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; //for (int i= 0; i<8; i++) @@ -243,7 +245,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ch, ul_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; //printf("ul_ch addr %p\n",ul_ch); @@ -277,12 +279,12 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, //for (int i= 0; i<16; i++) //printf("ul_ch addr %p %d\n", ul_ch+i, *(ul_ch+i)); - pil+=2; + pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - ul_ch+=8; + ul_ch += 8; - for (pilot_cnt=3; pilot_cnt<(6*nb_rb_pusch-3); pilot_cnt+=2) { + for (pilot_cnt=3; pilot_cnt<(6*nb_rb_pusch-3); pilot_cnt += 2) { ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); @@ -295,7 +297,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ch, ul_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; //printf("ul_ch addr %p\n",ul_ch); @@ -315,10 +317,10 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, //for (int i= 0; i<16; i++) //printf("ul_ch addr %p %d\n", ul_ch+i, *(ul_ch+i)); - pil+=2; + pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - ul_ch+=8; + ul_ch += 8; } @@ -337,7 +339,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, //for (int i= 0; i<8; i++) //printf("ul_ch addr %p %d\n", ul_ch+i, *(ul_ch+i)); - pil+=2; + pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; @@ -353,10 +355,10 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ul_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - ul_ch+=8; + ul_ch += 8; ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); @@ -425,15 +427,15 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, #ifdef DEBUG_PUSCH ul_ch = (int16_t *)&ul_ch_estimates[p*gNB->frame_parms.nb_antennas_rx+aarx][ch_offset]; for(uint16_t idxP=0; idxPdmrs_config_type == pusch_dmrs_type2 && gNB->prb_interpolation == 0) { //pusch_dmrs_type2 |p_r,p_l,d,d,d,d,p_r,p_l,d,d,d,d| + LOG_D(PHY,"PUSCH estimation DMRS type 2, Freq-domain interpolation"); // Treat first DMRS specially (left edge) rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; @@ -441,12 +443,12 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ul_ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ul_ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - pil+=2; - ul_ch+=2; + pil += 2; + ul_ch += 2; re_offset = (re_offset + 1)%gNB->frame_parms.ofdm_symbol_size; ch_offset++; - for (re_cnt = 1; re_cnt < (nb_rb_pusch*NR_NB_SC_PER_RB) - 5; re_cnt+=6){ + for (re_cnt = 1; re_cnt < (nb_rb_pusch*NR_NB_SC_PER_RB) - 5; re_cnt += 6){ rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; @@ -456,8 +458,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ul_ch[0] = ch_l[0]; ul_ch[1] = ch_l[1]; - pil+=2; - ul_ch+=2; + pil += 2; + ul_ch += 2; ch_offset++; multadd_real_four_symbols_vector_complex_scalar(filt8_ml2, @@ -476,17 +478,17 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ch_r, ul_ch); - //for (int re_idx = 0; re_idx < 8; re_idx+=2) + //for (int re_idx = 0; re_idx < 8; re_idx += 2) //printf("ul_ch = %d + j*%d\n", ul_ch[re_idx], ul_ch[re_idx+1]); - ul_ch+=8; - ch_offset+=4; + ul_ch += 8; + ch_offset += 4; ul_ch[0] = ch_r[0]; ul_ch[1] = ch_r[1]; - pil+=2; - ul_ch+=2; + pil += 2; + ul_ch += 2; ch_offset++; re_offset = (re_offset + 1)%gNB->frame_parms.ofdm_symbol_size; @@ -502,7 +504,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ul_ch[0] = ch_l[0]; ul_ch[1] = ch_l[1]; - ul_ch+=2; + ul_ch += 2; ch_offset++; multadd_real_four_symbols_vector_complex_scalar(filt8_rr1, @@ -518,6 +520,385 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ul_ch_128[0] = _mm_slli_epi16 (ul_ch_128[0], 2); } + else if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1) {// this is case without frequency-domain linear interpolation, just take average of LS channel estimates of 6 DMRS REs and use a common value for the whole PRB + LOG_D(PHY,"PUSCH estimation DMRS type 1, no Freq-domain interpolation"); + int32_t ch_0, ch_1; + // First PRB + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 6; + ch[1] = ch_1 / 6; + + + + +#if NO_INTERP + for (int i=0;i<12;i++) ((int32_t*)ul_ch)[i] = *(int32_t*)ch; + ul_ch+=24; +#else + multadd_real_vector_complex_scalar(filt8_avlip0, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip1, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip2, + ch, + ul_ch, + 8); + ul_ch -= 24; +#endif + + for (pilot_cnt=6; pilot_cnt<6*(nb_rb_pusch-1); pilot_cnt += 6) { + + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 6; + ch[1] = ch_1 / 6; + +#if NO_INTERP + for (int i=0;i<12;i++) ((int32_t*)ul_ch)[i] = *(int32_t*)ch; + ul_ch+=24; +#else + ul_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + ul_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + ul_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip4, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip5, + ch, + ul_ch, + 8); + ul_ch -= 16; +#endif + } + // Last PRB + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 6; + ch[1] = ch_1 / 6; + +#if NO_INTERP + for (int i=0;i<12;i++) ((int32_t*)ul_ch)[i] = *(int32_t*)ch; + ul_ch+=24; +#else + ul_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + ul_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + ul_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip6, + ch, + ul_ch, + 8); +#endif + } + else { // this is case without frequency-domain linear interpolation, just take average of LS channel estimates of 4 DMRS REs and use a common value for the whole PRB + LOG_D(PHY,"PUSCH estimation DMRS type 2, no Freq-domain interpolation"); + int32_t ch_0, ch_1; + //First PRB + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 4; + ch[1] = ch_1 / 4; + + multadd_real_vector_complex_scalar(filt8_avlip0, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip1, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip2, + ch, + ul_ch, + 8); + ul_ch -= 24; + + for (pilot_cnt=4; pilot_cnt<4*(nb_rb_pusch-1); pilot_cnt += 4) { + + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 4; + ch[1] = ch_1 / 4; + + ul_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + ul_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + ul_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip4, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip5, + ch, + ul_ch, + 8); + ul_ch -= 16; + } + // Last PRB + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 4; + ch[1] = ch_1 / 4; + + ul_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + ul_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + ul_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + ul_ch, + 8); + + ul_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip6, + ch, + ul_ch, + 8); + } +#ifdef DEBUG_PUSCH + ul_ch = (int16_t *)&ul_ch_estimates[p*gNB->frame_parms.nb_antennas_rx+aarx][ch_offset]; + for(uint16_t idxP=0; idxPconfig.prach_config.num_prach_fd_occasions_list[numRA].k1.value; - int sample_offset_slot = (prachStartSymbol==0?0:fp->ofdm_symbol_size*prachStartSymbol+fp->nb_prefix_samples0+fp->nb_prefix_samples*(prachStartSymbol-1)); + int sample_offset_slot; + if (!(slot%(fp->slots_per_subframe/2))) + sample_offset_slot = (prachStartSymbol==0?0:fp->ofdm_symbol_size*prachStartSymbol+fp->nb_prefix_samples0+fp->nb_prefix_samples*(prachStartSymbol-1)); + else + sample_offset_slot = (prachStartSymbol==0?0:prachStartSymbol*(fp->ofdm_symbol_size+fp->nb_prefix_samples)); //to be checked for mu=0; LOG_D(PHY,"frame %d, slot %d: doing rx_nr_prach_ru for format %d, numRA %d, prachStartSymbol %d, prachOccasion %d\n",frame,slot,prachFormat,numRA,prachStartSymbol,prachOccasion); diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 1524c840c3..e3b87ff233 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -48,6 +48,8 @@ #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" +#include "nfapi/oai_integration/vendor_ext.h" + #include "T.h" //#define DEBUG_NR_PUCCH_RX 1 @@ -1437,7 +1439,7 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, corr_tmp += corr_re*corr_re + corr_im*corr_im; } // aa loop }// group loop - + LOG_D(PHY,"cw %d, metric %f dB\n",cw,10*log10(corr_tmp)); if (corr_tmp > corr) { corr = corr_tmp; cw_ML=cw; diff --git a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c index 990f3101fd..65ae288252 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c +++ b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c @@ -239,3 +239,24 @@ short filt8_dcll2[8] = { short filt8_dclh2[8] = { 0,0,0,0,1489,2979,4468,5958}; + +short filt8_avlip0[8] = { +16384,16384,16384,16384,16384,16384,16384,15019}; + +short filt8_avlip1[8] = { +13653,12288,10923,9557,8192,6827,5461,4096}; + +short filt8_avlip2[8] = { +2731,1365,0,0,0,0,0,0}; + +short filt8_avlip3[8] = { +2731,4096,5461,6827,8192,9557,10923,12288}; + +short filt8_avlip4[8] = { +13653,15019,16384,15019,13653,12288,10923,9557}; + +short filt8_avlip5[8] = { +8192,6827,5461,4096,2731,1365,0,0}; + +short filt8_avlip6[8] = { +13653,15019,16384,16384,16384,16384,16384,16384}; diff --git a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.h b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.h index ef6137a07f..e28736b51d 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.h +++ b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.h @@ -169,4 +169,17 @@ extern short filt8_dcll2[8]; extern short filt8_dclh2[8]; -#endif \ No newline at end of file +extern short filt8_avlip0[8]; + +extern short filt8_avlip1[8]; + +extern short filt8_avlip2[8]; + +extern short filt8_avlip3[8]; + +extern short filt8_avlip4[8]; + +extern short filt8_avlip5[8]; + +extern short filt8_avlip6[8]; +#endif diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c index a2f824e468..3908febae9 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c @@ -45,25 +45,23 @@ void nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, static int max_pos_fil = 0; static int count_max_pos_ok = 0; static int first_time = 1; - int temp = 0, i, aa, max_val = 0, max_pos = 0; - int diff; - short Re,Im,ncoef; + int max_val = 0, max_pos = 0; + const int sync_pos = 0; uint8_t sync_offset = 0; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_IN); - ncoef = 32767 - coef; + short ncoef = 32767 - coef; LOG_D(PHY,"AbsSubframe %d: rx_offset (before) = %d\n",subframe,ue->rx_offset); - // we only use channel estimates from tx antenna 0 here - for (i = 0; i < frame_parms->nb_prefix_samples; i++) { - temp = 0; + for (int i = 0; i < frame_parms->nb_prefix_samples; i++) { + int temp = 0; - for (aa=0; aanb_antennas_rx; aa++) { - Re = ((int16_t*)ue->pbch_vars[gNB_id]->dl_ch_estimates_time[aa])[(i<<1)]; - Im = ((int16_t*)ue->pbch_vars[gNB_id]->dl_ch_estimates_time[aa])[1+(i<<1)]; + for (int aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { + int Re = ((int16_t*)ue->pbch_vars[gNB_id]->dl_ch_estimates_time[aa])[(i<<1)]; + int Im = ((int16_t*)ue->pbch_vars[gNB_id]->dl_ch_estimates_time[aa])[1+(i<<1)]; temp += (Re*Re/2) + (Im*Im/2); } @@ -73,6 +71,9 @@ void nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, } } + if (max_pos > frame_parms->ofdm_symbol_size/2) + max_pos = max_pos - frame_parms->ofdm_symbol_size; + // filter position to reduce jitter if (clear == 1) max_pos_fil = max_pos; @@ -82,61 +83,58 @@ void nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, // do not filter to have proactive timing adjustment //max_pos_fil = max_pos; - diff = max_pos_fil - (frame_parms->nb_prefix_samples>>3); + int diff = max_pos_fil - sync_pos; - if (frame_parms->freq_range==nr_FR2) - sync_offset = 2; - else - sync_offset = 0; - - if ( abs(diff) < (SYNCH_HYST+sync_offset) ) - ue->rx_offset = 0; - else - ue->rx_offset = diff; + if (frame_parms->freq_range==nr_FR2) + sync_offset = 2; + else + sync_offset = 0; - if(abs(diff)<5) - count_max_pos_ok ++; - else - count_max_pos_ok = 0; - - //printf("adjust sync count_max_pos_ok = %d\n",count_max_pos_ok); + if ( abs(diff) < (SYNCH_HYST+sync_offset) ) + ue->rx_offset = 0; + else + ue->rx_offset = diff; - if(count_max_pos_ok > 10 && first_time == 1) - { - first_time = 0; - ue->time_sync_cell = 1; - if (get_softmodem_params()->do_ra || get_softmodem_params()->sa) { - LOG_I(PHY,"[UE%d] Sending synch status to higher layers\n",ue->Mod_id); - //mac_resynch(); - //dl_phy_sync_success(ue->Mod_id,frame,0,1);//ue->common_vars.eNb_id); - ue->UE_mode[0] = PRACH; - ue->prach_resources[gNB_id]->sync_frame = frame; - ue->prach_resources[gNB_id]->init_msg1 = 0; - } else { - ue->UE_mode[0] = PUSCH; - } - } + if(abs(diff)<5) + count_max_pos_ok ++; + else + count_max_pos_ok = 0; + + //printf("adjust sync count_max_pos_ok = %d\n",count_max_pos_ok); - if (ue->rx_offset < 0) - ue->rx_offset += frame_parms->samples_per_frame; + if(count_max_pos_ok > 10 && first_time == 1) + { + first_time = 0; + ue->time_sync_cell = 1; + if (get_softmodem_params()->do_ra || get_softmodem_params()->sa) { + LOG_I(PHY,"[UE%d] Sending synch status to higher layers\n",ue->Mod_id); + //mac_resynch(); + //dl_phy_sync_success(ue->Mod_id,frame,0,1);//ue->common_vars.eNb_id); + ue->UE_mode[0] = PRACH; + ue->prach_resources[gNB_id]->sync_frame = frame; + ue->prach_resources[gNB_id]->init_msg1 = 0; + } else { + ue->UE_mode[0] = PUSCH; + } + } - if (ue->rx_offset >= frame_parms->samples_per_frame) - ue->rx_offset -= frame_parms->samples_per_frame; + if (ue->rx_offset < 0) + ue->rx_offset += frame_parms->samples_per_frame; + if (ue->rx_offset >= frame_parms->samples_per_frame) + ue->rx_offset -= frame_parms->samples_per_frame; +#ifdef DEBUG_PHY + LOG_D(PHY,"AbsSubframe %d: diff = %i, rx_offset (final) = %i : clear = %d, max_pos = %d, max_pos_fil = %d, max_val = %d, sync_pos %d\n", + subframe, + diff, + ue->rx_offset, + clear, + max_pos, + max_pos_fil, + max_val, + sync_pos); +#endif //DEBUG_PHY - #ifdef DEBUG_PHY - LOG_D(PHY,"AbsSubframe %d: diff =%i rx_offset (final) = %i : clear %d,max_pos = %d,max_pos_fil = %d (peak %d) max_val %d target_pos %d \n", - subframe, - diff, - ue->rx_offset, - clear, - max_pos, - max_pos_fil, - temp,max_val, - (frame_parms->nb_prefix_samples>>3)); - #endif //DEBUG_PHY - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_OUT); - + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_OUT); } diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index e4a82e7021..8097b9b3ea 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -94,15 +94,15 @@ int nr_pbch_dmrs_correlation(PHY_VARS_NR_UE *ue, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - current_ssb->c_re +=ch[0]; - current_ssb->c_im +=ch[1]; + current_ssb->c_re += ch[0]; + current_ssb->c_im += ch[1]; #ifdef DEBUG_CH printf("ch 0 %d\n",((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])); printf("pilot 0 : rxF - > (%d,%d) addr %p ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],&rxF[0],ch[0],ch[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -110,29 +110,29 @@ int nr_pbch_dmrs_correlation(PHY_VARS_NR_UE *ue, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - current_ssb->c_re +=ch[0]; - current_ssb->c_im +=ch[1]; + current_ssb->c_re += ch[0]; + current_ssb->c_im += ch[1]; #ifdef DEBUG_CH printf("pilot 1 : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; - current_ssb->c_re +=ch[0]; - current_ssb->c_im +=ch[1]; + current_ssb->c_re += ch[0]; + current_ssb->c_im += ch[1]; #ifdef DEBUG_CH printf("pilot 2 : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; - for (pilot_cnt=3; pilot_cnt<(3*20); pilot_cnt+=3) { + for (pilot_cnt=3; pilot_cnt<(3*20); pilot_cnt += 3) { // if (pilot_cnt == 30) // rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k)]; @@ -146,14 +146,14 @@ int nr_pbch_dmrs_correlation(PHY_VARS_NR_UE *ue, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - current_ssb->c_re +=ch[0]; - current_ssb->c_im +=ch[1]; + current_ssb->c_re += ch[0]; + current_ssb->c_im += ch[1]; #ifdef DEBUG_CH printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -161,13 +161,13 @@ int nr_pbch_dmrs_correlation(PHY_VARS_NR_UE *ue, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - current_ssb->c_re +=ch[0]; - current_ssb->c_im +=ch[1]; + current_ssb->c_re += ch[0]; + current_ssb->c_im += ch[1]; #ifdef DEBUG_CH printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt+1,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -175,14 +175,14 @@ int nr_pbch_dmrs_correlation(PHY_VARS_NR_UE *ue, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - current_ssb->c_re +=ch[0]; - current_ssb->c_im +=ch[1]; + current_ssb->c_re += ch[0]; + current_ssb->c_im += ch[1]; #ifdef DEBUG_CH printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt+2,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -287,7 +287,6 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, printf("rxF addr %p\n", rxF); printf("dl_ch addr %p\n",dl_ch); #endif - //if ((ue->frame_parms.N_RB_DL&1)==0) { // Treat first 2 pilots specially (left edge) ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -301,7 +300,7 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 16); - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -319,7 +318,7 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 16); - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -334,12 +333,12 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 16); - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; - dl_ch+=24; + dl_ch += 24; - for (pilot_cnt=3; pilot_cnt<(3*20); pilot_cnt+=3) { + for (pilot_cnt=3; pilot_cnt<(3*20); pilot_cnt += 3) { // if (pilot_cnt == 30) // rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k)]; @@ -365,7 +364,7 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, //for (int i= 0; i<8; i++) // printf("pilot_cnt %d dl_ch %d %d\n", pilot_cnt, dl_ch+i, *(dl_ch+i)); - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -380,7 +379,7 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 16); - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; @@ -396,10 +395,10 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 16); - pil+=2; + pil += 2; re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+re_offset)]; - dl_ch+=24; + dl_ch += 24; } @@ -458,8 +457,6 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue, } } - //} - } return(0); } @@ -526,111 +523,111 @@ int nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, printf("dl_ch addr %p\n",dl_ch); #endif // if ((ue->frame_parms.N_RB_DL&1)==0) { - // Treat first 2 pilots specially (left edge) + // Treat first 2 pilots specially (left edge) + ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); + ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); +#ifdef DEBUG_PDCCH + printf("ch 0 %d\n",((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])); + printf("pilot 0 : rxF - > (%d,%d) addr %p ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],&rxF[0],ch[0],ch[1],pil[0],pil[1]); +#endif + multadd_real_vector_complex_scalar(fl, + ch, + dl_ch, + 16); + pil += 2; + rxF += 8; + //for (int i= 0; i<8; i++) + //printf("dl_ch addr %p %d\n", dl_ch+i, *(dl_ch+i)); + + ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); + ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); +#ifdef DEBUG_PDCCH + printf("pilot 1 : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); +#endif + multadd_real_vector_complex_scalar(fm, + ch, + dl_ch, + 16); + pil += 2; + rxF += 8; + + ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); + ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); + +#ifdef DEBUG_PDCCH + printf("pilot 2 : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); +#endif + + multadd_real_vector_complex_scalar(fr, + ch, + dl_ch, + 16); + +#ifdef DEBUG_PDCCH + for (int m =0; m<12; m++) + printf("data : dl_ch -> (%d,%d)\n",dl_ch[0+2*m],dl_ch[1+2*m]); +#endif + pil += 2; + rxF += 8; + dl_ch += 24; + k += 12; + + + + for (pilot_cnt=3; pilot_cnt<(3*nb_rb_coreset); pilot_cnt += 3) { + + if (k >= ue->frame_parms.ofdm_symbol_size){ + k-=ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+1)];} + ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); #ifdef DEBUG_PDCCH - printf("ch 0 %d\n",((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])); - printf("pilot 0 : rxF - > (%d,%d) addr %p ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],&rxF[0],ch[0],ch[1],pil[0],pil[1]); + printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif multadd_real_vector_complex_scalar(fl, - ch, - dl_ch, - 16); - pil+=2; - rxF+=8; + ch, + dl_ch, + 16); + //for (int i= 0; i<8; i++) - //printf("dl_ch addr %p %d\n", dl_ch+i, *(dl_ch+i)); + // printf("pilot_cnt %d dl_ch %d %d\n", pilot_cnt, dl_ch+i, *(dl_ch+i)); + + pil += 2; + rxF += 8; ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); #ifdef DEBUG_PDCCH - printf("pilot 1 : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); + printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt+1,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif multadd_real_vector_complex_scalar(fm, - ch, - dl_ch, - 16); - pil+=2; - rxF+=8; + ch, + dl_ch, + 16); + pil += 2; + rxF += 8; ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); #ifdef DEBUG_PDCCH - printf("pilot 2 : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); + printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt+2,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif multadd_real_vector_complex_scalar(fr, - ch, - dl_ch, - 16); - -#ifdef DEBUG_PDCCH - for (int m =0; m<12; m++) - printf("data : dl_ch -> (%d,%d)\n",dl_ch[0+2*m],dl_ch[1+2*m]); -#endif - pil+=2; - rxF+=8; - dl_ch+=24; - k+=12; - - + ch, + dl_ch, + 16); + pil += 2; + rxF += 8; + dl_ch += 24; + k += 12; - for (pilot_cnt=3; pilot_cnt<(3*nb_rb_coreset); pilot_cnt+=3) { - - if (k >= ue->frame_parms.ofdm_symbol_size){ - k-=ue->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+k+1)];} - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); -#ifdef DEBUG_PDCCH - printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); -#endif - multadd_real_vector_complex_scalar(fl, - ch, - dl_ch, - 16); - - //for (int i= 0; i<8; i++) - // printf("pilot_cnt %d dl_ch %d %d\n", pilot_cnt, dl_ch+i, *(dl_ch+i)); - - pil+=2; - rxF+=8; - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); -#ifdef DEBUG_PDCCH - printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt+1,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); -#endif - multadd_real_vector_complex_scalar(fm, - ch, - dl_ch, - 16); - pil+=2; - rxF+=8; - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - -#ifdef DEBUG_PDCCH - printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt+2,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); -#endif - - multadd_real_vector_complex_scalar(fr, - ch, - dl_ch, - 16); - pil+=2; - rxF+=8; - dl_ch+=24; - k+=12; - - } + } - //} + //} } @@ -653,7 +650,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, unsigned short k; unsigned int pilot_cnt; int16_t ch_l[2],ch_r[2],ch[2],*pil,*rxF,*dl_ch; - int16_t *fl,*fm,*fr,*fml,*fmr,*fmm,*fdcl,*fdcr,*fdclh,*fdcrh, *frl, *frr; + int16_t *fl=NULL,*fm=NULL,*fr=NULL,*fml=NULL,*fmr=NULL,*fmm=NULL,*fdcl=NULL,*fdcr=NULL,*fdclh=NULL,*fdcrh=NULL, *frl=NULL, *frr=NULL; int ch_offset,symbol_offset; NR_UE_DLSCH_t **dlsch = ue->dlsch[proc->thread_id][gNB_id]; @@ -698,99 +695,97 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, if (p<4) ue->frame_parms.nushift = nushift; switch (delta) { - case 0://port 0,1 - fl = filt8_l0;//left interpolation Filter for DMRS config. 1 - fm = filt8_m0;//left middle interpolation Filter - fr = filt8_r0;//right interpolation Filter - fmm = filt8_mm0;;//middle middle interpolation Filter - fml = filt8_m0;//left middle interpolation Filter - fmr = filt8_mr0;//middle right interpolation Filter - fdcl = filt8_dcl0;//left DC interpolation Filter (even RB) - fdcr = filt8_dcr0;//right DC interpolation Filter (even RB) - fdclh = filt8_dcl0_h;//left DC interpolation Filter (odd RB) - fdcrh = filt8_dcr0_h;//right DC interpolation Filter (odd RB) - frl = NULL; - frr = NULL; - break; + case 0://port 0,1 + fl = filt8_l0;//left interpolation Filter for DMRS config. 1 + fm = filt8_m0;//left middle interpolation Filter + fr = filt8_r0;//right interpolation Filter + fmm = filt8_mm0;;//middle middle interpolation Filter + fml = filt8_m0;//left middle interpolation Filter + fmr = filt8_mr0;//middle right interpolation Filter + fdcl = filt8_dcl0;//left DC interpolation Filter (even RB) + fdcr = filt8_dcr0;//right DC interpolation Filter (even RB) + fdclh = filt8_dcl0_h;//left DC interpolation Filter (odd RB) + fdcrh = filt8_dcr0_h;//right DC interpolation Filter (odd RB) + frl = NULL; + frr = NULL; + break; - case 1://port2,3 - fl = filt8_l1; - fm = filt8_m1; - fr = filt8_r1; - fmm = filt8_mm1; - fml = filt8_ml1; - fmr = filt8_m1; - fdcl = filt8_dcl1; - fdcr = filt8_dcr1; - fdclh = filt8_dcl1_h; - fdcrh = filt8_dcr1_h; - frl = NULL; - frr = NULL; - break; + case 1://port2,3 + fl = filt8_l1; + fm = filt8_m1; + fr = filt8_r1; + fmm = filt8_mm1; + fml = filt8_ml1; + fmr = filt8_m1; + fdcl = filt8_dcl1; + fdcr = filt8_dcr1; + fdclh = filt8_dcl1_h; + fdcrh = filt8_dcr1_h; + frl = NULL; + frr = NULL; + break; - default: - msg("pdsch_channel_estimation: nushift=%d -> ERROR\n",nushift); - return -1; - break; + default: + LOG_E(PHY,"pdsch_channel_estimation: nushift=%d -> ERROR\n",nushift); + return -1; + break; } } else {//NFAPI_NR_DMRS_TYPE2 nushift = delta; if (p<6) ue->frame_parms.nushift = nushift; switch (delta) { - case 0://port 0,1 - fl = filt8_l2;//left interpolation Filter should be fml - fr = filt8_r2;//right interpolation Filter should be fmr - fm = filt8_l2; - fmm = filt8_r2; - fml = filt8_ml2; - fmr = filt8_mr2; - frl = filt8_rl2; - frr = filt8_rm2; - fdcl = filt8_dcl1; - fdcr = filt8_dcr1; - fdclh = filt8_dcl1_h; - fdcrh = filt8_dcr1_h; - break; + case 0://port 0,1 + fl = filt8_l2;//left interpolation Filter should be fml + fr = filt8_r2;//right interpolation Filter should be fmr + fm = filt8_l2; + fmm = filt8_r2; + fml = filt8_ml2; + fmr = filt8_mr2; + frl = filt8_rl2; + frr = filt8_rm2; + fdcl = filt8_dcl1; + fdcr = filt8_dcr1; + fdclh = filt8_dcl1_h; + fdcrh = filt8_dcr1_h; + break; - case 2://port2,3 - fl = filt8_l3; - fm = filt8_m2; - fr = filt8_r3; - fmm = filt8_mm2; - fml = filt8_l2; - fmr = filt8_r2; - frl = filt8_rl3; - frr = filt8_rr3; - fdcl = NULL; - fdcr = NULL; - fdclh = NULL; - fdcrh = NULL; - break; + case 2://port2,3 + fl = filt8_l3; + fm = filt8_m2; + fr = filt8_r3; + fmm = filt8_mm2; + fml = filt8_l2; + fmr = filt8_r2; + frl = filt8_rl3; + frr = filt8_rr3; + fdcl = NULL; + fdcr = NULL; + fdclh = NULL; + fdcrh = NULL; + break; - default: - msg("pdsch_channel_estimation: nushift=%d -> ERROR\n",nushift); - return -1; - break; + default: + LOG_E(PHY,"pdsch_channel_estimation: nushift=%d -> ERROR\n",nushift); + return -1; + break; } } for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { - pil = (int16_t *)&pilot[rb_offset*((config_type==NFAPI_NR_DMRS_TYPE1) ? 6:4)]; + pil = (int16_t *)&pilot[rb_offset*((config_type == NFAPI_NR_DMRS_TYPE1) ? 6:4)]; k = k % ue->frame_parms.ofdm_symbol_size; re_offset = k; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+re_offset+nushift)]; dl_ch = (int16_t *)&dl_ch_estimates[p*ue->frame_parms.nb_antennas_rx+aarx][ch_offset]; memset(dl_ch,0,4*(ue->frame_parms.ofdm_symbol_size)); - #ifdef DEBUG_PDSCH printf("ch est pilot addr %p RB_DL %d\n",&pilot[0], ue->frame_parms.N_RB_DL); printf("k %d, first_carrier %d\n",k,ue->frame_parms.first_carrier_offset); printf("rxF addr %p p %d\n", rxF,p); printf("dl_ch addr %p nushift %d\n",dl_ch,nushift); #endif - - if (config_type == NFAPI_NR_DMRS_TYPE1) { + if (config_type == NFAPI_NR_DMRS_TYPE1 && ue->prb_interpolation == 0) { // Treat first 2 pilots specially (left edge) ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -804,7 +799,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; //for (int i= 0; i<8; i++) @@ -819,7 +814,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; //printf("dl_ch addr %p\n",dl_ch); @@ -834,18 +829,12 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - //for (int i= 0; i<16; i++) - //printf("dl_ch addr %p %d\n", dl_ch+i, *(dl_ch+i)); - - pil+=2; + pil += 2; re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - dl_ch+=8; + dl_ch += 8; - for (pilot_cnt=3; pilot_cnt<(6*nb_rb_pdsch-3); pilot_cnt+=2) { - //if ((pilot_cnt%6)==0) - //dl_ch+=4; - //printf("re_offset %d\n",re_offset); + for (pilot_cnt=3; pilot_cnt<(6*nb_rb_pdsch-3); pilot_cnt += 2) { ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); @@ -857,7 +846,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; @@ -870,10 +859,10 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, ch, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - dl_ch+=8; + dl_ch += 8; } @@ -881,7 +870,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); #ifdef DEBUG_PDSCH - printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); + printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],ch[0],ch[1],pil[0],pil[1]); #endif multadd_real_vector_complex_scalar(fm, ch, @@ -891,7 +880,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, //for (int i= 0; i<8; i++) //printf("dl_ch addr %p %d\n", dl_ch+i, *(dl_ch+i)); - pil+=2; + pil += 2; re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; @@ -906,10 +895,10 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - dl_ch+=8; + dl_ch += 8; ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); @@ -921,59 +910,59 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - // check if PRB crosses DC and improve estimates around DC - if ((bwp_start_subcarrier < ue->frame_parms.ofdm_symbol_size) && (bwp_start_subcarrier+nb_rb_pdsch*12 >= ue->frame_parms.ofdm_symbol_size)) { - dl_ch = (int16_t *)&dl_ch_estimates[p*ue->frame_parms.nb_antennas_rx+aarx][ch_offset]; - uint16_t idxDC = 2*(ue->frame_parms.ofdm_symbol_size - bwp_start_subcarrier); - uint16_t idxPil = idxDC/2; - re_offset = k; - pil = (int16_t *)&pilot[rb_offset*((config_type==NFAPI_NR_DMRS_TYPE1) ? 6:4)]; - pil += (idxPil-2); - dl_ch += (idxDC-4); - dl_ch = memset(dl_ch, 0, sizeof(int16_t)*10); - re_offset = (re_offset+idxDC/2-2) % ue->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - - // for proper allignment of SIMD vectors - if((ue->frame_parms.N_RB_DL&1)==0) { - - multadd_real_vector_complex_scalar(fdcl, - ch, - dl_ch-4, - 8); - - pil += 4; - re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; + // check if PRB crosses DC and improve estimates around DC + if ((bwp_start_subcarrier < ue->frame_parms.ofdm_symbol_size) && (bwp_start_subcarrier+nb_rb_pdsch*12 >= ue->frame_parms.ofdm_symbol_size)) { + dl_ch = (int16_t *)&dl_ch_estimates[aarx][ch_offset]; + uint16_t idxDC = 2*(ue->frame_parms.ofdm_symbol_size - bwp_start_subcarrier); + uint16_t idxPil = idxDC/2; + re_offset = k; + pil = (int16_t *)&pilot[rb_offset*((config_type == NFAPI_NR_DMRS_TYPE1) ? 6:4)]; + pil += (idxPil-2); + dl_ch += (idxDC-4); + dl_ch = memset(dl_ch, 0, sizeof(int16_t)*10); + re_offset = (re_offset+idxDC/2-2) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - - multadd_real_vector_complex_scalar(fdcr, - ch, - dl_ch-4, - 8); - } else { + + // for proper allignment of SIMD vectors + if((ue->frame_parms.N_RB_DL&1) == 0) { + + multadd_real_vector_complex_scalar(fdcl, + ch, + dl_ch-4, + 8); + + pil += 4; + re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); + ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); + + multadd_real_vector_complex_scalar(fdcr, + ch, + dl_ch-4, + 8); + } else { - multadd_real_vector_complex_scalar(fdclh, - ch, - dl_ch, - 8); - - pil += 4; - re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - - multadd_real_vector_complex_scalar(fdcrh, - ch, - dl_ch, - 8); + multadd_real_vector_complex_scalar(fdclh, + ch, + dl_ch, + 8); + + pil += 4; + re_offset = (re_offset+4) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); + ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); + + multadd_real_vector_complex_scalar(fdcrh, + ch, + dl_ch, + 8); + } } - } - } else { //NFAPI_NR_DMRS_TYPE2 |dmrs_r,dmrs_l,0,0,0,0,dmrs_r,dmrs_l,0,0,0,0| + } else if (config_type == NFAPI_NR_DMRS_TYPE2 && ue->prb_interpolation == 0){ //pdsch_dmrs_type2 |dmrs_r,dmrs_l,0,0,0,0,dmrs_r,dmrs_l,0,0,0,0| // Treat first 4 pilots specially (left edge) ch_l[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -984,7 +973,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, printf("pilot 0 : rxF - > (%d,%d) addr %p ch -> (%d,%d), pil -> (%d,%d) \n",rxF[0],rxF[1],&rxF[0],ch_l[0],ch_l[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch_r[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -1003,13 +992,13 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch_l[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch_l[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - pil+=2; + pil += 2; re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch_r[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -1023,20 +1012,20 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - dl_ch+=12; + dl_ch += 12; dl_ch[0+2*nushift] = ch[0]; dl_ch[1+2*nushift] = ch[1]; dl_ch[2+2*nushift] = ch[0]; dl_ch[3+2*nushift] = ch[1]; - dl_ch+=4; + dl_ch += 4; - for (pilot_cnt=4; pilot_cnt<4*nb_rb_pdsch; pilot_cnt+=4) { + for (pilot_cnt=4; pilot_cnt<4*nb_rb_pdsch; pilot_cnt += 4) { multadd_real_vector_complex_scalar(fml, ch, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch_l[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -1046,7 +1035,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, printf("pilot %u : rxF - > (%d,%d) ch -> (%d,%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],ch_l[0],ch_l[1],pil[0],pil[1]); #endif - pil+=2; + pil += 2; re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch_r[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -1064,7 +1053,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - dl_ch+=8; + dl_ch += 8; dl_ch[0+2*nushift] = ch[0]; dl_ch[1+2*nushift] = ch[1]; dl_ch[2+2*nushift] = ch[0]; @@ -1075,13 +1064,13 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - pil+=2; + pil += 2; re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch_l[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch_l[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - pil+=2; + pil += 2; re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; ch_r[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); @@ -1099,12 +1088,12 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, dl_ch, 8); - dl_ch+=12; + dl_ch += 12; dl_ch[0+2*nushift] = ch[0]; dl_ch[1+2*nushift] = ch[1]; dl_ch[2+2*nushift] = ch[0]; dl_ch[3+2*nushift] = ch[1]; - dl_ch+=4; + dl_ch += 4; } // Treat last 2 pilots specially (right edge) @@ -1136,7 +1125,7 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, ch_l[1]= dl_ch[1] ; // for proper allignment of SIMD vectors - if((ue->frame_parms.N_RB_DL&1)==0) { + if((ue->frame_parms.N_RB_DL&1) == 0) { dl_ch -= 20; //Interpolate fdcrl1 with ch_r multadd_real_vector_complex_scalar(filt8_dcrl1, @@ -1185,11 +1174,359 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, } } } + else if (config_type == NFAPI_NR_DMRS_TYPE1) { // this is case without frequency-domain linear interpolation, just take average of LS channel estimates of 6 DMRS REs and use a common value for the whole PRB + int32_t ch_0, ch_1; + + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 6; + ch[1] = ch_1 / 6; + + multadd_real_vector_complex_scalar(filt8_avlip0, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip1, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip2, + ch, + dl_ch, + 8); + dl_ch -= 24; + + for (pilot_cnt=6; pilot_cnt<6*(nb_rb_pdsch-1); pilot_cnt += 6) { + + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 6; + ch[1] = ch_1 / 6; + dl_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + dl_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + dl_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip4, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip5, + ch, + dl_ch, + 8); + dl_ch -= 16; + } + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+2) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 6; + ch[1] = ch_1 / 6; + + dl_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + dl_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + dl_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip6, + ch, + dl_ch, + 8); + } + else { // this is case without frequency-domain linear interpolation, just take average of LS channel estimates of 4 DMRS REs and use a common value for the whole PRB + int32_t ch_0, ch_1; + + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 4; + ch[1] = ch_1 / 4; + + multadd_real_vector_complex_scalar(filt8_avlip0, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip1, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip2, + ch, + dl_ch, + 8); + dl_ch -= 24; + + for (pilot_cnt=4; pilot_cnt<4*(nb_rb_pdsch-1); pilot_cnt += 4) { + int32_t ch_0, ch_1; + + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 4; + ch[1] = ch_1 / 4; + + dl_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + dl_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + dl_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip4, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip5, + ch, + dl_ch, + 8); + dl_ch -= 16; + } + + ch_0 = ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 = ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+1) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch_0 += ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15; + ch_1 += ((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15; + + pil += 2; + re_offset = (re_offset+5) % ue->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(symbol_offset+nushift+re_offset)]; + + ch[0] = ch_0 / 4; + ch[1] = ch_1 / 4; + + dl_ch[6] += (ch[0] * 1365)>>15; // 1/12*16384 + dl_ch[7] += (ch[1] * 1365)>>15; // 1/12*16384 + + dl_ch += 8; + multadd_real_vector_complex_scalar(filt8_avlip3, + ch, + dl_ch, + 8); + + dl_ch += 16; + multadd_real_vector_complex_scalar(filt8_avlip6, + ch, + dl_ch, + 8); + } #ifdef DEBUG_PDSCH dl_ch = (int16_t *)&dl_ch_estimates[p*ue->frame_parms.nb_antennas_rx+aarx][ch_offset]; for(uint16_t idxP=0; idxPnb_search_space = 0; return(dci_ind->number_of_dcis); } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index 34eb29d4f1..eae4a2eaa7 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -445,8 +445,6 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, //LOG_I(PHY, "avgs Power per SC is %d\n", avgs); median[(aatx*frame_parms->nb_antennas_rx)+aarx] = avg[(aatx*frame_parms->nb_antennas_rx)+aarx]; } - pdsch_vars[gNB_id]->log2_maxh = (log2_approx(avgs)/2) + 1; - //LOG_I(PHY, "avgs Power per SC is %d lg2_maxh %d\n", avgs, pdsch_vars[gNB_id]->log2_maxh); if (dlsch0_harq->mimo_mode == NR_DUALSTREAM) { nr_dlsch_channel_level_median(pdsch_vars[gNB_id]->dl_ch_estimates_ext, @@ -461,8 +459,10 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, avgs = cmax(avgs, median[aatx*n_rx + aarx]); } } - pdsch_vars[gNB_id]->log2_maxh = (log2_approx(avgs)/2) + 1; } + + pdsch_vars[gNB_id]->log2_maxh = (log2_approx(avgs)/2) + 1; + //LOG_I(PHY, "avgs Power per SC is %d lg2_maxh %d\n", avgs, pdsch_vars[gNB_id]->log2_maxh); } LOG_D(PHY,"[DLSCH] AbsSubframe %d.%d log2_maxh = %d [log2_maxh0 %d log2_maxh1 %d] (%d,%d)\n", frame%1024, diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c index e7cf3c15a9..24299fd788 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c @@ -102,7 +102,11 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, uint8_t slot){ nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].prach_root_sequence_index, ue->X_u); - sample_offset_slot = (prachStartSymbol==0?0:fp->ofdm_symbol_size*prachStartSymbol+fp->nb_prefix_samples0+fp->nb_prefix_samples*(prachStartSymbol-1)); + if (slot % (fp->slots_per_subframe / 2) == 0) + sample_offset_slot = (prachStartSymbol==0?0:fp->ofdm_symbol_size*prachStartSymbol+fp->nb_prefix_samples0+fp->nb_prefix_samples*(prachStartSymbol-1)); + else + sample_offset_slot = (fp->ofdm_symbol_size + fp->nb_prefix_samples) * prachStartSymbol; + prach_start = fp->get_samples_slot_timestamp(slot, fp, 0) + sample_offset_slot; //printf("prachstartsymbold %d, sample_offset_slot %d, prach_start %d\n",prachStartSymbol, sample_offset_slot, prach_start); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index bb61e1a173..8d13d70ac1 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -1044,7 +1044,8 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, uint8_t is_crnti, uint8_t llr8_flag); -int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, +int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, + NR_UE_ULSCH_t *ulsch, NR_DL_FRAME_PARMS* frame_parms, uint8_t harq_pid, unsigned int G); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index 5733ef6eb3..ba5c65ceb7 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -215,11 +215,13 @@ NR_UE_ULSCH_t *new_nr_ue_ulsch(uint16_t N_RB_UL, } -int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, +int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, + NR_UE_ULSCH_t *ulsch, NR_DL_FRAME_PARMS* frame_parms, uint8_t harq_pid, unsigned int G) { + start_meas(&ue->ulsch_encoding_stats); /////////////////////////parameters and variables declaration///////////////////////// /////////// @@ -332,6 +334,7 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_IN); + start_meas(&ue->ulsch_segmentation_stats); Kb=nr_segmentation(harq_process->b, harq_process->c, harq_process->B, @@ -340,6 +343,7 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, pz, &harq_process->F, harq_process->BG); + stop_meas(&ue->ulsch_segmentation_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_OUT); F = harq_process->F; @@ -374,14 +378,6 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, //for (int i=0;i<68*384;i++) // printf("channel_input[%d]=%d\n",i,channel_input[i]); - int temp_opp = 0; - - if (opp_enabled) { - opp_enabled = 0; - temp_opp = 1; - } - - /*printf("output %d %d %d %d %d \n", harq_process->d[0][0], harq_process->d[0][1], harq_process->d[r][2],harq_process->d[0][3], harq_process->d[0][4]); for (int cnt =0 ; cnt < 66*(*pz); cnt ++){ printf("%d \n", harq_process->d[0][cnt]); @@ -397,11 +393,13 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_LDPC_ENCODER_OPTIM, VCD_FUNCTION_IN); + start_meas(&ue->ulsch_ldpc_encoding_stats); for(int j = 0; j < (harq_process->C/8 + 1); j++) { impp.macro_num = j; nrLDPC_encoder(harq_process->c,harq_process->d,*pz,Kb,Kr,harq_process->BG,&impp); } + stop_meas(&ue->ulsch_ldpc_encoding_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_LDPC_ENCODER_OPTIM, VCD_FUNCTION_OUT); @@ -412,8 +410,6 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, write_output("ulsch_enc_output0.m","enc0",&harq_process->d[0][0],(3*8*Kr_bytes)+12,1,4); #endif - if (temp_opp) opp_enabled = 1; - /////////// /////////////////////////////////////////////////////////////////////////////// LOG_D(PHY,"setting ndi to %d from pusch_data\n", harq_process->pusch_pdu.pusch_data.new_data_indicator); @@ -449,6 +445,7 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, Tbslbrm = nr_compute_tbslbrm(0,nb_rb,harq_process->pusch_pdu.nrOfLayers); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RATE_MATCHING_LDPC, VCD_FUNCTION_IN); + start_meas(&ue->ulsch_rate_matching_stats); nr_rate_matching_ldpc(Ilbrm, Tbslbrm, harq_process->BG, @@ -460,6 +457,7 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, Kr-F-2*(*pz), harq_process->pusch_pdu.pusch_data.rv_index, E); + stop_meas(&ue->ulsch_rate_matching_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RATE_MATCHING_LDPC, VCD_FUNCTION_OUT); @@ -481,10 +479,12 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, //start_meas(i_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_INTERLEAVING_LDPC, VCD_FUNCTION_IN); + start_meas(&ue->ulsch_interleaving_stats); nr_interleaving_ldpc(E, mod_order, harq_process->e+r_offset, harq_process->f+r_offset); + stop_meas(&ue->ulsch_interleaving_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_INTERLEAVING_LDPC, VCD_FUNCTION_OUT); //stop_meas(i_stats); @@ -509,5 +509,6 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_OUT); + stop_meas(&ue->ulsch_encoding_stats); return(0); } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 4ada19ba33..b1addf740a 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -159,7 +159,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, nb_dmrs_re_per_rb, number_dmrs_symbols, mod_order, Nl); - nr_ulsch_encoding(ulsch_ue, frame_parms, harq_pid, G); + nr_ulsch_encoding(UE, ulsch_ue, frame_parms, harq_pid, G); /////////// //////////////////////////////////////////////////////////////////// @@ -529,7 +529,8 @@ uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE, nr_normal_prefix_mod(txdataF[ap], &txdata[ap][tx_offset], 14, - frame_parms); + frame_parms, + slot); } } diff --git a/openair1/PHY/TOOLS/signal_energy.c b/openair1/PHY/TOOLS/signal_energy.c index f3dea58374..96ee211285 100644 --- a/openair1/PHY/TOOLS/signal_energy.c +++ b/openair1/PHY/TOOLS/signal_energy.c @@ -264,7 +264,7 @@ double signal_energy_fp(double *s_re[2],double *s_im[2],uint32_t nb_antennas,uin return(V/length/nb_antennas); } -double signal_energy_fp2(struct complex *s,uint32_t length) +double signal_energy_fp2(struct complexd *s,uint32_t length) { int32_t i; @@ -273,7 +273,7 @@ double signal_energy_fp2(struct complex *s,uint32_t length) for (i=0; i %f\n",s[i].x,s[i].y,V); // V= V + (s[i].y*s[i].x) + (s[i].y*s[i].x); - V= V + (s[i].x*s[i].x) + (s[i].y*s[i].y); + V= V + (s[i].r*s[i].r) + (s[i].i*s[i].i); } return(V/length); diff --git a/openair1/PHY/TOOLS/tools_defs.h b/openair1/PHY/TOOLS/tools_defs.h index 4c68c15cb6..966e93440e 100644 --- a/openair1/PHY/TOOLS/tools_defs.h +++ b/openair1/PHY/TOOLS/tools_defs.h @@ -39,11 +39,6 @@ extern "C" { #define CEILIDIV(a,b) ((a+b-1)/b) #define ROUNDIDIV(a,b) (((a<<1)+b)/(b<<1)) -struct complex { - double x; - double y; -}; - struct complexd { double r; double i; @@ -468,7 +463,7 @@ double signal_energy_fp(double *s_re[2], double *s_im[2], uint32_t nb_antennas, /*!\fn double signal_energy_fp2(struct complex *, uint32_t); \brief Computes the signal energy per subcarrier */ -double signal_energy_fp2(struct complex *s, uint32_t length); +double signal_energy_fp2(struct complexd *s, uint32_t length); int32_t iSqrt(int32_t value); diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index d1fb886341..778724218f 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -819,6 +819,8 @@ typedef struct PHY_VARS_gNB_s { uint32_t max_peak_val; + /// OFDM symbol offset divisor for UL + uint32_t ofdm_offset_divisor; /// \brief sinr for all subcarriers of the current link (used only for abstraction). /// first index: ? [0..N_RB_DL*12[ double *sinr_dB; @@ -838,6 +840,8 @@ typedef struct PHY_VARS_gNB_s { int **dl_precoder_SgNB[3]; char log2_maxp; /// holds the maximum channel/precoder coefficient + int prb_interpolation; + /// if ==0 enables phy only test mode int mac_enabled; /// counter to average prach energh over first 100 prach opportunities diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 3b1c03f377..019d5e6259 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -865,7 +865,9 @@ typedef struct { uint32_t perfect_ce; - + // flag to activate PRB based averaging of channel estimates + // when off, defaults to frequency domain interpolation + int prb_interpolation; int generate_ul_signal[NUMBER_OF_CONNECTED_gNB_MAX]; UE_NR_SCAN_INFO_t scan_info[NB_BANDS_MAX]; @@ -1002,6 +1004,7 @@ typedef struct { time_stats_t ofdm_mod_stats; time_stats_t ulsch_encoding_stats; + time_stats_t ulsch_ldpc_encoding_stats; time_stats_t ulsch_modulation_stats; time_stats_t ulsch_segmentation_stats; time_stats_t ulsch_rate_matching_stats; diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index d333732d1f..c385d323e3 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -234,8 +234,6 @@ typedef struct { int RA_PCMAX; /// Corresponding RA-RNTI for UL-grant uint16_t ra_RNTI; - /// Pointer to Msg3 payload for UL-grant - uint8_t *Msg3; /// Frame of last completed synch uint16_t sync_frame; /// Flag to indicate that prach is ready to start: it is enabled with an initial delay after the sync @@ -331,6 +329,9 @@ struct NR_DL_FRAME_PARMS { /// sequence which is computed based on carrier frequency and numerology to rotate/derotate each OFDM symbol according to Section 5.3 in 38.211 /// First dimension is for the direction of the link (0 DL, 1 UL) int16_t symbol_rotation[2][224*2]; + /// sequence used to compensate the phase rotation due to timeshifted OFDM symbols + /// First dimenstion is for different CP lengths + int16_t timeshift_symbol_rotation[4096*2] __attribute__ ((aligned (16))); /// shift of pilot position in one RB uint8_t nushift; /// SRS configuration from TS 38.331 RRC @@ -364,6 +365,8 @@ struct NR_DL_FRAME_PARMS { uint8_t ssb_index; /// PBCH polar encoder params t_nrPolar_params pbch_polar_params; + /// OFDM symbol offset divisor for UL + uint32_t ofdm_offset_divisor; }; diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index 9562e2de30..7d1e7e7684 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -17,6 +17,10 @@ void handle_nr_nfapi_pdsch_pdu(PHY_VARS_gNB *gNB,int frame,int slot, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu, uint8_t *sdu){ } +void handle_nfapi_nr_csirs_pdu(PHY_VARS_gNB *gNB, + int frame, int slot, + nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu){ + } int l1_north_init_gNB(void){return 0;} uint8_t slot_ahead=6; diff --git a/openair1/SCHED/nfapi_nr_dummy.c b/openair1/SCHED/nfapi_nr_dummy.c index db20017fb8..1286a4e48a 100644 --- a/openair1/SCHED/nfapi_nr_dummy.c +++ b/openair1/SCHED/nfapi_nr_dummy.c @@ -17,6 +17,10 @@ void handle_nr_nfapi_pdsch_pdu(PHY_VARS_gNB *gNB,int frame,int slot, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu, uint8_t *sdu){ } +void handle_nfapi_nr_csirs_pdu(PHY_VARS_gNB *gNB, + int frame, int slot, + nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu){ + } int l1_north_init_gNB(void){return 0;} uint8_t slot_ahead=6; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index f04401aac5..fa762fa5b8 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -181,71 +181,64 @@ void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO){ uint8_t number_dl_pdu = (DL_req==NULL) ? 0 : DL_req->dl_tti_request_body.nPDUs; uint8_t number_ul_dci_pdu = (UL_dci_req==NULL) ? 0 : UL_dci_req->numPdus; uint8_t number_ul_tti_pdu = (UL_tti_req==NULL) ? 0 : UL_tti_req->n_pdus; + uint8_t number_tx_data_pdu = (TX_req == NULL) ? 0 : TX_req->Number_of_PDUs; - if (DL_req != NULL && TX_req!=NULL) - LOG_D(PHY,"NFAPI: Sched_INFO:SFN/SLOT:%04d/%d DL_req:SFN/SLO:%04d/%d:dl_pdu:%d tx_req:SFN/SLOT:%04d/%d:pdus:%d;ul_dci %d ul_tti %d\n", - frame,slot, - DL_req->SFN,DL_req->Slot,number_dl_pdu, - TX_req->SFN,TX_req->Slot,TX_req->Number_of_PDUs, - number_ul_dci_pdu,number_ul_tti_pdu); + if (NFAPI_MODE == NFAPI_MONOLITHIC){ + if (DL_req != NULL && TX_req!=NULL) + LOG_D(PHY,"NFAPI: Sched_INFO:SFN/SLOT:%04d/%d DL_req:SFN/SLO:%04d/%d:dl_pdu:%d tx_req:SFN/SLOT:%04d/%d:pdus:%d;ul_dci %d ul_tti %d\n", + frame,slot, + DL_req->SFN,DL_req->Slot,number_dl_pdu, + TX_req->SFN,TX_req->Slot,TX_req->Number_of_PDUs, + number_ul_dci_pdu,number_ul_tti_pdu); - int pdcch_received=0; - gNB->num_pdsch_rnti[slot]=0; - for (int i=0; inumber_of_nr_dlsch_max; i++) { - gNB->dlsch[i][0]->rnti=0; - gNB->dlsch[i][0]->harq_mask=0; - } + int pdcch_received=0; + gNB->num_pdsch_rnti[slot]=0; + for (int i=0; inumber_of_nr_dlsch_max; i++) { + gNB->dlsch[i][0]->rnti=0; + gNB->dlsch[i][0]->harq_mask=0; + } - for (int i=0;idl_tti_request_body.dl_tti_pdu_list[i]; - LOG_D(PHY,"NFAPI: dl_pdu %d : type %d\n",i,dl_tti_pdu->PDUType); - switch (dl_tti_pdu->PDUType) { - case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: + for (int i=0;idl_tti_request_body.dl_tti_pdu_list[i]; + LOG_D(PHY,"NFAPI: dl_pdu %d : type %d\n",i,dl_tti_pdu->PDUType); + switch (dl_tti_pdu->PDUType) { + case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: + handle_nr_nfapi_ssb_pdu(gNB,frame,slot, + dl_tti_pdu); + break; - if(NFAPI_MODE != NFAPI_MODE_VNF) - handle_nr_nfapi_ssb_pdu(gNB,frame,slot, - dl_tti_pdu); + case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: + AssertFatal(pdcch_received == 0, "pdcch_received is not 0, we can only handle one PDCCH PDU per slot\n"); + handle_nfapi_nr_pdcch_pdu(gNB, + frame, slot, + &dl_tti_pdu->pdcch_pdu); + pdcch_received = 1; + break; - break; + case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: + LOG_D(PHY,"frame %d, slot %d, Got NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE for %d.%d\n",frame,slot,DL_req->SFN,DL_req->Slot); + handle_nfapi_nr_csirs_pdu(gNB, + frame, slot, + &dl_tti_pdu->csi_rs_pdu); + break; - case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: - AssertFatal(pdcch_received == 0, "pdcch_received is not 0, we can only handle one PDCCH PDU per slot\n"); - if(NFAPI_MODE != NFAPI_MODE_VNF) - handle_nfapi_nr_pdcch_pdu(gNB, - frame, slot, - &dl_tti_pdu->pdcch_pdu); - - pdcch_received = 1; - - break; - case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: - LOG_D(PHY,"frame %d, slot %d, Got NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE for %d.%d\n",frame,slot,DL_req->SFN,DL_req->Slot); - handle_nfapi_nr_csirs_pdu(gNB, - frame, slot, - &dl_tti_pdu->csi_rs_pdu); - break; - case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: - - { - LOG_D(PHY,"frame %d, slot %d, Got NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE for %d.%d\n",frame,slot,DL_req->SFN,DL_req->Slot); - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdu->pdsch_pdu.pdsch_pdu_rel15; - uint16_t pduIndex = pdsch_pdu_rel15->pduIndex; - AssertFatal(TX_req->pdu_list[pduIndex].num_TLV == 1, "TX_req->pdu_list[%d].num_TLV %d != 1\n", - pduIndex,TX_req->pdu_list[pduIndex].num_TLV); - uint8_t *sdu = (uint8_t *)TX_req->pdu_list[pduIndex].TLVs[0].value.direct; - if(NFAPI_MODE != NFAPI_MODE_VNF) - handle_nr_nfapi_pdsch_pdu(gNB,frame,slot,&dl_tti_pdu->pdsch_pdu, sdu); + case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: + LOG_D(PHY,"frame %d, slot %d, Got NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE for %d.%d\n",frame,slot,DL_req->SFN,DL_req->Slot); + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdu->pdsch_pdu.pdsch_pdu_rel15; + uint16_t pduIndex = pdsch_pdu_rel15->pduIndex; + AssertFatal(TX_req->pdu_list[pduIndex].num_TLV == 1, "TX_req->pdu_list[%d].num_TLV %d != 1\n", + pduIndex,TX_req->pdu_list[pduIndex].num_TLV); + uint8_t *sdu = (uint8_t *)TX_req->pdu_list[pduIndex].TLVs[0].value.direct; + handle_nr_nfapi_pdsch_pdu(gNB,frame,slot,&dl_tti_pdu->pdsch_pdu, sdu); + break; } } - } - // if (UL_tti_req!=NULL) memcpy(&gNB->UL_tti_req,UL_tti_req,sizeof(nfapi_nr_ul_tti_request_t)); - if(NFAPI_MODE != NFAPI_MODE_VNF) + for (int i=0;iul_dci_pdu_list[i]); } - - if(NFAPI_MODE != NFAPI_MODE_VNF) + for (int i = 0; i < number_ul_tti_pdu; i++) { switch (UL_tti_req->pdus_list[i].pdu_type) { case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: @@ -264,30 +257,22 @@ void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO){ break; } } - - if(NFAPI_MODE != NFAPI_MONOLITHIC && number_ul_tti_pdu>0) - { - oai_nfapi_ul_tti_req(UL_tti_req); } - - if (NFAPI_MODE != NFAPI_MONOLITHIC && Sched_INFO->UL_dci_req->numPdus!=0) - { - oai_nfapi_ul_dci_req(Sched_INFO->UL_dci_req); + + if (NFAPI_MODE == NFAPI_MODE_VNF) { //If VNF, oai_nfapi functions send respective p7 msgs to PNF for which nPDUs is greater than 0 + + if(number_ul_tti_pdu>0) + oai_nfapi_ul_tti_req(UL_tti_req); + + if (number_ul_dci_pdu>0) + oai_nfapi_ul_dci_req(UL_dci_req); + + if (number_dl_pdu>0) + oai_nfapi_dl_tti_req(DL_req); + + if (number_tx_data_pdu>0) + oai_nfapi_tx_data_req(TX_req); + } - if (NFAPI_MODE != NFAPI_MONOLITHIC) - { - if(Sched_INFO->DL_req->dl_tti_request_body.nPDUs>0) - { - Sched_INFO->DL_req->SFN = frame; - Sched_INFO->DL_req->Slot = slot; - oai_nfapi_dl_tti_req(Sched_INFO->DL_req); - } - if (Sched_INFO->TX_req->Number_of_PDUs > 0) - { - oai_nfapi_tx_data_req(Sched_INFO->TX_req); - } - - } - } diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c index 827e986f1b..eb0a4f8cdc 100644 --- a/openair1/SCHED_NR/nr_ru_procedures.c +++ b/openair1/SCHED_NR/nr_ru_procedures.c @@ -119,6 +119,7 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { fp->nb_prefix_samples, CYCLIC_PREFIX); slot_offset += fp->nb_prefix_samples+fp->ofdm_symbol_size; + slot_offsetF += fp->ofdm_symbol_size; } else { PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF], @@ -128,6 +129,7 @@ void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) { fp->nb_prefix_samples0, CYCLIC_PREFIX); slot_offset += fp->nb_prefix_samples0+fp->ofdm_symbol_size; + slot_offsetF += fp->ofdm_symbol_size; } } } diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index e0b7387ab9..0a87a08a56 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -147,14 +147,14 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_COMMON_TX,1); - if (NFAPI_MODE == NFAPI_MONOLITHIC || NFAPI_MODE == NFAPI_MODE_PNF) { - for (int i=0; iLmax; i++) { - if (gNB->ssb[i].active) { - nr_common_signal_procedures(gNB,frame,slot,gNB->ssb[i].ssb_pdu); - gNB->ssb[i].active = false; - } + + for (int i=0; iLmax; i++) { + if (gNB->ssb[i].active) { + nr_common_signal_procedures(gNB,frame,slot,gNB->ssb[i].ssb_pdu); + gNB->ssb[i].active = false; } } + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_COMMON_TX,0); int pdcch_pdu_id=find_nr_pdcch(frame,slot,gNB,SEARCH_EXIST); @@ -383,7 +383,13 @@ void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH // scale the 16 factor in N_TA calculation in 38.213 section 4.2 according to the used FFT size uint16_t bw_scaling = 16 * gNB->frame_parms.ofdm_symbol_size / 2048; - timing_advance_update = sync_pos / bw_scaling; + int sync_pos_rounded; + // do some integer rounding to improve TA accuracy + if (sync_pos > 0) + sync_pos_rounded = sync_pos + (bw_scaling / 2) - 1; + else + sync_pos_rounded = sync_pos - (bw_scaling / 2) - 1; + timing_advance_update = sync_pos_rounded / bw_scaling; // put timing advance command in 0..63 range timing_advance_update += 31; diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index ea12646395..4a17cb98b9 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -70,7 +70,8 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ for (i = 0; i < dl_config->number_pdus; ++i){ AssertFatal(dl_config->number_pdus < FAPI_NR_DL_CONFIG_LIST_NUM,"dl_config->number_pdus %d out of bounds\n",dl_config->number_pdus); AssertFatal(dl_config->dl_config_list[i].pdu_type<=FAPI_NR_DL_CONFIG_TYPES,"pdu_type %d > 2\n",dl_config->dl_config_list[i].pdu_type); - LOG_D(PHY, "In %s: received 1 DL %s PDU of %d total DL PDUs:\n", __FUNCTION__, dl_pdu_type[dl_config->dl_config_list[i].pdu_type - 1], dl_config->number_pdus); + LOG_D(PHY, "In %s: frame %d slot %d received 1 DL %s PDU of %d total DL PDUs:\n", + __FUNCTION__, scheduled_response->frame, slot, dl_pdu_type[dl_config->dl_config_list[i].pdu_type - 1], dl_config->number_pdus); if (dl_config->dl_config_list[i].pdu_type == FAPI_NR_DL_CONFIG_TYPE_DCI) { @@ -225,9 +226,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ break; } } - memset(ul_config, 0, sizeof(fapi_nr_ul_config_request_t)); - } } return 0; diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index bdec486f00..32f113f11c 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -253,7 +253,7 @@ void ue_ta_procedures(PHY_VARS_NR_UE *ue, int slot_tx, int frame_tx){ ue->timing_advance += (ul_time_alignment->ta_command - 31) * bw_scaling; - LOG_D(PHY, "In %s: [UE %d] [%d.%d] Got timing advance command %u from MAC, new value is %d\n", + LOG_I(PHY, "In %s: [UE %d] [%d.%d] Got timing advance command %u from MAC, new value is %d\n", __FUNCTION__, ue->Mod_id, frame_tx, @@ -949,11 +949,11 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, if (pdsch == RA_PDSCH) { if (ue->prach_resources[gNB_id]!=NULL) - dlsch0->rnti = ue->prach_resources[gNB_id]->ra_RNTI; + dlsch0->rnti = ue->prach_resources[gNB_id]->ra_RNTI; else { - LOG_E(PHY,"[UE %d] Frame %d, nr_slot_rx %d: FATAL, prach_resources is NULL\n", ue->Mod_id, frame_rx, nr_slot_rx); - //mac_xface->macphy_exit("prach_resources is NULL"); - return; + LOG_E(PHY,"[UE %d] Frame %d, nr_slot_rx %d: FATAL, prach_resources is NULL\n", ue->Mod_id, frame_rx, nr_slot_rx); + //mac_xface->macphy_exit("prach_resources is NULL"); + return; } } @@ -1270,37 +1270,37 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, default: break; } - /* d_2_1 */ - int d_2_1; - if (mapping_type_ul == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeB && start_symbol != 0) - d_2_1 = 0; - else - d_2_1 = 1; + /* d_2_1 */ + int d_2_1; + if (mapping_type_ul == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeB && start_symbol != 0) + d_2_1 = 0; + else + d_2_1 = 1; - /* d_2_2 */ - const double d_2_2 = pusch_d_2_2_timing_capability_1[numerology][1]; + /* d_2_2 */ + const double d_2_2 = pusch_d_2_2_timing_capability_1[numerology][1]; - /* N_t_1 time duration in msec of N_1 symbols corresponding to a PDSCH reception time - // N_t_2 time duration in msec of N_2 symbols corresponding to a PUSCH preparation time */ - double N_t_1 = (N_1 + d_1_1) * (ofdm_symbol_size + nb_prefix_samples) * tc_factor; - double N_t_2 = (N_2 + d_2_1) * (ofdm_symbol_size + nb_prefix_samples) * tc_factor; - if (N_t_2 < d_2_2) N_t_2 = d_2_2; + /* N_t_1 time duration in msec of N_1 symbols corresponding to a PDSCH reception time + // N_t_2 time duration in msec of N_2 symbols corresponding to a PUSCH preparation time */ + double N_t_1 = (N_1 + d_1_1) * (ofdm_symbol_size + nb_prefix_samples) * tc_factor; + double N_t_2 = (N_2 + d_2_1) * (ofdm_symbol_size + nb_prefix_samples) * tc_factor; + if (N_t_2 < d_2_2) N_t_2 = d_2_2; - /* Time alignment procedure */ - // N_t_1 + N_t_2 + N_TA_max must be in msec - const double t_subframe = 1.0; // subframe duration of 1 msec - const int ul_tx_timing_adjustment = 1 + (int)ceil(slots_per_subframe*(N_t_1 + N_t_2 + N_TA_max + 0.5)/t_subframe); + /* Time alignment procedure */ + // N_t_1 + N_t_2 + N_TA_max must be in msec + const double t_subframe = 1.0; // subframe duration of 1 msec + const int ul_tx_timing_adjustment = 1 + (int)ceil(slots_per_subframe*(N_t_1 + N_t_2 + N_TA_max + 0.5)/t_subframe); - if (ul_time_alignment->apply_ta == 1){ - ul_time_alignment->ta_slot = (nr_slot_rx + ul_tx_timing_adjustment) % slots_per_frame; - if (nr_slot_rx + ul_tx_timing_adjustment > slots_per_frame){ - ul_time_alignment->ta_frame = (frame_rx + 1) % 1024; - } else { - ul_time_alignment->ta_frame = frame_rx; - } - // reset TA flag - ul_time_alignment->apply_ta = 0; - LOG_D(PHY,"Frame %d slot %d -- Starting UL time alignment procedures. TA update will be applied at frame %d slot %d\n", frame_rx, nr_slot_rx, ul_time_alignment->ta_frame, ul_time_alignment->ta_slot); + if (ul_time_alignment->apply_ta == 1){ + ul_time_alignment->ta_slot = (nr_slot_rx + ul_tx_timing_adjustment) % slots_per_frame; + if (nr_slot_rx + ul_tx_timing_adjustment > slots_per_frame){ + ul_time_alignment->ta_frame = (frame_rx + 1) % 1024; + } else { + ul_time_alignment->ta_frame = frame_rx; + } + // reset TA flag + ul_time_alignment->apply_ta = 0; + LOG_D(PHY,"Frame %d slot %d -- Starting UL time alignment procedures. TA update will be applied at frame %d slot %d\n", frame_rx, nr_slot_rx, ul_time_alignment->ta_frame, ul_time_alignment->ta_slot); } } } @@ -2138,7 +2138,8 @@ void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, uint8_t int16_t ra_preamble_rx_power = (int16_t)(prach_resources->ra_PREAMBLE_RECEIVED_TARGET_POWER - pathloss + 30); ue->tx_power_dBm[nr_slot_tx] = min(nr_get_Pcmax(mod_id), ra_preamble_rx_power); - LOG_D(PHY,"DEBUG [UE %d][RAPROC][%d.%d]: Generating PRACH Msg1 (preamble %d, PL %d dB, P0_PRACH %d, TARGET_RECEIVED_POWER %d dBm, RA-RNTI %x)\n", + LOG_D(PHY, "In %s: [UE %d][RAPROC][%d.%d]: Generating PRACH Msg1 (preamble %d, PL %d dB, P0_PRACH %d, TARGET_RECEIVED_POWER %d dBm, RA-RNTI %x)\n", + __FUNCTION__, mod_id, frame_tx, nr_slot_tx, diff --git a/openair1/SIMULATION/LTE_PHY/dlsim.c b/openair1/SIMULATION/LTE_PHY/dlsim.c index adfda7a0c8..9a9ccaf1f7 100644 --- a/openair1/SIMULATION/LTE_PHY/dlsim.c +++ b/openair1/SIMULATION/LTE_PHY/dlsim.c @@ -200,8 +200,8 @@ void DL_channel(RU_t *ru,PHY_VARS_UE *UE,uint subframe,int awgn_flag,double SNR, for (u=0; u<2*ru->frame_parms->N_RB_DL; u++) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - channelx = eNB2UE[0]->chF[aarx+(aatx*eNB2UE[0]->nb_rx)][u].x; - channely = eNB2UE[0]->chF[aarx+(aatx*eNB2UE[0]->nb_rx)][u].y; + channelx = eNB2UE[0]->chF[aarx+(aatx*eNB2UE[0]->nb_rx)][u].r; + channely = eNB2UE[0]->chF[aarx+(aatx*eNB2UE[0]->nb_rx)][u].i; fprintf(csv_fd,"%e+i*(%e),",channelx,channely); } } @@ -213,8 +213,8 @@ void DL_channel(RU_t *ru,PHY_VARS_UE *UE,uint subframe,int awgn_flag,double SNR, for (u=0; u<2*ru->frame_parms->N_RB_DL; u++) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - channelx = eNB2UE[1]->chF[aarx+(aatx*eNB2UE[1]->nb_rx)][u].x; - channely = eNB2UE[1]->chF[aarx+(aatx*eNB2UE[1]->nb_rx)][u].y; + channelx = eNB2UE[1]->chF[aarx+(aatx*eNB2UE[1]->nb_rx)][u].r; + channely = eNB2UE[1]->chF[aarx+(aatx*eNB2UE[1]->nb_rx)][u].i; fprintf(csv_fd,"%e+i*(%e),",channelx,channely); } } @@ -225,8 +225,8 @@ void DL_channel(RU_t *ru,PHY_VARS_UE *UE,uint subframe,int awgn_flag,double SNR, for (u=0; u<2*ru->frame_parms->N_RB_DL; u++) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - channelx = eNB2UE[2]->chF[aarx+(aatx*eNB2UE[2]->nb_rx)][u].x; - channely = eNB2UE[2]->chF[aarx+(aatx*eNB2UE[2]->nb_rx)][u].y; + channelx = eNB2UE[2]->chF[aarx+(aatx*eNB2UE[2]->nb_rx)][u].r; + channely = eNB2UE[2]->chF[aarx+(aatx*eNB2UE[2]->nb_rx)][u].i; fprintf(csv_fd,"%e+i*(%e),",channelx,channely); } } @@ -237,8 +237,8 @@ void DL_channel(RU_t *ru,PHY_VARS_UE *UE,uint subframe,int awgn_flag,double SNR, for (u=0; u<2*ru->frame_parms->N_RB_DL; u++) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - channelx = eNB2UE[3]->chF[aarx+(aatx*eNB2UE[3]->nb_rx)][u].x; - channely = eNB2UE[3]->chF[aarx+(aatx*eNB2UE[3]->nb_rx)][u].y; + channelx = eNB2UE[3]->chF[aarx+(aatx*eNB2UE[3]->nb_rx)][u].r; + channely = eNB2UE[3]->chF[aarx+(aatx*eNB2UE[3]->nb_rx)][u].i; fprintf(csv_fd,"%e+i*(%e),",channelx,channely); } } diff --git a/openair1/SIMULATION/LTE_PHY/test.c b/openair1/SIMULATION/LTE_PHY/test.c index 06785f7cdc..6b78dad4e7 100644 --- a/openair1/SIMULATION/LTE_PHY/test.c +++ b/openair1/SIMULATION/LTE_PHY/test.c @@ -75,7 +75,7 @@ int main(int argc, char **argv) double rx_gain; int rx_pwr2, target_rx_pwr_dB; - struct complex **ch; + struct complexd **ch; unsigned char first_call = 1; LTE_DL_FRAME_PARMS frame_parms; @@ -169,10 +169,10 @@ int main(int argc, char **argv) channel_length = (int) 11+2*BW*Td; - ch = (struct complex**) malloc(4 * sizeof(struct complex*)); + ch = (struct complexd**) malloc(4 * sizeof(struct complexd*)); for (i = 0; i<4; i++) - ch[i] = (struct complex*) malloc(channel_length * sizeof(struct complex)); + ch[i] = (struct complexd*) malloc(channel_length * sizeof(struct complexd)); randominit(0); set_taus_seed(0); diff --git a/openair1/SIMULATION/LTE_PHY/ulsim.c b/openair1/SIMULATION/LTE_PHY/ulsim.c index 0d39e6e533..20a348f6c3 100644 --- a/openair1/SIMULATION/LTE_PHY/ulsim.c +++ b/openair1/SIMULATION/LTE_PHY/ulsim.c @@ -1111,8 +1111,8 @@ int main(int argc, char **argv) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { // abs_channel = (eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][u].x*eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][u].x + eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][u].y*eNB2UE->chF[aarx+(aatx*eNB2UE->nb_rx)][u].y); - channelx = UE2eNB->chF[aarx+(aatx*UE2eNB->nb_rx)][u].x; - channely = UE2eNB->chF[aarx+(aatx*UE2eNB->nb_rx)][u].y; + channelx = UE2eNB->chF[aarx+(aatx*UE2eNB->nb_rx)][u].r; + channely = UE2eNB->chF[aarx+(aatx*UE2eNB->nb_rx)][u].i; // if(transmission_m==5){ fprintf(csv_fdUL,"%e+i*(%e),",channelx,channely); // } diff --git a/openair1/SIMULATION/LTE_PHY/ulsim2.c b/openair1/SIMULATION/LTE_PHY/ulsim2.c index 69d4dc2e3f..e994d4fc36 100644 --- a/openair1/SIMULATION/LTE_PHY/ulsim2.c +++ b/openair1/SIMULATION/LTE_PHY/ulsim2.c @@ -49,7 +49,7 @@ int main(int argc, char **argv) double amps[8] = {0.3868472 , 0.3094778 , 0.1547389 , 0.0773694 , 0.0386847 , 0.0193424 , 0.0096712 , 0.0038685}; double aoa=.03,ricean_factor=1; //0.0000005; int channel_length; - struct complex **ch; + struct complexd **ch; unsigned char pbch_pdu[6]; int sync_pos, sync_pos_slot; FILE *rx_frame_file; @@ -170,10 +170,10 @@ int main(int argc, char **argv) bzero(r_im[i],FRAME_LENGTH_COMPLEX_SAMPLES*sizeof(double)); } - ch = (struct complex**) malloc(4 * sizeof(struct complex*)); + ch = (struct complexd**) malloc(4 * sizeof(struct complexd*)); for (i = 0; i<4; i++) - ch[i] = (struct complex*) malloc(channel_length * sizeof(struct complex)); + ch[i] = (struct complexd*) malloc(channel_length * sizeof(struct complexd)); generate_srs_tx(lte_frame_parms,lte_ue_common_vars->txdataF[0],AMP,0); generate_drs_puch(lte_frame_parms,lte_ue_common_vars->txdataF[0],AMP,0,first_rb,nb_rb); diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index a8376dc3b8..df2356e324 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -226,12 +226,17 @@ int DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(module_id_t module_idP, void processSlotTX(void *arg) {} -//nFAPI P7 dummy functions +//nFAPI P7 dummy functions to avoid linking errors int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { return(0); } int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req){ return(0); } int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req){ return(0); } int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { return(0); } +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { return(0); } +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { return(0); } +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { return(0); } +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { return(0); } // needed for some functions openair0_config_t openair0_cfg[MAX_CARDS]; @@ -408,6 +413,7 @@ int main(int argc, char **argv) uint16_t rbSize = 106; uint8_t mcsIndex = 9; uint8_t dlsch_threads = 0; + int prb_inter = 0; if ( load_configmodule(argc,argv,CONFIG_ENABLECMDLINEONLY) == 0) { exit_fun("[NR_DLSIM] Error, configuration module init failed\n"); } @@ -418,7 +424,7 @@ int main(int argc, char **argv) FILE *scg_fd=NULL; - while ((c = getopt (argc, argv, "f:hA:pf:g:i:j:n:s:S:t:x:y:z:M:N:F:GR:dPIL:Ea:b:d:e:m:w:T:U:q")) != -1) { + while ((c = getopt (argc, argv, "f:hA:pf:g:in:s:S:t:x:y:z:M:N:F:GR:dPIL:Ea:b:d:e:m:w:T:U:q")) != -1) { switch (c) { case 'f': scg_fd = fopen(optarg,"r"); @@ -474,14 +480,10 @@ int main(int argc, char **argv) break; - /*case 'i': - interf1=atoi(optarg); + case 'i': + prb_inter=1; break; - case 'j': - interf2=atoi(optarg); - break;*/ - case 'n': n_trials = atoi(optarg); break; @@ -640,7 +642,7 @@ int main(int argc, char **argv) printf("-g [A,B,C,D,E,F,G,R] Use 3GPP SCM (A,B,C,D) or 36-101 (E-EPA,F-EVA,G-ETU) models or R for MIMO model (ignores delay spread and Ricean factor)\n"); printf("-y Number of TX antennas used in gNB\n"); printf("-z Number of RX antennas used in UE\n"); - //printf("-i Relative strength of first intefering gNB (in dB) - cell_id mod 3 = 1\n"); + printf("-i Activate PRB based averaging for channel estimation. Frequncy domain interpolation by default.\n"); //printf("-j Relative strength of second intefering gNB (in dB) - cell_id mod 3 = 2\n"); printf("-R N_RB_DL\n"); printf("-O oversampling factor (1,2,4,8,16)\n"); @@ -686,6 +688,7 @@ int main(int argc, char **argv) memset(RC.gNB[0],0,sizeof(PHY_VARS_gNB)); gNB = RC.gNB[0]; + gNB->ofdm_offset_divisor = UINT_MAX; frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH) frame_parms->nb_antennas_tx = n_tx; frame_parms->nb_antennas_rx = n_rx; @@ -910,6 +913,7 @@ int main(int argc, char **argv) UE->if_inst->phy_config_request = nr_ue_phy_config_request; UE->if_inst->dl_indication = nr_ue_dl_indication; UE->if_inst->ul_indication = dummy_nr_ue_ul_indication; + UE->prb_interpolation = prb_inter; UE_mac->if_module = nr_ue_if_module_init(0); @@ -1082,7 +1086,8 @@ int main(int argc, char **argv) nr_normal_prefix_mod(&gNB->common_vars.txdataF[aa][txdataF_offset], &txdata[aa][tx_offset], 14, - frame_parms); + frame_parms, + slot); } } @@ -1317,7 +1322,7 @@ int main(int argc, char **argv) LOG_M("rxsig0.m","rxs0", UE->common_vars.rxdata[0], frame_length_complex_samples, 1, 1); if (UE->frame_parms.nb_antennas_rx>1) LOG_M("rxsig1.m","rxs1", UE->common_vars.rxdata[1], frame_length_complex_samples, 1, 1); - LOG_M("chestF0.m","chF0",UE->pdsch_vars[0][0]->dl_ch_estimates_ext,N_RB_DL*12*14,1,1); + LOG_M("chestF0.m","chF0",&UE->pdsch_vars[0][0]->dl_ch_estimates_ext[0][0],g_rbSize*12*14,1,1); write_output("rxF_comp.m","rxFc",&UE->pdsch_vars[0][0]->rxdataF_comp0[0][0],N_RB_DL*12*14,1,1); LOG_M("rxF_llr.m","rxFllr",UE->pdsch_vars[UE_proc.thread_id][0]->llr[0],available_bits,1,0); break; diff --git a/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c b/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c index 70d65a3dc2..056b018504 100644 --- a/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c +++ b/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c @@ -8,6 +8,12 @@ int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { return(0); int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req){ return(0); } int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req){ return(0); } int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { return(0); } +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { return(0); } +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { return(0); } +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { return(0); } +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { return(0); } + int32_t get_uldl_offset(int nr_bandP) { return(0); } NR_IF_Module_t *NR_IF_Module_init(int Mod_id) {return(NULL);} nfapi_mode_t nfapi_mod; diff --git a/openair1/SIMULATION/NR_PHY/nr_dummy_functions_prach.c b/openair1/SIMULATION/NR_PHY/nr_dummy_functions_prach.c index a930c96a75..76f4e826ee 100644 --- a/openair1/SIMULATION/NR_PHY/nr_dummy_functions_prach.c +++ b/openair1/SIMULATION/NR_PHY/nr_dummy_functions_prach.c @@ -3,9 +3,15 @@ int oai_nfapi_tx_req(nfapi_tx_request_t *tx_req) { re int oai_nfapi_dl_config_req(nfapi_dl_config_request_t *dl_config_req) { return(0); } int oai_nfapi_ul_config_req(nfapi_ul_config_request_t *ul_config_req) { return(0); } int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { return(0); } - int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req){ return(0); } - int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req){ return(0); } - int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } +int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req){ return(0); } +int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req){ return(0); } +int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { return(0); } +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { return(0); } +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { return(0); } +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { return(0); } +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { return(0); } + int32_t get_uldl_offset(int nr_bandP) { return(0); } NR_IF_Module_t *NR_IF_Module_init(int Mod_id) {return(NULL);} int dummy_nr_ue_dl_indication(nr_downlink_indication_t *dl_info) { return(0); } @@ -315,7 +321,7 @@ void nr_Msg1_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint mac->RA_attempt_number++; } -void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint8_t gNB_id){ +void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, slot_t slotP, uint8_t gNB_id){ AssertFatal(CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); LOG_D(MAC,"[UE %d][RAPROC] Frame %d : Msg3_tx: Starting contention resolution timer\n", mod_id, frameP); NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c index 448e1e7799..9d3ab17ebc 100644 --- a/openair1/SIMULATION/NR_PHY/pbchsim.c +++ b/openair1/SIMULATION/NR_PHY/pbchsim.c @@ -110,7 +110,9 @@ void nr_phy_config_request_sim_pbchsim(PHY_VARS_gNB *gNB, gNB_config->carrier_config.dl_bandwidth.value = config_bandwidth(mu, N_RB_DL, fp->nr_band); + fp->ofdm_offset_divisor = UINT_MAX; nr_init_frame_parms(gNB_config, fp); + init_timeshift_rotation(fp); init_symbol_rotation(fp); @@ -406,6 +408,7 @@ int main(int argc, char **argv) RC.gNB = (PHY_VARS_gNB**) malloc(sizeof(PHY_VARS_gNB *)); RC.gNB[0] = malloc(sizeof(PHY_VARS_gNB)); gNB = RC.gNB[0]; + gNB->ofdm_offset_divisor = UINT_MAX; frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH) frame_parms->nb_antennas_tx = n_tx; frame_parms->nb_antennas_rx = n_rx; diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index e601a0d7c1..5d5d600d84 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -81,6 +81,11 @@ int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { return(0); int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req){ return(0); } int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req){ return(0); } int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { return(0); } +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { return(0); } +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { return(0); } +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { return(0); } +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { return(0); } void rrc_data_ind( diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index c889e6dcb9..94aea36a67 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -504,7 +504,7 @@ int main(int argc, char **argv) unsigned int G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, Nl); if (input_fd == NULL) { - nr_ulsch_encoding(ulsch_ue, frame_parms, harq_pid, G); + nr_ulsch_encoding(UE, ulsch_ue, frame_parms, harq_pid, G); } printf("\n"); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index b2e82060b8..0b5b9a86fc 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -212,6 +212,11 @@ int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { return(0); int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req){ return(0); } int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req){ return(0); } int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { return(0); } +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { return(0); } +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { return(0); } +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { return(0); } +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { return(0); } int nr_derive_key(int alg_type, uint8_t alg_id, const uint8_t key[32], uint8_t **out) @@ -297,6 +302,7 @@ int main(int argc, char **argv) float effRate; //float eff_tp_check = 0.7; uint8_t snrRun; + int prb_inter = 0; int enable_ptrs = 0; int modify_dmrs = 0; @@ -330,7 +336,7 @@ int main(int argc, char **argv) /* initialize the sin-cos table */ InitSinLUT(); - while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:i:j:kl:m:n:p:r:s:y:z:F:G:H:M:N:PR:S:T:U:L:Z")) != -1) { + while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:ikl:m:n:p:r:s:u:w:y:z:F:G:H:M:N:PR:S:T:U:L:Z")) != -1) { printf("handling optarg %c\n",c); switch (c) { @@ -422,14 +428,10 @@ int main(int argc, char **argv) break; - /*case 'i': - interf1 = atoi(optarg); - break; + case 'i': + prb_inter=1; + break; - case 'j': - interf2 = atoi(optarg); - break;*/ - case 'k': printf("Setting threequarter_fs_flag\n"); openair0_cfg[0].threequarter_fs= 1; @@ -460,6 +462,14 @@ int main(int argc, char **argv) printf("Setting SNR0 to %f\n", snr0); break; + case 'u': + mu = atoi(optarg); + break; + + case 'w': + start_rb = atoi(optarg); + break; + /* case 't': eff_tp_check = (float)atoi(optarg)/100; @@ -581,13 +591,15 @@ int main(int argc, char **argv) printf("-f Number of frames to simulate\n"); printf("-g [A,B,C,D,E,F,G] Use 3GPP SCM (A,B,C,D) or 36-101 (E-EPA,F-EVA,G-ETU) models (ignores delay spread and Ricean factor)\n"); printf("-h This message\n"); - //printf("-i Relative strength of first intefering eNB (in dB) - cell_id mod 3 = 1\n"); + printf("-i Activate PRB based averaging for channel estimation. Frequncy domain interpolation by default.\n"); //printf("-j Relative strength of second intefering eNB (in dB) - cell_id mod 3 = 2\n"); printf("-s Starting SNR, runs from SNR0 to SNR0 + 10 dB if ending SNR isn't given\n"); printf("-m MCS value\n"); printf("-n Number of trials to simulate\n"); printf("-p Use extended prefix mode\n"); printf("-t Delay spread for multipath channel\n"); + printf("-u Set the numerology\n"); + printf("-w Start PRB for PUSCH\n"); //printf("-x Transmission mode (1,2,6 for the moment)\n"); printf("-y Number of TX antennas used in eNB\n"); printf("-z Number of RX antennas used in UE\n"); @@ -628,10 +640,12 @@ int main(int argc, char **argv) if (N_RB_UL >= 217) sampling_frequency = 122.88; else if (N_RB_UL >= 106) sampling_frequency = 61.44; + else if (N_RB_UL >= 32) sampling_frequency = 32.72; else { printf("Need at least 106 PRBs\b"); exit(-1); } if (N_RB_UL == 273) bandwidth = 100; else if (N_RB_UL == 217) bandwidth = 80; else if (N_RB_UL == 106) bandwidth = 40; + else if (N_RB_UL == 32) bandwidth = 50; else { printf("Add N_RB_UL %d\n",N_RB_UL); exit(-1); } if (openair0_cfg[0].threequarter_fs == 1) sampling_frequency*=.75; @@ -652,6 +666,7 @@ int main(int argc, char **argv) RC.gNB = (PHY_VARS_gNB **) malloc(sizeof(PHY_VARS_gNB *)); RC.gNB[0] = calloc(1,sizeof(PHY_VARS_gNB)); gNB = RC.gNB[0]; + gNB->ofdm_offset_divisor = UINT_MAX; gNB->threadPool = (tpool_t*)malloc(sizeof(tpool_t)); gNB->respDecode = (notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); char tp_param[] = "n"; @@ -664,6 +679,7 @@ int main(int argc, char **argv) gNB->UL_INFO.crc_ind.crc_list = (nfapi_nr_crc_t *)malloc(NB_UE_INST*sizeof(nfapi_nr_crc_t)); gNB->UL_INFO.rx_ind.number_of_pdus = 0; gNB->UL_INFO.crc_ind.number_crcs = 0; + gNB->prb_interpolation = prb_inter; frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH) @@ -796,7 +812,11 @@ int main(int argc, char **argv) nr_scheduled_response_t scheduled_response; fapi_nr_ul_config_request_t ul_config; fapi_nr_tx_request_t tx_req; - + + memset(&scheduled_response, 0, sizeof(scheduled_response)); + memset(&ul_config, 0, sizeof(ul_config)); + memset(&tx_req, 0, sizeof(tx_req)); + uint8_t ptrs_mcs1 = 2; uint8_t ptrs_mcs2 = 4; uint8_t ptrs_mcs3 = 10; @@ -944,9 +964,12 @@ int main(int argc, char **argv) input_fd); if (read_errors==0) exit(1); for (int i=0;i<16;i+=2) printf("slot_offset %d : %d,%d\n", - slot_offset, - ((int16_t*)&gNB->common_vars.rxdata[0][slot_offset])[i], - ((int16_t*)&gNB->common_vars.rxdata[0][slot_offset])[1+i]); + slot_offset, + ((int16_t*)&gNB->common_vars.rxdata[0][slot_offset])[i], + ((int16_t*)&gNB->common_vars.rxdata[0][slot_offset])[1+i]); + + mod_order = nr_get_Qm_ul(Imcs, mcs_table); + code_rate = nr_get_code_rate_ul(Imcs, mcs_table); } for (SNR = snr0; SNR < snr1; SNR += snr_step) { @@ -968,6 +991,10 @@ int main(int argc, char **argv) reset_meas(&gNB->ulsch_llr_stats); reset_meas(&gNB->ulsch_channel_compensation_stats); reset_meas(&gNB->ulsch_rbs_extraction_stats); + reset_meas(&UE->ulsch_ldpc_encoding_stats); + reset_meas(&UE->ulsch_rate_matching_stats); + reset_meas(&UE->ulsch_interleaving_stats); + reset_meas(&UE->ulsch_encoding_stats); clear_pusch_stats(gNB); for (trial = 0; trial < n_trials; trial++) { @@ -1160,7 +1187,7 @@ int main(int argc, char **argv) frame_parms->ofdm_symbol_size/(12*nb_rb)); for (i=0; inb_antennas_rx; aa++) { + for (int aa=0; aanb_antennas_tx; aa++) { s_re[aa][i] = ((double)(((short *)&UE->common_vars.txdata[aa][slot_offset]))[(i<<1)]); s_im[aa][i] = ((double)(((short *)&UE->common_vars.txdata[aa][slot_offset]))[(i<<1)+1]); } @@ -1228,6 +1255,8 @@ int main(int argc, char **argv) &gNB->pusch_vars[0]->rxdataF_ext[0][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); LOG_M("chestF0.m","chF0", &gNB->pusch_vars[0]->ul_ch_estimates[0][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); + LOG_M("chestT0.m","chT0", + &gNB->pusch_vars[0]->ul_ch_estimates_time[0][0],frame_parms->ofdm_symbol_size,1,1); LOG_M("chestF0_ext.m","chF0_ext", &gNB->pusch_vars[0]->ul_ch_estimates_ext[0][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); @@ -1345,6 +1374,11 @@ int main(int argc, char **argv) printStatIndent2(&gNB->ulsch_llr_stats,"ULSCH llr computation"); printStatIndent(&gNB->ulsch_unscrambling_stats,"ULSCH unscrambling"); printStatIndent(&gNB->ulsch_decoding_stats,"ULSCH total decoding time"); + printStatIndent(&UE->ulsch_encoding_stats,"ULSCH total encoding time"); + printStatIndent2(&UE->ulsch_segmentation_stats,"ULSCH segmentation time"); + printStatIndent2(&UE->ulsch_ldpc_encoding_stats,"ULSCH LDPC encoder time"); + printStatIndent2(&UE->ulsch_rate_matching_stats,"ULSCH rate-matching time"); + printStatIndent2(&UE->ulsch_interleaving_stats,"ULSCH interleaving time"); //printStatIndent2(&gNB->ulsch_deinterleaving_stats,"ULSCH deinterleaving"); //printStatIndent2(&gNB->ulsch_rate_unmatching_stats,"ULSCH rate matching rx"); //printStatIndent2(&gNB->ulsch_ldpc_decoding_stats,"ULSCH ldpc decoding"); diff --git a/openair1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c b/openair1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c index 4d17fe723c..b8a5c7a780 100644 --- a/openair1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c +++ b/openair1/SIMULATION/NR_UE_PHY/unit_tests/src/dummy_functions.c @@ -146,6 +146,11 @@ int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { return(0); int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req){ return(0); } int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req){ return(0); } int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req){ return(0); } +int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind) { return(0); } +int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind) { return(0); } +int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind) { return(0); } +int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind) { return(0); } +int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { return(0); } int oai_nfapi_dl_config_req(nfapi_dl_config_request_t *dl_config_req) { return(0); } diff --git a/openair1/SIMULATION/TOOLS/abstraction.c b/openair1/SIMULATION/TOOLS/abstraction.c index 7ac1980417..d19afcd030 100644 --- a/openair1/SIMULATION/TOOLS/abstraction.c +++ b/openair1/SIMULATION/TOOLS/abstraction.c @@ -111,14 +111,14 @@ int freq_channel(channel_desc_t *desc,uint16_t nb_rb,int16_t n_samples) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].x=0.0; - desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].y=0.0; + desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].r=0.0; + desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].i=0.0; for (l=0; l<(int)desc->nb_taps; l++) { - desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].x+=(desc->a[l][aarx+(aatx*desc->nb_rx)].x*clut[l]+ - desc->a[l][aarx+(aatx*desc->nb_rx)].y*slut[l]); - desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].y+=(-desc->a[l][aarx+(aatx*desc->nb_rx)].x*slut[l]+ - desc->a[l][aarx+(aatx*desc->nb_rx)].y*clut[l]); + desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].r+=(desc->a[l][aarx+(aatx*desc->nb_rx)].r*clut[l]+ + desc->a[l][aarx+(aatx*desc->nb_rx)].i*slut[l]); + desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].i+=(-desc->a[l][aarx+(aatx*desc->nb_rx)].r*slut[l]+ + desc->a[l][aarx+(aatx*desc->nb_rx)].i*clut[l]); } } } @@ -138,42 +138,42 @@ double compute_pbch_sinr(channel_desc_t *desc, uint16_t f; uint8_t aarx,aatx; double S; - struct complex S_i1; - struct complex S_i2; + struct complexd S_i1; + struct complexd S_i2; avg_sinr=0.0; // printf("nb_rb %d\n",nb_rb); for (f=(nb_rb-6); f<(nb_rb+6); f++) { S = 0.0; - S_i1.x =0.0; - S_i1.y =0.0; - S_i2.x =0.0; - S_i2.y =0.0; + S_i1.r =0.0; + S_i1.i =0.0; + S_i2.r =0.0; + S_i2.i =0.0; for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - S += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc->chF[aarx+(aatx*desc->nb_rx)][f].x + - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc->chF[aarx+(aatx*desc->nb_rx)][f].y); + S += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc->chF[aarx+(aatx*desc->nb_rx)][f].r + + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc->chF[aarx+(aatx*desc->nb_rx)][f].i); // printf("%d %d chF[%d] => (%f,%f)\n",aarx,aatx,f,desc->chF[aarx+(aatx*desc->nb_rx)][f].x,desc->chF[aarx+(aatx*desc->nb_rx)][f].y); if (desc_i1) { - S_i1.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x + - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y); - S_i1.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y - - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x); + S_i1.r += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].r + + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].i); + S_i1.i += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].i - + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].r); } if (desc_i2) { - S_i2.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x + - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y); - S_i2.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y - - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x); + S_i2.r += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].r + + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].i); + S_i2.r += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].i - + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].r); } } } // printf("snr %f f %d : S %f, S_i1 %f, S_i2 %f\n",snr,f-nb_rb,S,snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y),snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y)); - avg_sinr += (snr*S/(desc->nb_tx+snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y)+snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y))); + avg_sinr += (snr*S/(desc->nb_tx+snr_i1*sqrt(S_i1.r*S_i1.r + S_i1.i*S_i1.i)+snr_i2*sqrt(S_i2.r*S_i2.r + S_i2.i*S_i2.i))); } // printf("avg_sinr %f (%f,%f,%f)\n",avg_sinr/12.0,snr,snr_i1,snr_i2); @@ -191,42 +191,42 @@ double compute_sinr(channel_desc_t *desc, uint16_t f; uint8_t aarx,aatx; double S; - struct complex S_i1; - struct complex S_i2; + struct complexd S_i1; + struct complexd S_i2; DevAssert( nb_rb > 0 ); avg_sinr=0.0; // printf("nb_rb %d\n",nb_rb); for (f=0; f<2*nb_rb; f++) { S = 0.0; - S_i1.x =0.0; - S_i1.y =0.0; - S_i2.x =0.0; - S_i2.y =0.0; + S_i1.r =0.0; + S_i1.i =0.0; + S_i2.r =0.0; + S_i2.i =0.0; for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - S += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc->chF[aarx+(aatx*desc->nb_rx)][f].x + - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc->chF[aarx+(aatx*desc->nb_rx)][f].y); + S += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc->chF[aarx+(aatx*desc->nb_rx)][f].r + + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc->chF[aarx+(aatx*desc->nb_rx)][f].i); if (desc_i1) { - S_i1.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x + - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y); - S_i1.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y - - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x); + S_i1.r += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].r + + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].i); + S_i1.i += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].i - + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].r); } if (desc_i2) { - S_i2.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x + - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y); - S_i2.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y - - desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x); + S_i2.r += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].r + + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].i); + S_i2.i += (desc->chF[aarx+(aatx*desc->nb_rx)][f].r*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].i - + desc->chF[aarx+(aatx*desc->nb_rx)][f].i*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].r); } } } // printf("f %d : S %f, S_i1 %f, S_i2 %f\n",f-nb_rb,snr*S,snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y),snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y)); - avg_sinr += (snr*S/(desc->nb_tx+snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y)+snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y))); + avg_sinr += (snr*S/(desc->nb_tx+snr_i1*sqrt(S_i1.r*S_i1.r + S_i1.i*S_i1.i)+snr_i2*sqrt(S_i2.r*S_i2.r + S_i2.i*S_i2.i))); } // printf("avg_sinr %f (%f,%f,%f)\n",avg_sinr/12.0,snr,snr_i1,snr_i2); diff --git a/openair1/SIMULATION/TOOLS/multipath_channel.c b/openair1/SIMULATION/TOOLS/multipath_channel.c index 3608eb7da2..3045d8ca6c 100644 --- a/openair1/SIMULATION/TOOLS/multipath_channel.c +++ b/openair1/SIMULATION/TOOLS/multipath_channel.c @@ -156,7 +156,7 @@ void multipath_channel(channel_desc_t *desc, { int i,ii,j,l; - struct complex rx_tmp,tx; + struct complexd rx_tmp,tx; double path_loss = pow(10,desc->path_loss_dB/20); int dd; @@ -183,32 +183,32 @@ void multipath_channel(channel_desc_t *desc, for (i=0; i<((int)length-dd); i++) { for (ii=0; iinb_rx; ii++) { - rx_tmp.x = 0; - rx_tmp.y = 0; + rx_tmp.r = 0; + rx_tmp.i = 0; for (j=0; jnb_tx; j++) { for (l = 0; l<(int)desc->channel_length; l++) { if ((i>=0) && (i-l)>=0) { - tx.x = tx_sig_re[j][i-l]; - tx.y = tx_sig_im[j][i-l]; + tx.r = tx_sig_re[j][i-l]; + tx.i = tx_sig_im[j][i-l]; } else { - tx.x =0; - tx.y =0; + tx.r =0; + tx.i =0; } - rx_tmp.x += (tx.x * desc->ch[ii+(j*desc->nb_rx)][l].x) - (tx.y * desc->ch[ii+(j*desc->nb_rx)][l].y); - rx_tmp.y += (tx.y * desc->ch[ii+(j*desc->nb_rx)][l].x) + (tx.x * desc->ch[ii+(j*desc->nb_rx)][l].y); + rx_tmp.r += (tx.r * desc->ch[ii+(j*desc->nb_rx)][l].r) - (tx.i * desc->ch[ii+(j*desc->nb_rx)][l].i); + rx_tmp.i += (tx.i * desc->ch[ii+(j*desc->nb_rx)][l].r) + (tx.r * desc->ch[ii+(j*desc->nb_rx)][l].i); if (i==0 && log_channel == 1) { - printf("channel[%d][%d][%d] = %f dB (%e,%e)\n",ii,j,l,10*log10(pow(desc->ch[ii+(j*desc->nb_rx)][l].x,2.0)+pow(desc->ch[ii+(j*desc->nb_rx)][l].y,2.0)), - desc->ch[ii+(j*desc->nb_rx)][l].x, - desc->ch[ii+(j*desc->nb_rx)][l].y); + printf("channel[%d][%d][%d] = %f dB (%e,%e)\n",ii,j,l,10*log10(pow(desc->ch[ii+(j*desc->nb_rx)][l].r,2.0)+pow(desc->ch[ii+(j*desc->nb_rx)][l].i,2.0)), + desc->ch[ii+(j*desc->nb_rx)][l].r, + desc->ch[ii+(j*desc->nb_rx)][l].i); } } //l } // j - rx_sig_re[ii][i+dd] = rx_tmp.x*path_loss; - rx_sig_im[ii][i+dd] = rx_tmp.y*path_loss; + rx_sig_re[ii][i+dd] = rx_tmp.r*path_loss; + rx_sig_im[ii][i+dd] = rx_tmp.i*path_loss; #ifdef DEBUG_CHANNEL if ((i%32)==0) { printf("rx aa %d: %p %p %f,%f => %e,%e\n",ii,rx_sig_re[ii],rx_sig_im[ii],rx_tmp.x,rx_tmp.y,rx_sig_re[ii][i-dd],rx_sig_im[ii][i-dd]); diff --git a/openair1/SIMULATION/TOOLS/random_channel.c b/openair1/SIMULATION/TOOLS/random_channel.c index 3acc3f485f..d393bb1342 100644 --- a/openair1/SIMULATION/TOOLS/random_channel.c +++ b/openair1/SIMULATION/TOOLS/random_channel.c @@ -72,7 +72,7 @@ void fill_channel_desc(channel_desc_t *chan_desc, uint8_t channel_length, double *amps, double *delays, - struct complex *R_sqrt, + struct complexd *R_sqrt, double Td, double sampling_rate, double channel_bandwidth, @@ -116,44 +116,44 @@ void fill_channel_desc(channel_desc_t *chan_desc, chan_desc->first_run = 1; chan_desc->ip = 0.0; chan_desc->max_Doppler = max_Doppler; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(nb_taps*sizeof(struct complexd *)); LOG_D(OCM,"[CHANNEL] Filling ch \n"); for (i = 0; ich[i] = (struct complex *) malloc(channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); // allocate for up to 100 RBs, 12 samples per RB + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); // allocate for up to 100 RBs, 12 samples per RB LOG_D(OCM,"[CHANNEL] Filling a (nb_taps %d)\n",nb_taps); for (i = 0; ia[i],nb_tx*nb_rx * sizeof(struct complex)); - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + LOG_D(OCM,"tap %d (%p,%zu)\n",i,&chan_desc->a[i],nb_tx*nb_rx * sizeof(struct complexd)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); } LOG_D(OCM,"[CHANNEL] Doing R_sqrt ...\n"); if (R_sqrt == NULL) { - chan_desc->R_sqrt = (struct complex **) calloc(nb_taps,sizeof(struct complex *)); + chan_desc->R_sqrt = (struct complexd **) calloc(nb_taps,sizeof(struct complexd *)); chan_desc->free_flags=chan_desc->free_flags|CHANMODEL_FREE_RSQRT_NTAPS ; for (i = 0; iR_sqrt[i] = (struct complex *) calloc(nb_tx*nb_rx*nb_tx*nb_rx,sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) calloc(nb_tx*nb_rx*nb_tx*nb_rx,sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } } } else { - chan_desc->R_sqrt = (struct complex **) calloc(nb_taps,sizeof(struct complex *)); + chan_desc->R_sqrt = (struct complexd **) calloc(nb_taps,sizeof(struct complexd *)); for (i = 0; iR_sqrt[i] = (struct complex*) calloc(nb_tx*nb_rx*nb_tx*nb_rx,sizeof(struct complex)); - //chan_desc->R_sqrt = (struct complex*)&R_sqrt[i][0]; + //chan_desc->R_sqrt[i] = (struct complexd*) calloc(nb_tx*nb_rx*nb_tx*nb_rx,sizeof(struct complexd)); + //chan_desc->R_sqrt = (struct complexd*)&R_sqrt[i][0]; /* all chan_desc share the same R_sqrt, coming from caller */ chan_desc->R_sqrt[i] = R_sqrt; } @@ -161,7 +161,7 @@ void fill_channel_desc(channel_desc_t *chan_desc, for (i = 0; iR_sqrt[i][j].x,chan_desc->R_sqrt[i][j].y); + LOG_D(OCM,"Rsqrt[%d][%d] %f %f\n",i,j,chan_desc->R_sqrt[i][j].r,chan_desc->R_sqrt[i][j].i); } } @@ -418,61 +418,61 @@ static double default_amps_lin[] = {0.3868472, 0.3094778, 0.1547389, 0.0773694, static double default_amp_lin[] = {1}; //correlation matrix for a 2x2 channel with full Tx correlation -static struct complex R_sqrt_22_corr[16] = {{0.70711,0}, {0.0, 0.0}, {0.70711,0}, {0.0, 0.0}, +static struct complexd R_sqrt_22_corr[16] = {{0.70711,0}, {0.0, 0.0}, {0.70711,0}, {0.0, 0.0}, {0.0, 0.0}, {0.70711,0}, {0.0, 0.0}, {0.70711,0}, {0.70711,0}, {0.0, 0.0}, {0.70711,0}, {0.0, 0.0}, {0.0, 0.0}, {0.70711,0}, {0.0, 0.0}, {0.70711,0} }; //correlation matrix for a fully correlated 2x1 channel (h1==h2) -static struct complex R_sqrt_21_corr[] = {{0.70711,0}, {0.70711,0}, {0.70711,0}, {0.70711,0}}; +static struct complexd R_sqrt_21_corr[] = {{0.70711,0}, {0.70711,0}, {0.70711,0}, {0.70711,0}}; //correlation matrix for a 2x2 channel with full Tx anti-correlation -static struct complex R_sqrt_22_anticorr[16] = {{0.70711,0}, {0.0, 0.0}, {-0.70711,0}, {0.0, 0.0}, +static struct complexd R_sqrt_22_anticorr[16] = {{0.70711,0}, {0.0, 0.0}, {-0.70711,0}, {0.0, 0.0}, {0.0, 0.0}, {0.70711,0}, {0.0, 0.0}, {-0.70711,0}, {-0.70711,0}, {0.0, 0.0}, {0.70711,0}, {0.0, 0.0}, {0.0, 0.0}, {-0.70711,0}, {0.0, 0.0}, {0.70711,0} }; //correlation matrix for a fully anti-correlated 2x1 channel (h1==-h2) -static struct complex R_sqrt_21_anticorr[4] = {{0.70711,0}, {-0.70711,0}, {-0.70711,0}, {0.70711,0}}; +static struct complexd R_sqrt_21_anticorr[4] = {{0.70711,0}, {-0.70711,0}, {-0.70711,0}, {0.70711,0}}; // full correlation matrix in vectorized form for 2x2 channel, where h1 is perfectly orthogonal to h2 -static struct complex R_sqrt_22_orthogonal[16] = {{0.70711,0.0}, {0.0, 0.0}, {0.0,0.0}, {0.0, 0.0}, +static struct complexd R_sqrt_22_orthogonal[16] = {{0.70711,0.0}, {0.0, 0.0}, {0.0,0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0,0.0}, {0.0, 0.0}, {0.0,0.0}, {0.0,0.0}, {0.0, 0.0}, {0.0,0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0,0.0}, {0.0, 0.0}, {0.70711,0.0} }; // full correlation matrix for TM4 to make orthogonal effective channel -static struct complex R_sqrt_22_orth_eff_ch_TM4_prec_real[16] = {{0.70711,0.0}, {0.0, 0.0}, {0.70711,0.0}, {0.0, 0.0}, +static struct complexd R_sqrt_22_orth_eff_ch_TM4_prec_real[16] = {{0.70711,0.0}, {0.0, 0.0}, {0.70711,0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.70711,0.0}, {0.0, 0.0}, {-0.70711,0.0}, {0.70711,0.0}, {0.0, 0.0}, {0.70711,0.0}, {0.0, 0.0}, {0.0, 0.0}, {-0.70711,0.0}, {0.0, 0.0}, {0.70711,0.0} }; -static struct complex R_sqrt_22_orth_eff_ch_TM4_prec_imag[16] = {{0.70711,0.0}, {0.0,0.0}, {0.0, -0.70711}, {0.0,0.0}, +static struct complexd R_sqrt_22_orth_eff_ch_TM4_prec_imag[16] = {{0.70711,0.0}, {0.0,0.0}, {0.0, -0.70711}, {0.0,0.0}, {0.0, 0.0}, {0.70711,0.0}, {0.0, 0.0}, {0.0,0.70711}, {0.0,-0.70711}, {0.0, 0.0}, {-0.70711,0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0,0.70711}, {0.0, 0.0}, {-0.70711,0.0} }; //Correlation matrix for EPA channel -static struct complex R_sqrt_22_EPA_low[16] = {{1.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, +static struct complexd R_sqrt_22_EPA_low[16] = {{1.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, {1.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, {1.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, {1.0,0.0} }; -static struct complex R_sqrt_22_EPA_high[16] = { +static struct complexd R_sqrt_22_EPA_high[16] = { {0.7179,0.0}, {0.4500,0.0}, {0.4500,0.0}, {0.2821,0.0}, {0.4500,0.0}, {0.7179,0.0}, {0.2821,0.0}, {0.4500,0.0}, {0.4500,0.0}, {0.2821,0.0}, {0.7179,0.0}, {0.4500,0.0}, {0.2821,0.0}, {0.4500,0.0}, {0.4500,0.0}, {0.7179,0.0} }; -static struct complex R_sqrt_22_EPA_medium[16] = {{0.8375,0.0}, {0.5249,0.0}, {0.1286,0.0}, {0.0806,0.0}, +static struct complexd R_sqrt_22_EPA_medium[16] = {{0.8375,0.0}, {0.5249,0.0}, {0.1286,0.0}, {0.0806,0.0}, {0.5249,0.0}, {0.8375,0.0}, {0.0806,0.0}, {0.1286,0.0}, {0.1286,0.0}, {0.0806,0.0}, {0.8375,0.0}, {0.5249,0.0}, {0.0806,0.0}, {0.1286,0.0}, {0.5249,0.0}, {0.8375,0.0} @@ -511,37 +511,37 @@ void tdlModel(int tdl_paths, double *tdl_delays, double *tdl_amps_dB, double DS chan_desc->delays = tdl_delays; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (int i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (int i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (int i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); if (nb_tx==2 && nb_rx==2) { for (int i = 0; i<(tdl_pathsby3); i++) - chan_desc->R_sqrt[i] = (struct complex *) &R22_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R22_sqrt[i][0]; } else if (nb_tx==2 && nb_rx==1) { for (int i = 0; i<(tdl_pathsby3); i++) - chan_desc->R_sqrt[i] = (struct complex *) &R21_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R21_sqrt[i][0]; } else if (nb_tx==1 && nb_rx==2) { for (int i = 0; i<(tdl_pathsby3); i++) - chan_desc->R_sqrt[i] = (struct complex *) &R12_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R12_sqrt[i][0]; } else { for (int i = 0; i<(tdl_pathsby3); i++) { - chan_desc->R_sqrt[i] = (struct complex *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (int j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } LOG_W(OCM,"correlation matrix not implemented for nb_tx==%d and nb_rx==%d, using identity\n", nb_tx, nb_rx); @@ -576,7 +576,7 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, double sum_amps; double aoa,ricean_factor,Td,maxDoppler; int channel_length,nb_taps; - struct complex *R_sqrt_ptr2; + struct complexd *R_sqrt_ptr2; chan_desc->modelid = channel_model; chan_desc->nb_tx = nb_tx; chan_desc->nb_rx = nb_rx; @@ -625,39 +625,39 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); if (nb_tx==2 && nb_rx==2) { for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R22_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R22_sqrt[i][0]; } else if (nb_tx==2 && nb_rx==1) { for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R21_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R21_sqrt[i][0]; } else if (nb_tx==1 && nb_rx==2) { for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R12_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R12_sqrt[i][0]; } else { chan_desc->free_flags=chan_desc->free_flags|CHANMODEL_FREE_RSQRT_6 ; for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } LOG_W(OCM,"correlation matrix not implemented for nb_tx==%d and nb_rx==%d, using identity\n", nb_tx, nb_rx); @@ -687,39 +687,39 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 0.1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); if (nb_tx==2 && nb_rx==2) { for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R22_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R22_sqrt[i][0]; } else if (nb_tx==2 && nb_rx==1) { for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R21_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R21_sqrt[i][0]; } else if (nb_tx==1 && nb_rx==2) { for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R12_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R12_sqrt[i][0]; } else { chan_desc->free_flags=chan_desc->free_flags|CHANMODEL_FREE_RSQRT_6 ; for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } LOG_W(OCM,"correlation matrix not implemented for nb_tx==%d and nb_rx==%d, using identity\n", nb_tx, nb_rx); @@ -784,34 +784,34 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); if (nb_tx==2 && nb_rx==2) { - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R22_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R22_sqrt[i][0]; } else { - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); chan_desc->free_flags=chan_desc->free_flags|CHANMODEL_FREE_RSQRT_6 ; for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } LOG_W(OCM,"correlation matrix only implemented for nb_tx==2 and nb_rx==2, using identity\n"); @@ -840,21 +840,21 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); if (nb_tx==2 && nb_rx==2) { - chan_desc->R_sqrt = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd **)); for (i = 0; inb_taps; i++) chan_desc->R_sqrt[i] = R_sqrt_22_EPA_low; @@ -863,9 +863,9 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, } /*else { - chan_desc->R_sqrt = (struct complex**) malloc(6*sizeof(struct complex**)); + chan_desc->R_sqrt = (struct complexd**) malloc(6*sizeof(struct complexd**)); for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex*) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd*) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; chan_desc->R_sqrt[i][j].y = 0.0; @@ -895,21 +895,21 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); if (nb_tx==2 && nb_rx==2) { - chan_desc->R_sqrt = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd **)); for (i = 0; inb_taps; i++) chan_desc->R_sqrt[i] = R_sqrt_22_EPA_high; @@ -918,9 +918,9 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, } /*else { - chan_desc->R_sqrt = (struct complex**) malloc(6*sizeof(struct complex**)); + chan_desc->R_sqrt = (struct complexd**) malloc(6*sizeof(struct complexd**)); for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex*) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd*) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; chan_desc->R_sqrt[i][j].y = 0.0; @@ -950,21 +950,21 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); if (nb_tx==2 && nb_rx==2) { - chan_desc->R_sqrt = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd **)); for (i = 0; inb_taps; i++) chan_desc->R_sqrt[i] = R_sqrt_22_EPA_medium; @@ -973,9 +973,9 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, } /*else { - chan_desc->R_sqrt = (struct complex**) malloc(6*sizeof(struct complex**)); + chan_desc->R_sqrt = (struct complexd**) malloc(6*sizeof(struct complexd**)); for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex*) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd*) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; chan_desc->R_sqrt[i][j].y = 0.0; @@ -1005,34 +1005,34 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); if (nb_tx==2 && nb_rx==2) { - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R22_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R22_sqrt[i][0]; } else { - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); chan_desc->free_flags=chan_desc->free_flags|CHANMODEL_FREE_RSQRT_6 ; for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } LOG_W(OCM,"correlation matrix only implemented for nb_tx==2 and nb_rx==2, using identity\n"); @@ -1061,34 +1061,34 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); if (nb_tx==2 && nb_rx==2) { - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); for (i = 0; i<6; i++) - chan_desc->R_sqrt[i] = (struct complex *) &R22_sqrt[i][0]; + chan_desc->R_sqrt[i] = (struct complexd *) &R22_sqrt[i][0]; } else { - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex **)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd **)); chan_desc->free_flags=chan_desc->free_flags|CHANMODEL_FREE_RSQRT_6 ; for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } LOG_W(OCM,"correlation matrix only implemented for nb_tx==2 and nb_rx==2, using identity\n"); @@ -1117,28 +1117,28 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, chan_desc->ricean_factor = 1; chan_desc->aoa = 0; chan_desc->random_aoa = 0; - chan_desc->ch = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->chF = (struct complex **) malloc(nb_tx*nb_rx*sizeof(struct complex *)); - chan_desc->a = (struct complex **) malloc(chan_desc->nb_taps*sizeof(struct complex *)); + chan_desc->ch = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->chF = (struct complexd **) malloc(nb_tx*nb_rx*sizeof(struct complexd *)); + chan_desc->a = (struct complexd **) malloc(chan_desc->nb_taps*sizeof(struct complexd *)); for (i = 0; ich[i] = (struct complex *) malloc(chan_desc->channel_length * sizeof(struct complex)); + chan_desc->ch[i] = (struct complexd *) malloc(chan_desc->channel_length * sizeof(struct complexd)); for (i = 0; ichF[i] = (struct complex *) malloc(1200 * sizeof(struct complex)); + chan_desc->chF[i] = (struct complexd *) malloc(1200 * sizeof(struct complexd)); for (i = 0; inb_taps; i++) - chan_desc->a[i] = (struct complex *) malloc(nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->a[i] = (struct complexd *) malloc(nb_tx*nb_rx * sizeof(struct complexd)); - chan_desc->R_sqrt = (struct complex **) malloc(6*sizeof(struct complex *)); + chan_desc->R_sqrt = (struct complexd **) malloc(6*sizeof(struct complexd *)); chan_desc->free_flags=chan_desc->free_flags|CHANMODEL_FREE_RSQRT_6; for (i = 0; i<6; i++) { - chan_desc->R_sqrt[i] = (struct complex *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complex)); + chan_desc->R_sqrt[i] = (struct complexd *) malloc(nb_tx*nb_rx*nb_tx*nb_rx * sizeof(struct complexd)); for (j = 0; jR_sqrt[i][j].x = 1.0; - chan_desc->R_sqrt[i][j].y = 0.0; + chan_desc->R_sqrt[i][j].r = 1.0; + chan_desc->R_sqrt[i][j].i = 0.0; } LOG_W(OCM,"correlation matrix only implemented for nb_tx==2 and nb_rx==2, using identity\n"); @@ -1708,8 +1708,8 @@ void set_channeldesc_name(channel_desc_t *cdesc,char *modelname) { int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { double s; int i,k,l,aarx,aatx; - struct complex anew[NB_ANTENNAS_TX*NB_ANTENNAS_RX],acorr[NB_ANTENNAS_TX*NB_ANTENNAS_RX]; - struct complex phase, alpha, beta; + struct complexd anew[NB_ANTENNAS_TX*NB_ANTENNAS_RX],acorr[NB_ANTENNAS_TX*NB_ANTENNAS_RX]; + struct complexd phase, alpha, beta; AssertFatal(desc->nb_tx<=NB_ANTENNAS_TX && desc->nb_rx <= NB_ANTENNAS_RX, "random_channel.c: Error: temporary buffer for channel not big enough (%d,%d)\n",desc->nb_tx,desc->nb_rx); start_meas(&desc->random_channel); @@ -1717,8 +1717,8 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { for (i=0; i<(int)desc->nb_taps; i++) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { - anew[aarx+(aatx*desc->nb_rx)].x = sqrt(desc->ricean_factor*desc->amps[i]/2) * gaussdouble(0.0,1.0); - anew[aarx+(aatx*desc->nb_rx)].y = sqrt(desc->ricean_factor*desc->amps[i]/2) * gaussdouble(0.0,1.0); + anew[aarx+(aatx*desc->nb_rx)].r = sqrt(desc->ricean_factor*desc->amps[i]/2) * gaussdouble(0.0,1.0); + anew[aarx+(aatx*desc->nb_rx)].i = sqrt(desc->ricean_factor*desc->amps[i]/2) * gaussdouble(0.0,1.0); if ((i==0) && (desc->ricean_factor != 1.0)) { if (desc->random_aoa==1) { @@ -1728,10 +1728,10 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { // this assumes that both RX and TX have linear antenna arrays with lambda/2 antenna spacing. // Furhter it is assumed that the arrays are parallel to each other and that they are far enough apart so // that we can safely assume plane wave propagation. - phase.x = cos(M_PI*((aarx-aatx)*sin(desc->aoa))); - phase.y = sin(M_PI*((aarx-aatx)*sin(desc->aoa))); - anew[aarx+(aatx*desc->nb_rx)].x += phase.x * sqrt(1.0-desc->ricean_factor); - anew[aarx+(aatx*desc->nb_rx)].y += phase.y * sqrt(1.0-desc->ricean_factor); + phase.r = cos(M_PI*((aarx-aatx)*sin(desc->aoa))); + phase.i = sin(M_PI*((aarx-aatx)*sin(desc->aoa))); + anew[aarx+(aatx*desc->nb_rx)].r += phase.r * sqrt(1.0-desc->ricean_factor); + anew[aarx+(aatx*desc->nb_rx)].i += phase.i * sqrt(1.0-desc->ricean_factor); } #ifdef DEBUG_CH @@ -1752,10 +1752,10 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { */ //apply correlation matrix //compute acorr = R_sqrt[i] * anew - alpha.x = 1.0; - alpha.y = 0.0; - beta.x = 0.0; - beta.y = 0.0; + alpha.r = 1.0; + alpha.i = 0.0; + beta.r = 0.0; + beta.i = 0.0; cblas_zgemv(CblasRowMajor, CblasNoTrans, desc->nb_tx*desc->nb_rx, desc->nb_tx*desc->nb_rx, (void *) &alpha, (void *) desc->R_sqrt[i/3], desc->nb_rx*desc->nb_tx, (void *) anew, 1, (void *) &beta, (void *) acorr, 1); @@ -1776,10 +1776,10 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { // a = alpha*acorr+beta*a // a = beta*a // a = a+alpha*acorr - alpha.x = sqrt(1-desc->forgetting_factor); - alpha.y = 0; - beta.x = sqrt(desc->forgetting_factor); - beta.y = 0; + alpha.r = sqrt(1-desc->forgetting_factor); + alpha.i = 0; + beta.r = sqrt(desc->forgetting_factor); + beta.i = 0; cblas_zscal(desc->nb_tx*desc->nb_rx, (void *) &beta, (void *) desc->a[i], 1); cblas_zaxpy(desc->nb_tx*desc->nb_rx, (void *) &alpha, (void *) acorr, 1, (void *) desc->a[i], 1); // desc->a[i][aarx+(aatx*desc->nb_rx)].x = (sqrt(desc->forgetting_factor)*desc->a[i][aarx+(aatx*desc->nb_rx)].x) + sqrt(1-desc->forgetting_factor)*anew.x; @@ -1799,7 +1799,7 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { stop_meas(&desc->random_channel); - //memset((void *)desc->ch[aarx+(aatx*desc->nb_rx)],0,(int)(desc->channel_length)*sizeof(struct complex)); + //memset((void *)desc->ch[aarx+(aatx*desc->nb_rx)],0,(int)(desc->channel_length)*sizeof(struct complexd)); if (abstraction_flag==0) { start_meas(&desc->interp_time); @@ -1807,12 +1807,12 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx=0; aatxnb_tx; aatx++) { if (desc->channel_length == 1) { - desc->ch[aarx+(aatx*desc->nb_rx)][0].x = desc->a[0][aarx+(aatx*desc->nb_rx)].x; - desc->ch[aarx+(aatx*desc->nb_rx)][0].y = desc->a[0][aarx+(aatx*desc->nb_rx)].y; + desc->ch[aarx+(aatx*desc->nb_rx)][0].r = desc->a[0][aarx+(aatx*desc->nb_rx)].r; + desc->ch[aarx+(aatx*desc->nb_rx)][0].i = desc->a[0][aarx+(aatx*desc->nb_rx)].i; } else { for (k=0; k<(int)desc->channel_length; k++) { - desc->ch[aarx+(aatx*desc->nb_rx)][k].x = 0.0; - desc->ch[aarx+(aatx*desc->nb_rx)][k].y = 0.0; + desc->ch[aarx+(aatx*desc->nb_rx)][k].r = 0.0; + desc->ch[aarx+(aatx*desc->nb_rx)][k].i = 0.0; for (l=0; lnb_taps; l++) { if ((k - (desc->delays[l]*desc->sampling_rate) - desc->channel_offset) == 0) @@ -1821,8 +1821,8 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { s = sin(M_PI*(k - (desc->delays[l]*desc->sampling_rate) - desc->channel_offset))/ (M_PI*(k - (desc->delays[l]*desc->sampling_rate) - desc->channel_offset)); - desc->ch[aarx+(aatx*desc->nb_rx)][k].x += s*desc->a[l][aarx+(aatx*desc->nb_rx)].x; - desc->ch[aarx+(aatx*desc->nb_rx)][k].y += s*desc->a[l][aarx+(aatx*desc->nb_rx)].y; + desc->ch[aarx+(aatx*desc->nb_rx)][k].r += s*desc->a[l][aarx+(aatx*desc->nb_rx)].r; + desc->ch[aarx+(aatx*desc->nb_rx)][k].i += s*desc->a[l][aarx+(aatx*desc->nb_rx)].i; // printf("l %d : desc->ch.x %f, s %e, delay %f\n",l,desc->a[l][aarx+(aatx*desc->nb_rx)].x,s,desc->delays[l]); } //nb_taps @@ -2102,7 +2102,7 @@ int load_channellist(uint8_t nb_tx, uint8_t nb_rx, double sampling_rate, double #define Td 2.0 main(int argc,char **argv) { double amps[8] = {.8,.2,.1,.04,.02,.01,.005}; - struct complex ch[(int)(1+2*sampling_rate*Td)],phase; + struct complexd ch[(int)(1+2*sampling_rate*Td)],phase; int i; randominit(); phase.x = 1.0; diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index 78e260b73f..4b3ac5f862 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -64,11 +64,11 @@ typedef struct { ///length of impulse response. should be set to 11+2*bw*t_max uint8_t channel_length; ///channel state vector. size(state) = nb_taps * (n_tx * n_rx); - struct complex **a; + struct complexd **a; ///interpolated (sample-spaced) channel impulse response. size(ch) = (n_tx * n_rx) * channel_length. ATTENTION: the dimensions of ch are the transposed ones of a. This is to allow the use of BLAS when applying the correlation matrices to the state. - struct complex **ch; + struct complexd **ch; ///Sampled frequency response (90 kHz resolution) - struct complex **chF; + struct complexd **chF; ///Maximum path delay in mus. double Td; ///Channel bandwidth in MHz. @@ -84,7 +84,7 @@ typedef struct { ///in Hz. if >0 generate a channel with a Clarke's Doppler profile with a maximum Doppler bandwidth max_Doppler. CURRENTLY NOT IMPLEMENTED! double max_Doppler; ///Square root of the full correlation matrix size(R_tx) = nb_taps * (n_tx * n_rx) * (n_tx * n_rx). - struct complex **R_sqrt; + struct complexd **R_sqrt; ///path loss including shadow fading in dB double path_loss_dB; ///additional delay of channel in samples. diff --git a/openair2/GNB_APP/L1_nr_paramdef.h b/openair2/GNB_APP/L1_nr_paramdef.h index fc2bca19d5..0306713845 100644 --- a/openair2/GNB_APP/L1_nr_paramdef.h +++ b/openair2/GNB_APP/L1_nr_paramdef.h @@ -47,6 +47,7 @@ #define CONFIG_STRING_L1_REMOTE_N_PORTD "remote_n_portd" #define CONFIG_STRING_L1_TRANSPORT_N_PREFERENCE "tr_n_preference" #define CONFIG_STRING_L1_PUSCH_PROC_THREADS "pusch_proc_threads" +#define CONFIG_STRING_L1_OFDM_OFFSET_DIVISOR "ofdm_offset_divisor" #define CONFIG_STRING_L1_PUCCH0_DTX_THRESHOLD "pucch0_dtx_threshold" #define CONFIG_STRING_L1_PRACH_DTX_THRESHOLD "prach_dtx_threshold" #define CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD "pusch_dtx_threshold" @@ -64,7 +65,8 @@ {CONFIG_STRING_L1_REMOTE_N_PORTC, NULL, 0, uptr:NULL, defintval:50030, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_LOCAL_N_PORTD, NULL, 0, uptr:NULL, defintval:50031, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_REMOTE_N_PORTD, NULL, 0, uptr:NULL, defintval:50031, TYPE_UINT, 0}, \ -{CONFIG_STRING_L1_PUSCH_PROC_THREADS, NULL, 0, uptr:NULL, defintval:1, TYPE_UINT, 0}, \ +{CONFIG_STRING_L1_PUSCH_PROC_THREADS, NULL, 0, uptr:NULL, defintval:3, TYPE_UINT, 0}, \ +{CONFIG_STRING_L1_OFDM_OFFSET_DIVISOR, NULL, 0, uptr:NULL, defuintval:8, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PUCCH0_DTX_THRESHOLD, NULL, 0, uptr:NULL, defintval:100, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PRACH_DTX_THRESHOLD, NULL, 0, uptr:NULL, defintval:150, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD, NULL, 0, uptr:NULL, defintval:50, TYPE_UINT, 0} \ @@ -79,9 +81,10 @@ #define L1_LOCAL_N_PORTD_IDX 7 #define L1_REMOTE_N_PORTD_IDX 8 #define L1_PUSCH_PROC_THREADS 9 -#define L1_PUCCH0_DTX_THRESHOLD 10 -#define L1_PRACH_DTX_THRESHOLD 11 -#define L1_PUSCH_DTX_THRESHOLD 12 +#define L1_OFDM_OFFSET_DIVISOR 10 +#define L1_PUCCH0_DTX_THRESHOLD 11 +#define L1_PRACH_DTX_THRESHOLD 12 +#define L1_PUSCH_DTX_THRESHOLD 13 /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ #endif diff --git a/openair2/GNB_APP/gnb_app.c b/openair2/GNB_APP/gnb_app.c index b1dc59f2e2..5104021399 100644 --- a/openair2/GNB_APP/gnb_app.c +++ b/openair2/GNB_APP/gnb_app.c @@ -219,29 +219,30 @@ void *gNB_app_task(void *args_p) //registered_gnb = 0; __attribute__((unused)) uint32_t register_gnb_pending = gNB_app_register (gnb_id_start, gnb_id_end); } + + if (RC.nb_nr_inst > 0) { + if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { - if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { - - if (itti_create_task(TASK_CU_F1, F1AP_CU_task, NULL) < 0) { - LOG_E(F1AP, "Create task for F1AP CU failed\n"); - AssertFatal(1==0,"exiting"); - } - } - - if (NODE_IS_DU(RC.nrrrc[0]->node_type)) { - - if (itti_create_task(TASK_DU_F1, F1AP_DU_task, NULL) < 0) { - LOG_E(F1AP, "Create task for F1AP DU failed\n"); - AssertFatal(1==0,"exiting"); + if (itti_create_task(TASK_CU_F1, F1AP_CU_task, NULL) < 0) { + LOG_E(F1AP, "Create task for F1AP CU failed\n"); + AssertFatal(1==0,"exiting"); + } } - // configure F1AP here for F1C - LOG_I(GNB_APP,"ngran_gNB_DU: Allocating ITTI message for F1AP_SETUP_REQ\n"); - msg_p = itti_alloc_new_message (TASK_GNB_APP, 0, F1AP_SETUP_REQ); - RCconfig_NR_DU_F1(msg_p, 0); - - itti_send_msg_to_task (TASK_DU_F1, GNB_MODULE_ID_TO_INSTANCE(0), msg_p); - } + if (NODE_IS_DU(RC.nrrrc[0]->node_type)) { + + if (itti_create_task(TASK_DU_F1, F1AP_DU_task, NULL) < 0) { + LOG_E(F1AP, "Create task for F1AP DU failed\n"); + AssertFatal(1==0,"exiting"); + } + // configure F1AP here for F1C + LOG_I(GNB_APP,"ngran_gNB_DU: Allocating ITTI message for F1AP_SETUP_REQ\n"); + msg_p = itti_alloc_new_message (TASK_GNB_APP, 0, F1AP_SETUP_REQ); + RCconfig_NR_DU_F1(msg_p, 0); + + itti_send_msg_to_task (TASK_DU_F1, GNB_MODULE_ID_TO_INSTANCE(0), msg_p); + } + } do { // Wait for a message itti_receive_msg (TASK_GNB_APP, &msg_p); diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 9bc86d94ea..1a59f333f3 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -590,6 +590,7 @@ void RCconfig_NR_L1(void) { } RC.gNB[j]->pusch_proc_threads = *(L1_ParamList.paramarray[j][L1_PUSCH_PROC_THREADS].uptr); + RC.gNB[j]->ofdm_offset_divisor = *(L1_ParamList.paramarray[j][L1_OFDM_OFFSET_DIVISOR].uptr); RC.gNB[j]->pucch0_thres = *(L1_ParamList.paramarray[j][L1_PUCCH0_DTX_THRESHOLD].uptr); RC.gNB[j]->prach_thres = *(L1_ParamList.paramarray[j][L1_PRACH_DTX_THRESHOLD].uptr); RC.gNB[j]->pusch_thres = *(L1_ParamList.paramarray[j][L1_PUSCH_DTX_THRESHOLD].uptr); @@ -1406,7 +1407,6 @@ void NRRCConfig(void) { config_get( GNBSParams,sizeof(GNBSParams)/sizeof(paramdef_t),NULL); RC.nb_nr_inst = GNBSParams[GNB_ACTIVE_GNBS_IDX].numelt; - // Get num MACRLC instances config_getlist( &MACRLCParamList,NULL,0, NULL); diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index d1d60d236c..b73da4c4e7 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -292,8 +292,8 @@ typedef struct { /// Random-access Contention Resolution Timer active flag uint8_t RA_contention_resolution_timer_active; - /// Random-access Contention Resolution Timer count value - uint8_t RA_contention_resolution_cnt; + int RA_contention_resolution_target_frame; + int RA_contention_resolution_target_slot; /// Transmitted UE Contention Resolution Identifier uint8_t cont_res_id[6]; @@ -325,6 +325,15 @@ typedef struct { } RAR_grant_t; +typedef struct { + + uint8_t phr_reporting; + uint16_t truncated_bsr; + uint16_t short_bsr; + uint16_t long_bsr; + +} NR_UE_MAC_CE_t; + typedef struct { int n_HARQ_ACK; uint32_t ack_payload; @@ -340,7 +349,6 @@ typedef struct { int8_t delta_pucch; } PUCCH_sched_t; - /*!\brief Top level UE MAC structure */ typedef struct { @@ -407,12 +415,8 @@ typedef struct { nr_ue_if_module_t *if_module; nr_phy_config_t phy_config; - /// BSR report flag management - uint8_t BSR_reporting_active; NR_UE_SCHEDULING_INFO scheduling_info; - - /// PHR - uint8_t PHR_reporting_active; + NR_UE_MAC_CE_t nr_ue_mac_ce; NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config; NR_SearchSpace_t *search_space_zero; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index dac62cf9a6..41480d55a4 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -169,18 +169,8 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *ul_time_alignment, int pdu_id); -uint16_t nr_generate_ulsch_pdu(uint8_t *sdus_payload, - uint8_t *pdu, - uint8_t num_sdus, - uint16_t *sdu_lengths, - uint8_t *sdu_lcids, - uint8_t power_headroom, - uint16_t crnti, - uint16_t truncated_bsr, - uint16_t short_bsr, - uint16_t long_bsr, - unsigned short post_padding, - uint16_t buflen); +int nr_write_ce_ulsch_pdu(uint8_t *mac_ce, + NR_UE_MAC_INST_t *mac); void fill_dci_search_candidates(NR_SearchSpace_t *ss,fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15); @@ -201,10 +191,12 @@ int8_t nr_ue_process_dci_time_dom_resource_assignment(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind, bool use_default); -uint8_t -nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, - sub_frame_t subframe, uint8_t eNB_index, - uint8_t *ulsch_buffer, uint16_t buflen, uint8_t *access_mode) ; +uint8_t nr_ue_get_sdu(module_id_t module_idP, + frame_t frameP, + sub_frame_t subframe, + uint8_t gNB_index, + uint8_t *ulsch_buffer, + uint16_t buflen); int set_tdd_config_nr_ue(fapi_nr_config_request_t *cfg, int mu, int nrofDownlinkSlots, int nrofDownlinkSymbols, @@ -360,7 +352,7 @@ void nr_get_prach_resources(module_id_t mod_id, void nr_Msg1_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint8_t gNB_id); -void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint8_t gNB_id); +void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, slot_t slotP, uint8_t gNB_id); void nr_ue_msg2_scheduler(module_id_t mod_id, uint16_t rach_frame, uint16_t rach_slot, uint16_t *msg2_frame, uint16_t *msg2_slot); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index 5d9126ad69..452cc982ff 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -157,7 +157,7 @@ void init_RA(module_id_t mod_id, } if (nr_rach_ConfigCommon->ext1) { - if (nr_rach_ConfigCommon->ext1->ra_PrioritizationForAccessIdentity){ + if (nr_rach_ConfigCommon->ext1->ra_PrioritizationForAccessIdentity_r16){ LOG_D(MAC, "In %s:%d: Missing implementation for Access Identity initialization procedures\n", __FUNCTION__, __LINE__); } } @@ -457,40 +457,45 @@ void nr_Msg1_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint ra->RA_attempt_number++; } -void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint8_t gNB_id){ +void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, slot_t slotP, uint8_t gNB_id){ NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); - NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = (mac->scc) ? + RA_config_t *ra = &mac->ra; + NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = (mac->scc) ? mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup: mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; - RA_config_t *ra = &mac->ra; + long mu = (mac->scc) ? + mac->scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing : + mac->scc_SIB->downlinkConfigCommon.frequencyInfoDL.scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + int subframes_per_slot = nr_slots_per_frame[mu]/10; - LOG_D(MAC,"In %s: [UE %d] Frame %d, CB-RA: starting contention resolution timer\n", __FUNCTION__, mod_id, frameP); + // start contention resolution timer (cnt in slots) + int RA_contention_resolution_timer_subframes = (nr_rach_ConfigCommon->ra_ContentionResolutionTimer + 1)<<3; + + ra->RA_contention_resolution_target_frame = frameP + (RA_contention_resolution_timer_subframes/10); + ra->RA_contention_resolution_target_slot = (slotP + (RA_contention_resolution_timer_subframes * subframes_per_slot)) % nr_slots_per_frame[mu]; + + LOG_D(MAC,"In %s: [UE %d] CB-RA: contention resolution timer set in frame.slot %d.%d and expiring in %d.%d\n", + __FUNCTION__, mod_id, frameP, slotP, ra->RA_contention_resolution_target_frame, ra->RA_contention_resolution_target_slot); - // start contention resolution timer - ra->RA_contention_resolution_cnt = (nr_rach_ConfigCommon->ra_ContentionResolutionTimer + 1) * 8; ra->RA_contention_resolution_timer_active = 1; ra->ra_state = WAIT_CONTENTION_RESOLUTION; } -///////////////////////////////////////////////////////////////////////// -// This function handles: -// - Random Access Preamble Initialization (5.1.1 TS 38.321) -// - Random Access Response reception (5.1.4 TS 38.321) -/// In the current implementation, RA is 4-step contention free only -///////////////////////////////////////////////////////////////////////// -// todo TS 38.321: -// - BWP operation (subclause 5.15 TS 38.321) -// - beam failure recovery -// - handle initialization by handover -// - handle DL assignment on PDCCH for RA-RNTI -// - transmission on DCCH using PRACH (during handover, or sending SR for example) -// - take into account MAC CEs in size_sdu (currently hardcoded size to 1 MAC subPDU and 1 padding subheader) -// - fix rrc data req logic -// - retrieve TBS -// - add mac_rrc_nr_data_req_ue, etc ... -// - Msg3 Retransmissions to be scheduled by DCI 0_0 +/** + * Function: handles Random Access Preamble Initialization (5.1.1 TS 38.321) + * handles Random Access Response reception (5.1.4 TS 38.321) + * Note: In SA mode the Msg3 contains a CCCH SDU, therefore no C-RNTI MAC CE is transmitted. + * + * @prach_resources pointer to PRACH resources + * @prach_pdu pointer to FAPI UL PRACH PDU + * @mod_id module ID + * @CC_id CC ID + * @frame current UL TX frame + * @gNB_id gNB ID + * @nr_slot_tx current UL TX slot + */ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, fapi_nr_ul_config_prach_pdu *prach_pdu, module_id_t mod_id, @@ -501,11 +506,6 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); RA_config_t *ra = &mac->ra; - uint8_t mac_sdus[MAX_NR_ULSCH_PAYLOAD_BYTES]; - uint8_t lcid = UL_SCH_LCID_CCCH; - uint8_t *payload; - uint16_t size_sdu = 0; - unsigned short post_padding; NR_RACH_ConfigCommon_t *setup; if (mac->scc) setup = mac->scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; else setup = mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.rach_ConfigCommon->choice.setup; @@ -513,11 +513,6 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric; NR_RACH_ConfigDedicated_t *rach_ConfigDedicated = ra->rach_ConfigDedicated; - uint8_t sdu_lcids[NB_RB_MAX] = {0}; - uint16_t sdu_lengths[NB_RB_MAX] = {0}; - int num_sdus = 0; - int offset = 0; - // Delay init RA procedure to allow the convergence of the IIR filter on PRACH noise measurements at gNB side if (!prach_resources->init_msg1) { if ( (mac->common_configuration_complete>0 || get_softmodem_params()->do_ra==1) && ((MAX_FRAME_NUMBER+frame-prach_resources->sync_frame)%MAX_FRAME_NUMBER)>150 ){ @@ -528,90 +523,107 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, } } - LOG_D(NR_MAC,"frame %d prach_resources->init_msg1 %d, ra->ra_state %d, ra->RA_active %d\n", - frame,prach_resources->init_msg1,ra->ra_state,ra->RA_active); + LOG_D(NR_MAC, "In %s: [UE %d][%d.%d]: init_msg1 %d, ra_state %d, RA_active %d\n", + __FUNCTION__, + mod_id, + frame, + nr_slot_tx, + prach_resources->init_msg1, + ra->ra_state, + ra->RA_active); if (prach_resources->init_msg1 && ra->ra_state != RA_SUCCEEDED) { if (ra->RA_active == 0) { /* RA not active - checking if RRC is ready to initiate the RA procedure */ - LOG_D(NR_MAC, "RA not active. Checking for data to transmit from upper layers...\n"); + LOG_D(NR_MAC, "In %s: RA not active. Checking for data to transmit from upper layers...\n", __FUNCTION__); - uint8_t TBS_max = 8 + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); - payload = (uint8_t*) mac->CCCH_pdu.payload; + const uint8_t lcid = UL_SCH_LCID_CCCH; + const uint8_t sh_size = sizeof(NR_MAC_SUBHEADER_FIXED); + const uint8_t TBS_max = 8 + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); // Note: unclear the reason behind the selection of such TBS_max + int8_t size_sdu = 0; + uint8_t mac_ce[16] = {0}; + uint8_t *pdu = get_softmodem_params()->sa ? mac->CCCH_pdu.payload : mac_ce; + uint8_t *payload = pdu; - num_sdus = 1; - post_padding = 1; - sdu_lcids[0] = lcid; + // Concerning the C-RNTI MAC CE, it has to be included if the UL transmission (Msg3) is not being made for the CCCH logical channel. + // Therefore it has been assumed that this event only occurs only when RA is done and it is not SA mode. + if (get_softmodem_params()->sa) { - // initialisation by RRC + NR_MAC_SUBHEADER_FIXED *header = (NR_MAC_SUBHEADER_FIXED *) pdu; + pdu += sh_size; - // TODO: To be removed after RA procedures fully implemented - if(get_softmodem_params()->do_ra) { + // initialisation by RRC nr_rrc_ue_generate_RRCSetupRequest(mod_id,gNB_id); + + // CCCH PDU + size_sdu = nr_mac_rrc_data_req_ue(mod_id, CC_id, gNB_id, frame, CCCH, pdu); + LOG_D(NR_MAC, "In %s: [UE %d][%d.%d]: Requested RRCConnectionRequest, got %d bytes for LCID 0x%02x \n", __FUNCTION__, mod_id, frame, nr_slot_tx, size_sdu, lcid); + + if (size_sdu > 0) { + + // UE Contention Resolution Identity + // Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to determine whether or not the + // Random Access Procedure has been successful after reception of Msg4 + memcpy(ra->cont_res_id, pdu, sizeof(uint8_t) * 6); + + pdu += size_sdu; + ra->Msg3_size = size_sdu + sh_size; + + // Build header + header->R = 0; + header->LCID = lcid; + + } else { + pdu -= sh_size; + } + + } else { + + size_sdu = nr_write_ce_ulsch_pdu(pdu, mac); + pdu += size_sdu; + ra->Msg3_size = size_sdu; + } - // CCCH PDU - size_sdu = (uint16_t) nr_mac_rrc_data_req_ue(mod_id, CC_id, gNB_id, frame, CCCH, mac_sdus); + if (size_sdu > 0 && ra->generate_nr_prach == GENERATE_PREAMBLE) { - sdu_lengths[0] = size_sdu; - - LOG_D(NR_MAC,"[UE %d] Frame %d: Requested RRCConnectionRequest, got %d bytes\n", mod_id, frame, size_sdu); - - if (size_sdu > 0) { - - // UE Contention Resolution Identity - // Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to determine whether or not the - // Random Access Procedure has been successful after reception of Msg4 - memcpy(ra->cont_res_id, mac_sdus, sizeof(uint8_t) * 6); - - LOG_D(NR_MAC, "[UE %d][%d.%d]: starting initialisation Random Access Procedure...\n", mod_id, frame, nr_slot_tx); - - ra->Msg3_size = size_sdu + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); + LOG_D(NR_MAC, "In %s: [UE %d][%d.%d]: starting initialisation Random Access Procedure...\n", __FUNCTION__, mod_id, frame, nr_slot_tx); + AssertFatal(TBS_max > ra->Msg3_size, "In %s: allocated resources are not enough for Msg3!\n", __FUNCTION__); + // Init RA procedure init_RA(mod_id, prach_resources, setup, rach_ConfigGeneric, rach_ConfigDedicated); - prach_resources->Msg3 = payload; nr_get_RA_window(mac); - // Fill in preamble and PRACH resources - if (ra->generate_nr_prach == GENERATE_PREAMBLE) { - nr_get_prach_resources(mod_id, CC_id, gNB_id, prach_resources, prach_pdu, rach_ConfigDedicated); - } - offset = nr_generate_ulsch_pdu((uint8_t *) mac_sdus, // sdus buffer - (uint8_t *) payload, // UL MAC pdu pointer - num_sdus, // num sdus - sdu_lengths, // sdu length - sdu_lcids, // sdu lcid - 0, // power headroom - 0, // crnti - 0, // truncated bsr - 0, // short bsr - 0, // long_bsr - post_padding, - 0); - - AssertFatal(TBS_max > offset, "Frequency resources are not enough for Msg3!\n"); + nr_get_prach_resources(mod_id, CC_id, gNB_id, prach_resources, prach_pdu, rach_ConfigDedicated); // Padding: fill remainder with 0 - if (post_padding > 0){ - for (int j = 0; j < (TBS_max - offset); j++) - payload[offset + j] = 0; + if (TBS_max - ra->Msg3_size > 0) { + LOG_D(NR_MAC, "In %s: remaining %d bytes, filling with padding\n", __FUNCTION__, TBS_max - ra->Msg3_size); + ((NR_MAC_SUBHEADER_FIXED *) pdu)->R = 0; + ((NR_MAC_SUBHEADER_FIXED *) pdu)->LCID = UL_SCH_LCID_PADDING; + pdu += sizeof(NR_MAC_SUBHEADER_FIXED); + for (int j = 0; j < TBS_max - ra->Msg3_size - sizeof(NR_MAC_SUBHEADER_FIXED); j++) { + pdu[j] = 0; + } } + + // Dumping ULSCH payload + LOG_D(NR_MAC, "In %s: dumping UL Msg3 MAC PDU with length %d: \n", __FUNCTION__, TBS_max); + for(int k = 0; k < TBS_max; k++) { + LOG_D(NR_MAC,"(%i): %i\n", k, payload[k]); + } + + // Msg3 was initialized with TBS_max bytes because the RA_Msg3_size will only be known after + // receiving Msg2 (which contains the Msg3 resource reserve). + // Msg3 will be transmitted with RA_Msg3_size bytes, removing unnecessary 0s. + mac->ulsch_pdu.Pdu_size = TBS_max; + memcpy(mac->ulsch_pdu.payload, payload, TBS_max); + + } else { + return 0; } - - LOG_D(MAC,"size_sdu = %i\n", size_sdu); - LOG_D(MAC,"offset = %i\n", offset); - for(int k = 0; k < TBS_max; k++) { - LOG_D(MAC,"(%i): %i\n", k, prach_resources->Msg3[k]); - } - - // Msg3 was initialized with TBS_max bytes because the RA_Msg3_size will only be known after - // receiving Msg2 (which contains the Msg3 resource reserve). - // Msg3 will be transmitted with RA_Msg3_size bytes, removing unnecessary 0s. - mac->ulsch_pdu.Pdu_size = TBS_max; - memcpy(mac->ulsch_pdu.payload, prach_resources->Msg3, TBS_max); - } else if (ra->RA_window_cnt != -1) { // RACH is active LOG_D(MAC, "In %s [%d.%d] RA is active: RA window count %d, RA backoff count %d\n", __FUNCTION__, frame, nr_slot_tx, ra->RA_window_cnt, ra->RA_backoff_cnt); @@ -736,20 +748,15 @@ void nr_ue_contention_resolution(module_id_t module_id, int cc_id, frame_t frame RA_config_t *ra = &mac->ra; if (ra->RA_contention_resolution_timer_active == 1) { - - ra->RA_contention_resolution_cnt--; - - LOG_D(MAC, "In %s: [%d.%d] RA contention resolution timer %d\n", __FUNCTION__, frame, slot, ra->RA_contention_resolution_cnt); - - if (ra->RA_contention_resolution_cnt == 0) { - ra->t_crnti = 0; - ra->RA_active = 0; - ra->RA_contention_resolution_timer_active = 0; - // Signal PHY to quit RA procedure - LOG_E(MAC, "[UE %d] CB-RA: Contention resolution timer has expired, RA procedure has failed...\n", module_id); - nr_ra_failed(module_id, cc_id, prach_resources, frame, slot); - } - + if (frame >= ra->RA_contention_resolution_target_frame && + slot >= ra->RA_contention_resolution_target_slot) { + ra->t_crnti = 0; + ra->RA_active = 0; + ra->RA_contention_resolution_timer_active = 0; + // Signal PHY to quit RA procedure + LOG_E(MAC, "[UE %d] CB-RA: Contention resolution timer has expired, RA procedure has failed...\n", module_id); + nr_ra_failed(module_id, cc_id, prach_resources, frame, slot); + } } } @@ -767,13 +774,11 @@ void nr_ra_succeeded(module_id_t mod_id, frame_t frame, int slot){ LOG_I(MAC, "[UE %d][%d.%d][RAPROC] RA procedure succeeded. CF-RA: RAR successfully received.\n", mod_id, frame, slot); ra->RA_window_cnt = -1; - mac->crnti = ra->t_crnti; } else { LOG_I(MAC, "[UE %d][%d.%d][RAPROC] RA procedure succeeded. CB-RA: Contention Resolution is successful.\n", mod_id, frame, slot); - ra->RA_contention_resolution_cnt = -1; ra->RA_contention_resolution_timer_active = 0; mac->crnti = ra->t_crnti; ra->t_crnti = 0; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 42296254a5..f9ba33c2b1 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -183,7 +183,8 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPSize = NRRIV2BW(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); rel15->BWPStart = NRRIV2PRBOFFSET(initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; - rel15->dci_length_options[0] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_TC, rel15->BWPSize, dl_bwp_id); + for (int i = 0; i < rel15->num_dci_options; i++) + rel15->dci_length_options[i] = nr_dci_size(initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_TC, rel15->BWPSize, dl_bwp_id); break; case NR_RNTI_SP_CSI: break; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 7c9759bf03..205b702b4e 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -171,7 +171,7 @@ int8_t nr_ue_decode_mib(module_id_t module_id, uint16_t ssb_start_subcarrier, uint16_t cell_id) { - LOG_D(MAC,"[L2][MAC] decode mib\n"); + LOG_I(MAC,"[L2][MAC] decode mib\n"); NR_UE_MAC_INST_t *mac = get_mac_inst(module_id); mac->physCellId = cell_id; @@ -516,7 +516,10 @@ int nr_ue_process_dci_indication_pdu(module_id_t module_id,int cc_id, int gNB_in dci->rnti,dci->dci_format,dci->n_CCE,dci->payloadSize,*(unsigned long long*)dci->payloadBits); int8_t ret = nr_extract_dci_info(mac, dci->dci_format, dci->payloadSize, dci->rnti, (uint64_t *)dci->payloadBits, def_dci_pdu_rel15); if ((ret&1) == 1) return -1; - else if (ret == 2) dci->dci_format = NR_UL_DCI_FORMAT_0_0; + else if (ret == 2) { + dci->dci_format = NR_UL_DCI_FORMAT_0_0; + def_dci_pdu_rel15 = &mac->def_dci_pdu_rel15[dci->dci_format]; + } return (nr_ue_process_dci(module_id, cc_id, gNB_index, frame, slot, def_dci_pdu_rel15, dci)); } @@ -2388,14 +2391,6 @@ void nr_ue_send_sdu(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *u VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_IN); - #if defined(ENABLE_MAC_PAYLOAD_DEBUG) - LOG_T(MAC, "[UE %d] First 32 bytes of DLSCH : \n", module_idP); - for (i = 0; i < 32; i++) { - LOG_T(MAC, "%x.", sdu[i]); - } - LOG_T(MAC, "\n"); - #endif - LOG_D(MAC, "In %s [%d.%d] Handling DLSCH PDU...\n", __FUNCTION__, dl_info->frame, dl_info->slot); // Processing MAC PDU @@ -2521,9 +2516,10 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, dci_pdu_rel15->format_indicator = (*dci_pdu>>(dci_size-pos))&1; //switch to DCI_0_0 - if (dci_pdu_rel15->format_indicator == 0) + if (dci_pdu_rel15->format_indicator == 0) { + dci_pdu_rel15 = &mac->def_dci_pdu_rel15[NR_UL_DCI_FORMAT_0_0]; return 2+nr_extract_dci_info(mac, NR_UL_DCI_FORMAT_0_0, dci_size, rnti, dci_pdu, dci_pdu_rel15); - + } #ifdef DEBUG_EXTRACT_DCI LOG_D(MAC,"Format indicator %d (%d bits) N_RB_BWP %d => %d (0x%lx)\n",dci_pdu_rel15->format_indicator,1,N_RB,dci_size-pos,*dci_pdu); #endif @@ -2725,6 +2721,12 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, pos++; dci_pdu_rel15->format_indicator = (*dci_pdu>>(dci_size-pos))&1; + //switch to DCI_0_0 + if (dci_pdu_rel15->format_indicator == 0) { + dci_pdu_rel15 = &mac->def_dci_pdu_rel15[NR_UL_DCI_FORMAT_0_0]; + return 2+nr_extract_dci_info(mac, NR_UL_DCI_FORMAT_0_0, dci_size, rnti, dci_pdu, dci_pdu_rel15); + } + if (dci_pdu_rel15->format_indicator == 0) return 1; // discard dci, format indicator not corresponding to dci_format @@ -2863,40 +2865,62 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, break; case NR_RNTI_TC: - /* - // indicating a DL DCI format 1bit - dci_pdu->= (*dci_pdu>>(dci_size-pos)format_indicator&1)<<(dci_size-pos++); - // Freq domain assignment max 16 bit + //Identifier for DCI formats + pos++; + dci_pdu_rel15->format_indicator = (*dci_pdu>>(dci_size-pos))&1; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"Format indicator %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->format_indicator,1,dci_size-pos,*dci_pdu); +#endif + if (dci_pdu_rel15->format_indicator == 1) + return 1; // discard dci, format indicator not corresponding to dci_format fsize = (int)ceil( log2( (N_RB_UL*(N_RB_UL+1))>>1 ) ); - for (int i=0; i= ((*dci_pdu>>(dci_size-pos)frequency_domain_assignment>>(fsize-i-1))&1)<<(dci_size-pos++); + pos+=fsize; + dci_pdu_rel15->frequency_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&((1< %d (0x%lx)\n",dci_pdu_rel15->frequency_domain_assignment.val,fsize,dci_size-pos,*dci_pdu); +#endif // Time domain assignment 4bit - for (int i=0; i<4; i++) - dci_pdu->= (((uint64_t)*dci_pdu>>(dci_size-pos)time_domain_assignment>>(3-i))&1)<<(dci_size-pos++); + pos+=4; + dci_pdu_rel15->time_domain_assignment.val = (*dci_pdu>>(dci_size-pos))&0xf; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"time-domain assignment %d (4 bits)=> %d (0x%lx)\n",dci_pdu_rel15->time_domain_assignment.val,dci_size-pos,*dci_pdu); +#endif // Frequency hopping flag  E1 bit - dci_pdu->= ((uint64_t)*dci_pdu>>(dci_size-pos)frequency_hopping_flag&1)<<(dci_size-pos++); + pos++; + dci_pdu_rel15->frequency_hopping_flag.val= (*dci_pdu>>(dci_size-pos))&1; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"frequency_hopping %d (1 bit)=> %d (0x%lx)\n",dci_pdu_rel15->frequency_hopping_flag.val,dci_size-pos,*dci_pdu); +#endif // MCS 5 bit - for (int i=0; i<5; i++) - dci_pdu->= (((uint64_t)*dci_pdu>>(dci_size-pos)mcs>>(4-i))&1)<<(dci_size-pos++); + pos+=5; + dci_pdu_rel15->mcs= (*dci_pdu>>(dci_size-pos))&0x1f; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"mcs %d (5 bits)=> %d (0x%lx)\n",dci_pdu_rel15->mcs,dci_size-pos,*dci_pdu); +#endif // New data indicator 1bit - dci_pdu->= ((uint64_t)*dci_pdu>>(dci_size-pos)ndi&1)<<(dci_size-pos++); + pos++; + dci_pdu_rel15->ndi= (*dci_pdu>>(dci_size-pos))&1; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"NDI %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->ndi,1,dci_size-pos,*dci_pdu); +#endif // Redundancy version 2bit - for (int i=0; i<2; i++) - dci_pdu->= (((uint64_t)*dci_pdu>>(dci_size-pos)rv>>(1-i))&1)<<(dci_size-pos++); + pos+=2; + dci_pdu_rel15->rv= (*dci_pdu>>(dci_size-pos))&3; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"RV %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->rv,2,dci_size-pos,*dci_pdu); +#endif // HARQ process number 4bit - for (int i=0; i<4; i++) - *dci_pdu |= (((uint64_t)*dci_pdu>>(dci_size-pos)harq_pid>>(3-i))&1)<<(dci_size-pos++); - + pos+=4; + dci_pdu_rel15->harq_pid = (*dci_pdu>>(dci_size-pos))&0xf; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"HARQ_PID %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->harq_pid,4,dci_size-pos,*dci_pdu); +#endif // TPC command for scheduled PUSCH  E2 bits - for (int i=0; i<2; i++) - dci_pdu->= (((uint64_t)*dci_pdu>>(dci_size-pos)tpc>>(1-i))&1)<<(dci_size-pos++); - */ - // UL/SUL indicator  E1 bit - /* - commented for now (RK): need to get this information from BWP descriptor - if (cfg->pucch_config.pucch_GroupHopping.value) - dci_pdu->= ((uint64_t)dci_pdu_rel15->ul_sul_indicator&1)<<(dci_size-pos++); - */ + pos+=2; + dci_pdu_rel15->tpc = (*dci_pdu>>(dci_size-pos))&3; +#ifdef DEBUG_EXTRACT_DCI + LOG_I(MAC,"TPC %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->tpc,2,dci_size-pos,*dci_pdu); +#endif break; } @@ -3312,7 +3336,7 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, #endif */ - LOG_I(MAC, "[%d.%d] Received TA_COMMAND %u TAGID %u CC_id %d\n", frameP, slot, ul_time_alignment->ta_command, ul_time_alignment->tag_id, CC_id); + LOG_D(MAC, "[%d.%d] Received TA_COMMAND %u TAGID %u CC_id %d\n", frameP, slot, ul_time_alignment->ta_command, ul_time_alignment->tag_id, CC_id); break; case DL_SCH_LCID_CON_RES_ID: @@ -3409,145 +3433,116 @@ void nr_ue_process_mac_pdu(nr_downlink_indication_t *dl_info, } } -//////////////////////////////////////////////////////// -/////* ULSCH MAC PDU generation (6.1.2 TS 38.321) *///// -//////////////////////////////////////////////////////// +/** + * Function: generating MAC CEs (MAC CE and subheader) for the ULSCH PDU + * Notes: TODO: PHR and BSR reporting + * Parameters: + * @mac_ce pointer to the MAC sub-PDUs including the MAC CEs + * @mac pointer to the MAC instance + * Return: number of written bytes + */ +int nr_write_ce_ulsch_pdu(uint8_t *mac_ce, + NR_UE_MAC_INST_t *mac) { -uint16_t nr_generate_ulsch_pdu(uint8_t *sdus_payload, - uint8_t *pdu, - uint8_t num_sdus, - uint16_t *sdu_lengths, - uint8_t *sdu_lcids, - uint8_t power_headroom, - uint16_t crnti, - uint16_t truncated_bsr, - uint16_t short_bsr, - uint16_t long_bsr, - unsigned short post_padding, - uint16_t buflen) { + int mac_ce_len = 0; + uint8_t mac_ce_size = 0; + NR_UE_MAC_CE_t *nr_ue_mac_ce = &mac->nr_ue_mac_ce; - NR_MAC_SUBHEADER_FIXED *mac_pdu_ptr = (NR_MAC_SUBHEADER_FIXED *) pdu; + if (nr_ue_mac_ce->phr_reporting && mac->phr_Config != NULL) { - LOG_D(MAC, "[UE] Generating ULSCH PDU : num_sdus %d\n", num_sdus); - - #ifdef DEBUG_HEADER_PARSING - - for (int i = 0; i < num_sdus; i++) - LOG_D(MAC, "[UE] MAC subPDU %d (lcid %d length %d bytes \n", i, sdu_lcids[i], sdu_lengths[i]); - - #endif - - // Generating UL MAC subPDUs including MAC SDU and subheader - - for (int i = 0; i < num_sdus; i++) { - LOG_D(MAC, "[UE] Generating UL MAC subPDUs for SDU with lenght %d ( num_sdus %d )\n", sdu_lengths[i], num_sdus); - - if (sdu_lcids[i] != UL_SCH_LCID_CCCH){ - if (sdu_lengths[i] < 128) { - ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->R = 0; - ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->F = 0; - ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->LCID = sdu_lcids[i]; - ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->L = (unsigned char) sdu_lengths[i]; - mac_pdu_ptr += sizeof(NR_MAC_SUBHEADER_SHORT); - } else { - ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->R = 0; - ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->F = 1; - ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->LCID = sdu_lcids[i]; - ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->L1 = ((unsigned short) sdu_lengths[i] >> 8) & 0x7f; - ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->L2 = (unsigned short) sdu_lengths[i] & 0xff; - mac_pdu_ptr += sizeof(NR_MAC_SUBHEADER_LONG); - } - } else { // UL CCCH SDU - mac_pdu_ptr->R = 0; - mac_pdu_ptr->LCID = sdu_lcids[i]; - mac_pdu_ptr ++; - } - - // cycle through SDUs, compute each relevant and place ulsch_buffer in - memcpy((void *) mac_pdu_ptr, (void *) sdus_payload, sdu_lengths[i]); - sdus_payload += sdu_lengths[i]; - mac_pdu_ptr += sdu_lengths[i]; - } - - // Generating UL MAC subPDUs including MAC CEs (MAC CE and subheader) - - if (power_headroom) { // MAC CE fixed subheader - mac_pdu_ptr->R = 0; - mac_pdu_ptr->LCID = UL_SCH_LCID_SINGLE_ENTRY_PHR; - mac_pdu_ptr++; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_SINGLE_ENTRY_PHR; + mac_ce++; // PHR MAC CE (1 octet) - ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_pdu_ptr)->PH = power_headroom; - ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_pdu_ptr)->R1 = 0; - ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_pdu_ptr)->PCMAX = 0; // todo - ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_pdu_ptr)->R2 = 0; - mac_pdu_ptr += sizeof(NR_SINGLE_ENTRY_PHR_MAC_CE); + ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->PH = 0; + ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->R1 = 0; + ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->PCMAX = 0; + ((NR_SINGLE_ENTRY_PHR_MAC_CE *) mac_ce)->R2 = 0; + + // update pointer and length + mac_ce_size = sizeof(NR_SINGLE_ENTRY_PHR_MAC_CE); + mac_ce += mac_ce_size; + mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED); + } - if (crnti) { + if (!get_softmodem_params()->sa && get_softmodem_params()->do_ra && mac->ra.ra_state != RA_SUCCEEDED) { + + LOG_D(NR_MAC, "In %s: generating C-RNTI MAC CE with C-RNTI %x\n", __FUNCTION__, mac->crnti); + // MAC CE fixed subheader - mac_pdu_ptr->R = 0; - mac_pdu_ptr->LCID = UL_SCH_LCID_C_RNTI; - mac_pdu_ptr++; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_C_RNTI; + mac_ce++; // C-RNTI MAC CE (2 octets) - * (uint16_t *) mac_pdu_ptr = crnti; - mac_pdu_ptr += sizeof(uint16_t); + *(uint16_t *) mac_ce = mac->crnti; + + // update pointer and length + mac_ce_size = sizeof(uint16_t); + mac_ce += mac_ce_size; + mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED); + } - if (truncated_bsr) { + if (nr_ue_mac_ce->truncated_bsr) { + + LOG_D(NR_MAC, "In %s: generating short truncated BSR MAC CE with command %x\n", __FUNCTION__, nr_ue_mac_ce->truncated_bsr); + // MAC CE fixed subheader - mac_pdu_ptr->R = 0; - mac_pdu_ptr->LCID = UL_SCH_LCID_S_TRUNCATED_BSR; - mac_pdu_ptr++; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_S_TRUNCATED_BSR; + mac_ce++; // Short truncated BSR MAC CE (1 octet) - ((NR_BSR_SHORT_TRUNCATED *) mac_pdu_ptr)-> Buffer_size = truncated_bsr; - ((NR_BSR_SHORT_TRUNCATED *) mac_pdu_ptr)-> LcgID = 0; // todo - mac_pdu_ptr+= sizeof(NR_BSR_SHORT_TRUNCATED); - } else if (short_bsr) { + ((NR_BSR_SHORT_TRUNCATED *) mac_ce)-> Buffer_size = 0; + ((NR_BSR_SHORT_TRUNCATED *) mac_ce)-> LcgID = 0; + + // update pointer and length + mac_ce_size = sizeof(NR_BSR_SHORT_TRUNCATED); + mac_ce += mac_ce_size; + mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED); + + } else if (nr_ue_mac_ce->short_bsr) { + + LOG_D(NR_MAC, "In %s: generating short BSR MAC CE with command %x\n", __FUNCTION__, nr_ue_mac_ce->short_bsr); + // MAC CE fixed subheader - mac_pdu_ptr->R = 0; - mac_pdu_ptr->LCID = UL_SCH_LCID_S_BSR; - mac_pdu_ptr++; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->R = 0; + ((NR_MAC_SUBHEADER_FIXED *) mac_ce)->LCID = UL_SCH_LCID_S_BSR; + mac_ce++; // Short truncated BSR MAC CE (1 octet) - ((NR_BSR_SHORT *) mac_pdu_ptr)->Buffer_size = short_bsr; - ((NR_BSR_SHORT *) mac_pdu_ptr)->LcgID = 0; // todo - mac_pdu_ptr+= sizeof(NR_BSR_SHORT); - } else if (long_bsr) { + ((NR_BSR_SHORT *) mac_ce)->Buffer_size = nr_ue_mac_ce->short_bsr; + ((NR_BSR_SHORT *) mac_ce)->LcgID = 0; + + // update pointer and length + mac_ce_size = sizeof(NR_BSR_SHORT); + mac_ce += mac_ce_size; + mac_ce_len += mac_ce_size + sizeof(NR_MAC_SUBHEADER_FIXED); + + } else if (nr_ue_mac_ce->long_bsr) { // MAC CE variable subheader // todo ch 6.1.3.1. TS 38.321 // ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->R = 0; // ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->F = 0; // ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->LCID = UL_SCH_LCID_L_BSR; // ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->L = 0; - // last_size = 2; - // mac_pdu_ptr += last_size; + // sh_size = 2; // Short truncated BSR MAC CE (1 octet) - // ((NR_BSR_LONG *) ce_ptr)->Buffer_size0 = short_bsr; - // ((NR_BSR_LONG *) ce_ptr)->LCGID0 = 0; + // ((NR_BSR_LONG *) mac_ce)->Buffer_size0 = short_bsr; + // ((NR_BSR_LONG *) mac_ce)->LCGID0 = 0; // mac_ce_size = sizeof(NR_BSR_LONG); // size is variable } -// compute offset before adding padding (if necessary) - int padding_bytes = 0; - if(buflen > 0) // If the buflen is provided - padding_bytes = buflen + pdu - (unsigned char *) mac_pdu_ptr; + return mac_ce_len; - AssertFatal(padding_bytes>=0,""); - - // Compute final offset for padding - if (post_padding || padding_bytes>0) { - ((NR_MAC_SUBHEADER_FIXED *) mac_pdu_ptr)->R = 0; - ((NR_MAC_SUBHEADER_FIXED *) mac_pdu_ptr)->LCID = UL_SCH_LCID_PADDING; - mac_pdu_ptr++; - } - return (uint8_t *)mac_pdu_ptr-pdu; } + ///////////////////////////////////// // Random Access Response PDU // // TS 38.213 ch 8.2 // @@ -3596,7 +3591,6 @@ int nr_ue_process_rar(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t uint8_t is_Msg3 = 1; frame_t frame_tx = 0; int slot_tx = 0; - uint16_t rnti = 0; int ret = 0; NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) dlsch_buffer; // RAR subheader pointer NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 1); // RAR subPDU pointer @@ -3648,9 +3642,6 @@ int nr_ue_process_rar(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t unsigned char csi_req; #endif - // TC-RNTI - ra->t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8); - // TA command ul_time_alignment->apply_ta = 1; ul_time_alignment->ta_command = 31 + rar->TA2 + (rar->TA1 << 5); @@ -3696,12 +3687,6 @@ int nr_ue_process_rar(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t rar_grant.Msg3_f_alloc = (uint16_t) ((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12)); // frequency hopping rar_grant.freq_hopping = (unsigned char) (rar->UL_GRANT_1 >> 2); - // TC-RNTI - if (ra->t_crnti) { - rnti = ra->t_crnti; - } else { - rnti = mac->crnti; - } #ifdef DEBUG_RAR LOG_I(NR_MAC, "rarh->E = 0x%x\n", rarh->E); @@ -3741,12 +3726,19 @@ int nr_ue_process_rar(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t if (ret != -1){ fapi_nr_ul_config_request_t *ul_config = get_ul_config_request(mac, slot_tx); + uint16_t rnti = mac->crnti; if (!ul_config) { LOG_W(MAC, "In %s: ul_config request is NULL. Probably due to unexpected UL DCI in frame.slot %d.%d. Ignoring DCI!\n", __FUNCTION__, frame, slot); return -1; } + // Upon successful reception, set the T-CRNTI to the RAR value if the RA preamble is selected among the contention-based RA Preambles + if (!ra->cfra) { + ra->t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8); + rnti = ra->t_crnti; + } + nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu = &ul_config->ul_config_list[ul_config->number_pdus].pusch_config_pdu; fill_ul_config(ul_config, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 5daea8e84c..659bf37371 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -947,12 +947,15 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in } // this is for Msg2/Msg4 if (mac->ra.ra_state >= WAIT_RAR) { - rel15->num_dci_options = 1; + rel15->num_dci_options = mac->ra.ra_state == WAIT_RAR ? 1 : 2; rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; + if (mac->ra.ra_state == WAIT_CONTENTION_RESOLUTION) + rel15->dci_format_options[1] = NR_UL_DCI_FORMAT_0_0; // msg3 retransmission config_dci_pdu(mac, rel15, dl_config, mac->ra.ra_state == WAIT_RAR ? NR_RNTI_RA : NR_RNTI_TC , -1); fill_dci_search_candidates(ss0, rel15); dl_config->number_pdus = 1; - LOG_D(NR_MAC,"mac->cg %p: Calling fill_scheduled_response rnti %x, type0_pdcch, num_pdus %d\n",mac->cg,rel15->rnti,dl_config->number_pdus); + LOG_D(NR_MAC,"mac->cg %p: Calling fill_scheduled_response rnti %x, type0_pdcch, num_pdus %d frame %d slot %d\n", + mac->cg,rel15->rnti,dl_config->number_pdus,rx_frame,rx_slot); fill_scheduled_response(&scheduled_response, dl_config, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) mac->if_module->scheduled_response(&scheduled_response); @@ -995,7 +998,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in frame_t frame_tx = ul_info->frame_tx; slot_t slot_tx = ul_info->slot_tx; module_id_t mod_id = ul_info->module_id; - uint8_t access_mode = SCHEDULED_ACCESS; + uint32_t gNB_index = ul_info->gNB_index; NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id); RA_config_t *ra = &mac->ra; @@ -1009,7 +1012,6 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in LOG_D(NR_MAC, "In %s:[%d.%d]: number of UL PDUs: %d with UL transmission in [%d.%d]\n", __FUNCTION__, frame_tx, slot_tx, ul_config->number_pdus, ul_config->sfn, ul_config->slot); uint8_t ulsch_input_buffer[MAX_ULSCH_PAYLOAD_BYTES]; - uint8_t data_existing = 0; nr_scheduled_response_t scheduled_response; fapi_nr_tx_request_t tx_req; @@ -1020,7 +1022,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in if (ulcfg_pdu->pdu_type == FAPI_NR_UL_CONFIG_TYPE_PUSCH) { uint16_t TBS_bytes = ulcfg_pdu->pusch_config_pdu.pusch_data.tb_size; - LOG_D(NR_MAC,"harq_id %d, NDI %d NDI_DCI %d, TBS_bytes %d (ra_state %d\n", + LOG_D(NR_MAC,"harq_id %d, NDI %d NDI_DCI %d, TBS_bytes %d (ra_state %d)\n", ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id, mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id], ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator, @@ -1041,46 +1043,15 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in mac->first_ul_tx[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id]==1)){ // Getting IP traffic to be transmitted - data_existing = nr_ue_get_sdu(mod_id, - cc_id, - frame_tx, - slot_tx, - 0, - ulsch_input_buffer, - TBS_bytes, - &access_mode); + nr_ue_get_sdu(mod_id, frame_tx, slot_tx, gNB_index, ulsch_input_buffer, TBS_bytes); } LOG_D(NR_MAC,"Flipping NDI for harq_id %d\n",ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator); mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] = ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator; mac->first_ul_tx[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] = 0; - //Random traffic to be transmitted if there is no IP traffic available for this Tx opportunity - if (!data_existing) { - //Use zeros for the header bytes in noS1 mode, in order to make sure that the LCID is not valid - //and block this traffic from being forwarded to the upper layers at the gNB - LOG_D(PHY, "In %s: Random data to be transmitted: TBS_bytes %d \n", __FUNCTION__, TBS_bytes); - - //Give the first byte a dummy value (a value not corresponding to any valid LCID based on 38.321, Table 6.2.1-2) - //in order to distinguish the PHY random packets at the MAC layer of the gNB receiver from the normal packets that should - //have a valid LCID (nr_process_mac_pdu function) - ulsch_input_buffer[0] = UL_SCH_LCID_PADDING; - - for (int i = 1; i < TBS_bytes; i++) { - ulsch_input_buffer[i] = (unsigned char) rand(); - } - } } - #ifdef DEBUG_MAC_PDU - LOG_D(PHY, "Is data existing ?: %d \n", data_existing); - LOG_I(PHY, "Printing MAC PDU to be encoded, TBS is: %d \n", TBS_bytes); - for (i = 0; i < TBS_bytes; i++) { - printf("%02x", ulsch_input_buffer[i]); - } - printf("\n"); - #endif - // Config UL TX PDU tx_req.slot = slot_tx; tx_req.sfn = frame_tx; @@ -1089,11 +1060,15 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in tx_req.tx_request_body[0].pdu_index = j; tx_req.tx_request_body[0].pdu = ulsch_input_buffer; + if (ra->ra_state == WAIT_CONTENTION_RESOLUTION && !ra->cfra){ + LOG_I(NR_MAC,"[RAPROC] RA-Msg3 retransmitted\n"); + // 38.321 restart the ra-ContentionResolutionTimer at each HARQ retransmission in the first symbol after the end of the Msg3 transmission + nr_Msg3_transmitted(ul_info->module_id, ul_info->cc_id, ul_info->frame_tx, ul_info->slot_tx, ul_info->gNB_index); + } if (ra->ra_state == WAIT_RAR && !ra->cfra){ LOG_I(NR_MAC,"[RAPROC] RA-Msg3 transmitted\n"); - nr_Msg3_transmitted(ul_info->module_id, ul_info->cc_id, ul_info->frame_tx, ul_info->gNB_index); + nr_Msg3_transmitted(ul_info->module_id, ul_info->cc_id, ul_info->frame_tx, ul_info->slot_tx, ul_info->gNB_index); } - } } @@ -2062,90 +2037,174 @@ void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t s } // if is_nr_UL_slot } -#define MAX_LCID 8 //Fixme: also defined in LCID table -uint8_t -nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP, - sub_frame_t subframe, uint8_t eNB_index, - uint8_t *ulsch_buffer, uint16_t buflen, uint8_t *access_mode) { +#define MAX_LCID 8 // NR_MAX_NUM_LCID shall be used but the mac_rlc_data_req function can fetch data for max 8 LCID + +/** + * Function: to fetch data to be transmitted from RLC, place it in the ULSCH PDU buffer + to generate the complete MAC PDU with sub-headers and MAC CEs according to ULSCH MAC PDU generation (6.1.2 TS 38.321) + the selected sub-header for the payload sub-PDUs is NR_MAC_SUBHEADER_LONG + * @module_idP Module ID + * @frameP current UL frame + * @subframe current UL slot + * @gNB_index gNB index + * @ulsch_buffer Pointer to ULSCH PDU + * @buflen TBS + */ +uint8_t nr_ue_get_sdu(module_id_t module_idP, + frame_t frameP, + sub_frame_t subframe, + uint8_t gNB_index, + uint8_t *ulsch_buffer, + uint16_t buflen) { + + int16_t buflen_remain = 0; uint8_t lcid = 0; - uint16_t sdu_lengths[MAX_LCID] = { 0 }; - uint8_t sdu_lcids[MAX_LCID] = { 0 }; - uint16_t payload_offset = 0, num_sdus = 0; - uint8_t ulsch_sdus[MAX_ULSCH_PAYLOAD_BYTES]; - //unsigned short post_padding = 0; + uint16_t sdu_length = 0; + uint16_t num_sdus = 0; + uint16_t sdu_length_total = 0; NR_UE_MAC_INST_t *mac = get_mac_inst(module_idP); + const uint8_t sh_size = sizeof(NR_MAC_SUBHEADER_LONG); + + // Pointer used to build the MAC PDU by placing the RLC SDUs in the ULSCH buffer + uint8_t *pdu = ulsch_buffer; + + // Preparing the MAC CEs sub-PDUs and get the total size + unsigned char mac_header_control_elements[16] = {0}; + int tot_mac_ce_len = nr_write_ce_ulsch_pdu(&mac_header_control_elements[0], mac); + uint8_t total_mac_pdu_header_len = tot_mac_ce_len; + + LOG_D(NR_MAC, "In %s: [UE %d] [%d.%d] process UL transport block at with size TBS = %d bytes \n", __FUNCTION__, module_idP, frameP, subframe, buflen); - LOG_D(NR_MAC, - "[UE %d] MAC PROCESS UL TRANSPORT BLOCK at frame%d subframe %d TBS=%d\n", - module_idP, frameP, subframe, buflen); - AssertFatal(CC_id == 0, - "Transmission on secondary CCs is not supported yet\n"); - // Check for DCCH first // TO DO: Multiplex in the order defined by the logical channel prioritization - int buflen_remain = buflen; - char * current_ulsch_ptr = (char*)ulsch_sdus; for (lcid = UL_SCH_LCID_SRB1; lcid < MAX_LCID; lcid++) { - while ( (sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, - mac->crnti, - eNB_index, - frameP, - ENB_FLAG_NO, - MBMS_FLAG_NO, - lcid, - buflen_remain-MAX_RLC_SDU_SUBHEADER_SIZE*2, - //Fixme: Laurent I removed MAX_RLC_SDU_SUBHEADER_SIZE*2 because else we get out the buffer silently - // the interface with nr_generate_ulsch_pdu() looks over complex and not CPU optimized - current_ulsch_ptr , 0, - 0)) > 0 ) { - - AssertFatal(buflen_remain >= sdu_lengths[num_sdus], - "LCID=%d RLC has segmented %d bytes but MAC has max=%d\n", - lcid, sdu_lengths[num_sdus], buflen_remain); - AssertFatal(current_ulsch_ptr < (char*)ulsch_sdus+MAX_ULSCH_PAYLOAD_BYTES, ""); - - current_ulsch_ptr += sdu_lengths[num_sdus]; - buflen_remain -= sdu_lengths[num_sdus]; - buflen_remain -= MAX_RLC_SDU_SUBHEADER_SIZE; - sdu_lcids[num_sdus] = lcid; - num_sdus++; + + buflen_remain = buflen - (total_mac_pdu_header_len + sdu_length_total + sh_size); + + LOG_D(NR_MAC, "In %s: [UE %d] [%d.%d] UL-DXCH -> ULSCH, RLC with LCID 0x%02x (TBS %d bytes, sdu_length_total %d bytes, MAC header len %d bytes, buflen_remain %d bytes)\n", + __FUNCTION__, + module_idP, + frameP, + subframe, + lcid, + buflen, + sdu_length_total, + tot_mac_ce_len, + buflen_remain); + + while (buflen_remain > 0){ + + // Pointer used to build the MAC sub-PDU headers in the ULSCH buffer for each SDU + NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) pdu; + + pdu += sh_size; + + sdu_length = mac_rlc_data_req(module_idP, + mac->crnti, + gNB_index, + frameP, + ENB_FLAG_NO, + MBMS_FLAG_NO, + lcid, + buflen_remain, + (char *)pdu, + 0, + 0); + + AssertFatal(buflen_remain >= sdu_length, "In %s: LCID = 0x%02x RLC has segmented %d bytes but MAC has max %d remaining bytes\n", + __FUNCTION__, + lcid, + sdu_length, + buflen_remain); + + if (sdu_length > 0) { + + LOG_D(MAC, "In %s: Generating UL MAC sub-PDU for SDU %d, length %d bytes, RB with LCID 0x%02x (buflen (TBS) %d bytes)\n", __FUNCTION__, + num_sdus + 1, + sdu_length, + lcid, + buflen); + + header->R = 0; + header->F = 1; + header->LCID = lcid; + header->L1 = ((unsigned short) sdu_length >> 8) & 0x7f; + header->L2 = (unsigned short) sdu_length & 0xff; + + #ifdef ENABLE_MAC_PAYLOAD_DEBUG + LOG_I(NR_MAC, "In %s: dumping MAC sub-header with length %d: \n", __FUNCTION__, sh_size); + log_dump(NR_MAC, header, sh_size, LOG_DUMP_CHAR, "\n"); + LOG_I(NR_MAC, "In %s: dumping MAC SDU with length %d \n", __FUNCTION__, sdu_length); + log_dump(NR_MAC, pdu, sdu_length, LOG_DUMP_CHAR, "\n"); + #endif + + pdu += sdu_length; + sdu_length_total += sdu_length; + total_mac_pdu_header_len += sh_size; + + num_sdus++; + + } else { + pdu -= sh_size; + LOG_D(MAC, "In %s: no data to transmit for RB with LCID 0x%02x\n", __FUNCTION__, lcid); + break; + } + + buflen_remain = buflen - (total_mac_pdu_header_len + sdu_length_total + sh_size); + } } - - // Generate ULSCH PDU - if (num_sdus>0) { - payload_offset = nr_generate_ulsch_pdu(ulsch_sdus, - ulsch_buffer, // mac header - num_sdus, // num sdus - sdu_lengths, // sdu length - sdu_lcids, // sdu lcid - 0, // power_headroom - mac->crnti, // crnti - 0, // truncated_bsr - 0, // short_bsr - 0, // long_bsr - 0, // post_padding - buflen); // TBS in bytes - } else { - return 0; + + if (tot_mac_ce_len > 0) { + + LOG_D(NR_MAC, "In %s copying %d bytes of MAC CEs to the UL PDU \n", __FUNCTION__, tot_mac_ce_len); + memcpy((void *) pdu, (void *) mac_header_control_elements, tot_mac_ce_len); + pdu += (unsigned char) tot_mac_ce_len; + + #ifdef ENABLE_MAC_PAYLOAD_DEBUG + LOG_I(NR_MAC, "In %s: dumping MAC CE with length tot_mac_ce_len %d: \n", __FUNCTION__, tot_mac_ce_len); + log_dump(NR_MAC, mac_header_control_elements, tot_mac_ce_len, LOG_DUMP_CHAR, "\n"); + #endif + } - // Padding: fill remainder of ULSCH with 0 - if (buflen - payload_offset > 0){ - for (int j = payload_offset; j < buflen; j++) - ulsch_buffer[j] = 0; + buflen_remain = buflen - (total_mac_pdu_header_len + sdu_length_total); + + // Compute final offset for padding and fill remainder of ULSCH with 0 + if (buflen_remain > 0) { + + ((NR_MAC_SUBHEADER_FIXED *) pdu)->R = 0; + ((NR_MAC_SUBHEADER_FIXED *) pdu)->LCID = UL_SCH_LCID_PADDING; + + #ifdef ENABLE_MAC_PAYLOAD_DEBUG + LOG_I(NR_MAC, "In %s: padding MAC sub-header with length %ld bytes \n", __FUNCTION__, sizeof(NR_MAC_SUBHEADER_FIXED)); + log_dump(NR_MAC, pdu, sizeof(NR_MAC_SUBHEADER_FIXED), LOG_DUMP_CHAR, "\n"); + #endif + + pdu++; + buflen_remain--; + + if (IS_SOFTMODEM_RFSIM) { + for (int j = 0; j < buflen_remain; j++) { + pdu[j] = (unsigned char) rand(); + } + } else { + memset(pdu, 0, buflen_remain); + } + + #ifdef ENABLE_MAC_PAYLOAD_DEBUG + LOG_I(NR_MAC, "In %s: MAC padding sub-PDU with length %d bytes \n", __FUNCTION__, buflen_remain); + log_dump(NR_MAC, pdu, buflen_remain, LOG_DUMP_CHAR, "\n"); + #endif + } -#if defined(ENABLE_MAC_PAYLOAD_DEBUG) - LOG_I(NR_MAC, "Printing UL MAC payload UE side, payload_offset: %d \n", payload_offset); - for (int i = 0; i < buflen ; i++) { - //harq_process_ul_ue->a[i] = (unsigned char) rand(); - //printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]); - printf("%02x ",(unsigned char)ulsch_buffer[i]); - } - printf("\n"); -#endif + #ifdef ENABLE_MAC_PAYLOAD_DEBUG + LOG_I(NR_MAC, "In %s: dumping MAC PDU with length %d: \n", __FUNCTION__, buflen); + log_dump(NR_MAC, ulsch_buffer, buflen, LOG_DUMP_CHAR, "\n"); + #endif + + return num_sdus > 0 ? 1 : 0; - return 1; } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 14581b2706..041bb22768 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -60,6 +60,8 @@ uint16_t nr_pdcch_order_table[6] = { 31, 31, 511, 2047, 2047, 8191 }; +uint8_t vnf_first_sched_entry = 1; + void clear_mac_stats(gNB_MAC_INST *gNB) { memset((void*)gNB->UE_info.mac_stats,0,MAX_MOBILES_PER_GNB*sizeof(NR_mac_stats_t)); } @@ -150,34 +152,32 @@ void clear_nr_nfapi_information(gNB_MAC_INST * gNB, gNB->pdu_index[CC_idP] = 0; - if (NFAPI_MODE == NFAPI_MONOLITHIC || NFAPI_MODE == NFAPI_MODE_PNF) { // monolithic or PNF + DL_req[CC_idP].SFN = frameP; + DL_req[CC_idP].Slot = slotP; + DL_req[CC_idP].dl_tti_request_body.nPDUs = 0; + DL_req[CC_idP].dl_tti_request_body.nGroup = 0; + //DL_req[CC_idP].dl_tti_request_body.transmission_power_pcfich = 6000; + memset(pdcch, 0, sizeof(**pdcch) * MAX_NUM_BWP * MAX_NUM_CORESET); - DL_req[CC_idP].SFN = frameP; - DL_req[CC_idP].Slot = slotP; - DL_req[CC_idP].dl_tti_request_body.nPDUs = 0; - DL_req[CC_idP].dl_tti_request_body.nGroup = 0; - //DL_req[CC_idP].dl_tti_request_body.transmission_power_pcfich = 6000; - memset(pdcch, 0, sizeof(**pdcch) * MAX_NUM_BWP * MAX_NUM_CORESET); + UL_dci_req[CC_idP].SFN = frameP; + UL_dci_req[CC_idP].Slot = slotP; + UL_dci_req[CC_idP].numPdus = 0; - UL_dci_req[CC_idP].SFN = frameP; - UL_dci_req[CC_idP].Slot = slotP; - UL_dci_req[CC_idP].numPdus = 0; + /* advance last round's future UL_tti_req to be ahead of current frame/slot */ + future_ul_tti_req->SFN = (slotP == 0 ? frameP : frameP + 1) % 1024; + LOG_D(MAC,"Future_ul_tti SFN = %d for slot %d \n", future_ul_tti_req->SFN, (slotP + num_slots - 1) % num_slots); + /* future_ul_tti_req->Slot is fixed! */ + future_ul_tti_req->n_pdus = 0; + future_ul_tti_req->n_ulsch = 0; + future_ul_tti_req->n_ulcch = 0; + future_ul_tti_req->n_group = 0; - /* advance last round's future UL_tti_req to be ahead of current frame/slot */ - future_ul_tti_req->SFN = (slotP == 0 ? frameP : frameP + 1) % 1024; - /* future_ul_tti_req->Slot is fixed! */ - future_ul_tti_req->n_pdus = 0; - future_ul_tti_req->n_ulsch = 0; - future_ul_tti_req->n_ulcch = 0; - future_ul_tti_req->n_group = 0; + /* UL_tti_req is a simple pointer into the current UL_tti_req_ahead, i.e., + * it walks over UL_tti_req_ahead in a circular fashion */ + gNB->UL_tti_req[CC_idP] = &gNB->UL_tti_req_ahead[CC_idP][slotP]; - /* UL_tti_req is a simple pointer into the current UL_tti_req_ahead, i.e., - * it walks over UL_tti_req_ahead in a circular fashion */ - gNB->UL_tti_req[CC_idP] = &gNB->UL_tti_req_ahead[CC_idP][slotP]; + TX_req[CC_idP].Number_of_PDUs = 0; - TX_req[CC_idP].Number_of_PDUs = 0; - - } } /* void check_nr_ul_failure(module_id_t module_idP, @@ -401,6 +401,22 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, memset(&vrb_map_UL[last_slot * MAX_BWP_SIZE], 0, sizeof(uint16_t) * MAX_BWP_SIZE); clear_nr_nfapi_information(RC.nrmac[module_idP], CC_id, frame, slot); + + /*VNF first entry into scheduler. Since frame numbers for future_ul_tti_req of some future slots + will not be set before we encounter them, set them here */ + + if (NFAPI_MODE == NFAPI_MODE_VNF){ + if(vnf_first_sched_entry == 1) + { + for (int i = 0; iUL_tti_req_ahead[CC_id][i].SFN = (frame + 1) % 1024; + else + gNB->UL_tti_req_ahead[CC_id][i].SFN = frame; + } + vnf_first_sched_entry = 0; + } + } } @@ -429,7 +445,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, schedule_nr_prach(module_idP, f, s); } - // This schedule SR + // This schedule SR nr_sr_reporting(module_idP, frame, slot); // Schedule CSI-RS transmission diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index cfe4ebfb6e..b55fee2af6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -545,7 +545,7 @@ void nr_initiate_ra_proc(module_id_t module_idP, if (ra->CellGroup && ra->CellGroup->spCellConfig && ra->CellGroup->spCellConfig->spCellConfigDedicated && ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { ra->bwp_id = 1; - bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 1); @@ -653,33 +653,222 @@ void nr_initiate_ra_proc(module_id_t module_idP, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 0); } - void nr_schedule_RA(module_id_t module_idP, frame_t frameP, sub_frame_t slotP) - { - gNB_MAC_INST *mac = RC.nrmac[module_idP]; +void nr_schedule_RA(module_id_t module_idP, frame_t frameP, sub_frame_t slotP) { - start_meas(&mac->schedule_ra); - for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { - NR_COMMON_channels_t *cc = &mac->common_channels[CC_id]; - for (int i = 0; i < NR_NB_RA_PROC_MAX; i++) { - NR_RA_t *ra = &cc->ra[i]; - LOG_D(NR_MAC, "RA[state:%d]\n", ra->state); - switch (ra->state) { - case Msg2: - nr_generate_Msg2(module_idP, CC_id, frameP, slotP, ra); - break; - case Msg4: - nr_generate_Msg4(module_idP, CC_id, frameP, slotP, ra); - break; - case WAIT_Msg4_ACK: - nr_check_Msg4_Ack(module_idP, CC_id, frameP, slotP, ra); - break; - default: - break; - } + gNB_MAC_INST *mac = RC.nrmac[module_idP]; + + start_meas(&mac->schedule_ra); + for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { + NR_COMMON_channels_t *cc = &mac->common_channels[CC_id]; + for (int i = 0; i < NR_NB_RA_PROC_MAX; i++) { + NR_RA_t *ra = &cc->ra[i]; + LOG_D(NR_MAC, "RA[state:%d]\n", ra->state); + switch (ra->state) { + case Msg2: + nr_generate_Msg2(module_idP, CC_id, frameP, slotP, ra); + break; + case Msg3_retransmission: + nr_generate_Msg3_retransmission(module_idP, CC_id, frameP, slotP, ra); + break; + case Msg4: + nr_generate_Msg4(module_idP, CC_id, frameP, slotP, ra); + break; + case WAIT_Msg4_ACK: + nr_check_Msg4_Ack(module_idP, CC_id, frameP, slotP, ra); + break; + default: + break; } } - stop_meas(&mac->schedule_ra); } + stop_meas(&mac->schedule_ra); +} + + +void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t frame, sub_frame_t slot, NR_RA_t *ra) { + + gNB_MAC_INST *nr_mac = RC.nrmac[module_idP]; + NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id]; + NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + + NR_BWP_Uplink_t *ubwp = ra->CellGroup ? + ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id-1] : + NULL; + + NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList= ubwp ? + ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList: + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + + int mu = ubwp ? + ubwp->bwp_Common->genericParameters.subcarrierSpacing : + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; + + uint8_t K2 = *pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->k2; + + const int sched_frame = frame + (slot + K2 >= nr_slots_per_frame[mu]); + const int sched_slot = (slot + K2) % nr_slots_per_frame[mu]; + + if (is_xlsch_in_slot(RC.nrmac[module_idP]->ulsch_slot_bitmap[sched_slot / 64], sched_slot)) { + // beam association for FR2 + int16_t *tdd_beam_association = nr_mac->tdd_beam_association; + if (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0] >= 257) { + uint8_t tdd_period_slot = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots + scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots; + if ((scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSymbols > 0) || (scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols > 0)) + tdd_period_slot++; + int num_tdd_period = sched_slot/tdd_period_slot; + if((tdd_beam_association[num_tdd_period]!=-1)&&(tdd_beam_association[num_tdd_period]!=ra->beam_id)) + return; // can't schedule retransmission in this slot + else + tdd_beam_association[num_tdd_period] = ra->beam_id; + } + + int bwpSize = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + int bwpStart = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + int scs = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; + int fh = 0; + int startSymbolAndLength = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->startSymbolAndLength; + int mappingtype = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->mappingType; + + if (ra->CellGroup) { + AssertFatal(ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, + "downlinkBWP_ToAddModList has %d BWP!\n", ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); + int act_bwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + int act_bwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->startSymbolAndLength; + mappingtype = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->mappingType; + scs = ubwp->bwp_Common->genericParameters.subcarrierSpacing; + fh = ubwp->bwp_Dedicated->pusch_Config->choice.setup->frequencyHopping ? 1 : 0; + if ((bwpStart < act_bwp_start) || (bwpSize > act_bwp_size)) + bwpStart = act_bwp_start; + } + uint16_t *vrb_map_UL = + &RC.nrmac[module_idP]->common_channels[CC_id].vrb_map_UL[sched_slot * MAX_BWP_SIZE]; + + int rbStart = 0; + for (int i = 0; (i < ra->msg3_nb_rb) && (rbStart <= (bwpSize - ra->msg3_nb_rb)); i++) { + if (vrb_map_UL[rbStart + bwpStart + i]) { + rbStart += i; + i = 0; + } + } + if (rbStart > (bwpSize - ra->msg3_nb_rb)) { + // cannot find free vrb_map for msg3 retransmission in this slot + return; + } + + LOG_I(NR_MAC, "[gNB %d][RAPROC] Frame %d, Slot %d : CC_id %d Scheduling retransmission of Msg3 in (%d,%d)\n", + module_idP, frame, slot, CC_id, sched_frame, sched_slot); + + nfapi_nr_ul_tti_request_t *future_ul_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[CC_id][sched_slot]; + AssertFatal(future_ul_tti_req->SFN == sched_frame + && future_ul_tti_req->Slot == sched_slot, + "future UL_tti_req's frame.slot %d.%d does not match PUSCH %d.%d\n", + future_ul_tti_req->SFN, + future_ul_tti_req->Slot, + sched_frame, + sched_slot); + future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE; + future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pusch_pdu_t); + nfapi_nr_pusch_pdu_t *pusch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pusch_pdu; + memset(pusch_pdu, 0, sizeof(nfapi_nr_pusch_pdu_t)); + + fill_msg3_pusch_pdu(pusch_pdu, scc, + ra->msg3_round, + startSymbolAndLength, + ra->rnti, scs, + bwpSize, bwpStart, + mappingtype, fh, + rbStart, ra->msg3_nb_rb); + future_ul_tti_req->n_pdus += 1; + + // generation of DCI 0_0 to schedule msg3 retransmission + NR_SearchSpace_t *ss = ra->ra_ss; + NR_BWP_Downlink_t *bwp = NULL; + NR_ControlResourceSet_t *coreset = NULL; + + if (ra->CellGroup && + ra->CellGroup->spCellConfig && + ra->CellGroup->spCellConfig->spCellConfigDedicated && + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]) + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + + if (*ss->controlResourceSetId == 0) + coreset = nr_mac->sched_ctrlCommon->coreset; // this is coreset 0 + else + coreset = get_coreset(scc, bwp, ss, NR_SearchSpace__searchSpaceType_PR_common); + AssertFatal(coreset!=NULL,"Coreset cannot be null for RA-Msg3 retransmission\n"); + + nfapi_nr_ul_dci_request_t *ul_dci_req = &nr_mac->UL_dci_req[CC_id]; + + const int coresetid = coreset->controlResourceSetId; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + if (!pdcch_pdu_rel15) { + nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; + memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); + ul_dci_request_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE; + ul_dci_request_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu)); + pdcch_pdu_rel15 = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; + ul_dci_req->numPdus += 1; + nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp); + nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + } + + uint8_t aggregation_level; + uint8_t nr_of_candidates; + find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss); + int CCEIndex = allocate_nr_CCEs(nr_mac, bwp, coreset, aggregation_level, 0, 0, nr_of_candidates); + if (CCEIndex < 0) { + LOG_E(NR_MAC, "%s(): cannot find free CCE for RA RNTI %04x!\n", __func__, ra->rnti); + return; + } + + // Fill PDCCH DL DCI PDU + nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci]; + pdcch_pdu_rel15->numDlDci++; + dci_pdu->RNTI = ra->rnti; + dci_pdu->ScramblingId = *scc->physCellId; + dci_pdu->ScramblingRNTI = 0; + dci_pdu->AggregationLevel = aggregation_level; + dci_pdu->CceIndex = CCEIndex; + dci_pdu->beta_PDCCH_1_0 = 0; + dci_pdu->powerControlOffsetSS = 1; + + dci_pdu_rel15_t uldci_payload; + memset(&uldci_payload, 0, sizeof(uldci_payload)); + + config_uldci(ubwp, + scc, + pusch_pdu, + &uldci_payload, + NR_UL_DCI_FORMAT_0_0, + ra->Msg3_tda_id, + ra->msg3_TPC, + 0, // not used in format 0_0 + ra->bwp_id); + + fill_dci_pdu_rel15(scc, + ra->CellGroup, + dci_pdu, + &uldci_payload, + NR_UL_DCI_FORMAT_0_0, + NR_RNTI_TC, + pusch_pdu->bwp_size, + ra->bwp_id); + + // Mark the corresponding RBs as used + for (int rb = 0; rb < ra->msg3_nb_rb; rb++) { + vrb_map_UL[rbStart + bwpStart + rb] = 1; + } + + // reset state to wait msg3 + ra->state = WAIT_Msg3; + ra->Msg3_frame = sched_frame; + ra->Msg3_slot = sched_slot; + + } + +} void nr_get_Msg3alloc(module_id_t module_id, int CC_id, @@ -690,45 +879,45 @@ void nr_get_Msg3alloc(module_id_t module_id, NR_RA_t *ra, int16_t *tdd_beam_association) { - // msg3 is schedulend in mixed slot in the following TDD period + // msg3 is scheduled in mixed slot in the following TDD period - uint16_t msg3_nb_rb = 8 + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); // sdu has 6 or 8 bytes + uint16_t msg3_nb_rb = 8 + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT); // sdu has 6 or 8 bytes - int mu = ubwp ? - ubwp->bwp_Common->genericParameters.subcarrierSpacing : - scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; - int StartSymbolIndex = 0; - int NrOfSymbols = 0; - int startSymbolAndLength = 0; - int temp_slot = 0; - ra->Msg3_tda_id = 16; // initialization to a value above limit + int mu = ubwp ? + ubwp->bwp_Common->genericParameters.subcarrierSpacing : + scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; + int StartSymbolIndex = 0; + int NrOfSymbols = 0; + int startSymbolAndLength = 0; + int temp_slot = 0; + ra->Msg3_tda_id = 16; // initialization to a value above limit - NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList= ubwp ? - ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList: - scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList= ubwp ? + ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList: + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; - uint8_t k2 = 0; - for (int i=0; ilist.count; i++) { - startSymbolAndLength = pusch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; - SLIV2SL(startSymbolAndLength, &StartSymbolIndex, &NrOfSymbols); - // we want to transmit in the uplink symbols of mixed slot - if (NrOfSymbols == scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols) { - k2 = *pusch_TimeDomainAllocationList->list.array[i]->k2; - temp_slot = current_slot + k2 + DELTA[mu]; // msg3 slot according to 8.3 in 38.213 - ra->Msg3_slot = temp_slot%nr_slots_per_frame[mu]; - if (is_xlsch_in_slot(RC.nrmac[module_id]->ulsch_slot_bitmap[ra->Msg3_slot / 64], ra->Msg3_slot)) { - ra->Msg3_tda_id = i; - break; - } + uint8_t k2 = 0; + for (int i=0; ilist.count; i++) { + startSymbolAndLength = pusch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; + SLIV2SL(startSymbolAndLength, &StartSymbolIndex, &NrOfSymbols); + // we want to transmit in the uplink symbols of mixed slot + if (NrOfSymbols == scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols) { + k2 = *pusch_TimeDomainAllocationList->list.array[i]->k2; + temp_slot = current_slot + k2 + DELTA[mu]; // msg3 slot according to 8.3 in 38.213 + ra->Msg3_slot = temp_slot%nr_slots_per_frame[mu]; + if (is_xlsch_in_slot(RC.nrmac[module_id]->ulsch_slot_bitmap[ra->Msg3_slot / 64], ra->Msg3_slot)) { + ra->Msg3_tda_id = i; + break; } } + } - AssertFatal(ra->Msg3_tda_id<16,"Unable to find Msg3 time domain allocation in list\n"); + AssertFatal(ra->Msg3_tda_id<16,"Unable to find Msg3 time domain allocation in list\n"); - if (nr_slots_per_frame[mu]>temp_slot) - ra->Msg3_frame = current_frame; - else - ra->Msg3_frame = (current_frame + (temp_slot/nr_slots_per_frame[mu]))%1024; + if (nr_slots_per_frame[mu]>temp_slot) + ra->Msg3_frame = current_frame; + else + ra->Msg3_frame = (current_frame + (temp_slot/nr_slots_per_frame[mu]))%1024; // beam association for FR2 if (*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0] >= 257) { @@ -750,7 +939,7 @@ void nr_get_Msg3alloc(module_id_t module_id, scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - /* search msg3_nb_rb free RBs */ + /* search msg3_nb_rb free RBs */ int rbSize = 0; int rbStart = 0; while (rbSize < msg3_nb_rb) { @@ -768,6 +957,75 @@ void nr_get_Msg3alloc(module_id_t module_id, ra->msg3_first_rb = rbStart; } + +void fill_msg3_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_ServingCellConfigCommon_t *scc, + int round, + int startSymbolAndLength, + rnti_t rnti, int scs, + int bwp_size, int bwp_start, + int mappingtype, int fh, + int msg3_first_rb, int msg3_nb_rb) { + + + int start_symbol_index,nr_of_symbols; + SLIV2SL(startSymbolAndLength, &start_symbol_index, &nr_of_symbols); + + pusch_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; + pusch_pdu->rnti = rnti; + pusch_pdu->handle = 0; + pusch_pdu->bwp_start = bwp_start; + pusch_pdu->bwp_size = bwp_size; + pusch_pdu->subcarrier_spacing = scs; + pusch_pdu->cyclic_prefix = 0; + pusch_pdu->mcs_index = 0; + pusch_pdu->mcs_table = 0; + pusch_pdu->target_code_rate = nr_get_code_rate_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); + pusch_pdu->qam_mod_order = nr_get_Qm_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) + pusch_pdu->transform_precoding = 1; + else + pusch_pdu->transform_precoding = 0; + pusch_pdu->data_scrambling_id = *scc->physCellId; + pusch_pdu->nrOfLayers = 1; + pusch_pdu->ul_dmrs_symb_pos = get_l_prime(nr_of_symbols,mappingtype,pusch_dmrs_pos2,pusch_len1,start_symbol_index, scc->dmrs_TypeA_Position); + LOG_D(MAC, "MSG3 start_sym:%d NR Symb:%d mappingtype:%d , ul_dmrs_symb_pos:%x\n", start_symbol_index, nr_of_symbols, mappingtype, pusch_pdu->ul_dmrs_symb_pos); + pusch_pdu->dmrs_config_type = 0; + pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId; //If provided and the PUSCH is not a msg3 PUSCH, otherwise, L2 should set this to physical cell id. + pusch_pdu->scid = 0; //DMRS sequence initialization [TS38.211, sec 6.4.1.1.1]. Should match what is sent in DCI 0_1, otherwise set to 0. + pusch_pdu->dmrs_ports = 1; // 6.2.2 in 38.214 only port 0 to be used + pusch_pdu->num_dmrs_cdm_grps_no_data = 2; // no data in dmrs symbols as in 6.2.2 in 38.214 + pusch_pdu->resource_alloc = 1; //type 1 + + pusch_pdu->rb_start = msg3_first_rb; + if (msg3_nb_rb > pusch_pdu->bwp_size) + AssertFatal(1==0,"MSG3 allocated number of RBs exceed the BWP size\n"); + else + pusch_pdu->rb_size = msg3_nb_rb; + pusch_pdu->vrb_to_prb_mapping = 0; + + pusch_pdu->frequency_hopping = fh; + //pusch_pdu->tx_direct_current_location;//The uplink Tx Direct Current location for the carrier. Only values in the value range of this field between 0 and 3299, which indicate the subcarrier index within the carrier corresponding 1o the numerology of the corresponding uplink BWP and value 3300, which indicates "Outside the carrier" and value 3301, which indicates "Undetermined position within the carrier" are used. [TS38.331, UplinkTxDirectCurrentBWP IE] + pusch_pdu->uplink_frequency_shift_7p5khz = 0; + //Resource Allocation in time domain + pusch_pdu->start_symbol_index = start_symbol_index; + pusch_pdu->nr_of_symbols = nr_of_symbols; + //Optional Data only included if indicated in pduBitmap + pusch_pdu->pusch_data.rv_index = nr_rv_round_map[round]; + pusch_pdu->pusch_data.harq_process_id = 0; + pusch_pdu->pusch_data.new_data_indicator = 1; + pusch_pdu->pusch_data.num_cb = 0; + pusch_pdu->pusch_data.tb_size = nr_compute_tbs(pusch_pdu->qam_mod_order, + pusch_pdu->target_code_rate, + pusch_pdu->rb_size, + pusch_pdu->nr_of_symbols, + 12, // nb dmrs set for no data in dmrs symbol + 0, //nb_rb_oh + 0, // to verify tb scaling + pusch_pdu->nrOfLayers)>>3; + +} + void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t slotP, NR_RA_t *ra, uint8_t *RAR_pdu) { gNB_MAC_INST *mac = RC.nrmac[module_idP]; @@ -790,7 +1048,7 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t vrb_map_UL[i + ra->msg3_first_rb] = 1; } - LOG_D(NR_MAC, "[gNB %d][RAPROC] Frame %d, Subframe %d : CC_id %d RA is active, Msg3 in (%d,%d)\n", module_idP, frameP, slotP, CC_id, ra->Msg3_frame, ra->Msg3_slot); + LOG_D(NR_MAC, "[gNB %d][RAPROC] Frame %d, Slot %d : CC_id %d RA is active, Msg3 in (%d,%d)\n", module_idP, frameP, slotP, CC_id, ra->Msg3_frame, ra->Msg3_slot); nfapi_nr_ul_tti_request_t *future_ul_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[CC_id][ra->Msg3_slot]; AssertFatal(future_ul_tti_req->SFN == ra->Msg3_frame @@ -804,7 +1062,7 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pusch_pdu_t); nfapi_nr_pusch_pdu_t *pusch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pusch_pdu; memset(pusch_pdu, 0, sizeof(nfapi_nr_pusch_pdu_t)); - future_ul_tti_req->n_pdus += 1; + int ibwp_size = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); int ibwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); int abwp_size = ibwp_size; @@ -837,67 +1095,20 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t ra->msg3_round, ra->rnti); - int start_symbol_index,nr_of_symbols; - SLIV2SL(startSymbolAndLength, &start_symbol_index, &nr_of_symbols); - - pusch_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; - pusch_pdu->rnti = ra->rnti; - pusch_pdu->handle = 0; - + int bwp_start; if ((ibwp_start < abwp_start) || (ibwp_size > abwp_size)) - pusch_pdu->bwp_start = abwp_start; + bwp_start = abwp_start; else - pusch_pdu->bwp_start = ibwp_start; - pusch_pdu->bwp_size = ibwp_size; - pusch_pdu->subcarrier_spacing = scs; - pusch_pdu->cyclic_prefix = 0; - pusch_pdu->mcs_index = 0; - pusch_pdu->mcs_table = 0; - pusch_pdu->target_code_rate = nr_get_code_rate_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); - pusch_pdu->qam_mod_order = nr_get_Qm_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); - if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) - pusch_pdu->transform_precoding = 1; - else - pusch_pdu->transform_precoding = 0; - pusch_pdu->data_scrambling_id = *scc->physCellId; - pusch_pdu->nrOfLayers = 1; + bwp_start = ibwp_start; - pusch_pdu->ul_dmrs_symb_pos = get_l_prime(nr_of_symbols,mappingtype,pusch_dmrs_pos2,pusch_len1,start_symbol_index, scc->dmrs_TypeA_Position); - LOG_D(MAC, "MSG3 start_sym:%d NR Symb:%d mappingtype:%d , ul_dmrs_symb_pos:%x\n", start_symbol_index, nr_of_symbols, mappingtype, pusch_pdu->ul_dmrs_symb_pos); - - pusch_pdu->dmrs_config_type = 0; - pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId; //If provided and the PUSCH is not a msg3 PUSCH, otherwise, L2 should set this to physical cell id. - pusch_pdu->scid = 0; //DMRS sequence initialization [TS38.211, sec 6.4.1.1.1]. Should match what is sent in DCI 0_1, otherwise set to 0. - pusch_pdu->dmrs_ports = 1; // 6.2.2 in 38.214 only port 0 to be used - pusch_pdu->num_dmrs_cdm_grps_no_data = 2; // no data in dmrs symbols as in 6.2.2 in 38.214 - pusch_pdu->resource_alloc = 1; //type 1 - //pusch_pdu->rb_start = ra->msg3_first_rb + ibwp_start - abwp_start; // as for 6.3.1.7 in 38.211 - pusch_pdu->rb_start = ra->msg3_first_rb; - if (ra->msg3_nb_rb > pusch_pdu->bwp_size) - AssertFatal(1==0,"MSG3 allocated number of RBs exceed the BWP size\n"); - else - pusch_pdu->rb_size = ra->msg3_nb_rb; - pusch_pdu->vrb_to_prb_mapping = 0; - - pusch_pdu->frequency_hopping = fh; - //pusch_pdu->tx_direct_current_location;//The uplink Tx Direct Current location for the carrier. Only values in the value range of this field between 0 and 3299, which indicate the subcarrier index within the carrier corresponding 1o the numerology of the corresponding uplink BWP and value 3300, which indicates "Outside the carrier" and value 3301, which indicates "Undetermined position within the carrier" are used. [TS38.331, UplinkTxDirectCurrentBWP IE] - pusch_pdu->uplink_frequency_shift_7p5khz = 0; - //Resource Allocation in time domain - pusch_pdu->start_symbol_index = start_symbol_index; - pusch_pdu->nr_of_symbols = nr_of_symbols; - //Optional Data only included if indicated in pduBitmap - pusch_pdu->pusch_data.rv_index = 0; // 8.3 in 38.213 - pusch_pdu->pusch_data.harq_process_id = 0; - pusch_pdu->pusch_data.new_data_indicator = 1; // new data - pusch_pdu->pusch_data.num_cb = 0; - pusch_pdu->pusch_data.tb_size = nr_compute_tbs(pusch_pdu->qam_mod_order, - pusch_pdu->target_code_rate, - pusch_pdu->rb_size, - pusch_pdu->nr_of_symbols, - 12, // nb dmrs set for no data in dmrs symbol - 0, //nb_rb_oh - 0, // to verify tb scaling - pusch_pdu->nrOfLayers = 1)>>3; + fill_msg3_pusch_pdu(pusch_pdu,scc, + ra->msg3_round, + startSymbolAndLength, + ra->rnti, scs, + ibwp_size, bwp_start, + mappingtype, fh, + ra->msg3_first_rb, ra->msg3_nb_rb); + future_ul_tti_req->n_pdus += 1; // calling function to fill rar message nr_fill_rar(module_idP, ra, RAR_pdu, pusch_pdu); @@ -1479,6 +1690,9 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra } } + T(T_GNB_MAC_DL_PDU_WITH_DATA, T_INT(module_idP), T_INT(CC_id), T_INT(ra->rnti), + T_INT(frameP), T_INT(slotP), T_INT(current_harq_pid), T_BUFFER(harq->tb, harq->tb_size)); + // DL TX request nfapi_nr_pdu_t *tx_req = &nr_mac->TX_req[CC_id].pdu_list[nr_mac->TX_req[CC_id].Number_of_PDUs]; memcpy(tx_req->TLVs[0].value.direct, harq->tb, sizeof(uint8_t) * harq->tb_size); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 93d2e959dd..c5cd9ac803 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -47,6 +47,7 @@ /*Softmodem params*/ #include "executables/softmodem-common.h" +#include "../../../nfapi/oai_integration/vendor_ext.h" //////////////////////////////////////////////////////// /////* DLSCH MAC PDU generation (6.1.2 TS 38.321) *///// diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 3383dd7504..41890866c8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -607,6 +607,7 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, uint8_t tpc, int n_ubwp, int bwp_id) { + const int bw = NRRIV2BW(ubwp ? ubwp->bwp_Common->genericParameters.locationAndBandwidth : scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); @@ -1386,41 +1387,53 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, break; case NFAPI_NR_RNTI_TC: - // indicating a DL DCI format 1bit - *dci_pdu |= (dci_pdu_rel15->format_indicator & 1) << (dci_size - pos++); + // indicating a UL DCI format 1bit + pos=1; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 1) << (dci_size - pos); // Freq domain assignment max 16 bit fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1)); - for (int i = 0; i < fsize; i++) - *dci_pdu |= ((dci_pdu_rel15->frequency_domain_assignment.val >> (fsize - i - 1)) & 1) << (dci_size - pos++); + pos+=fsize; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << fsize) - 1)) << (dci_size - pos); // Time domain assignment 4bit - for (int i = 0; i < 4; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val >> (3 - i)) & 1) << (dci_size - pos++); + pos += 4; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->time_domain_assignment.val & ((1 << 4) - 1)) << (dci_size - pos); // Frequency hopping flag – 1 bit - *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val & 1) << (dci_size - pos++); + pos++; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val & 1) << (dci_size - pos); // MCS 5 bit - for (int i = 0; i < 5; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs >> (4 - i)) & 1) << (dci_size - pos++); + pos+=5; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos); // New data indicator 1bit - *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 1) << (dci_size - pos++); + pos++; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 1) << (dci_size - pos); // Redundancy version 2bit - for (int i = 0; i < 2; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv >> (1 - i)) & 1) << (dci_size - pos++); + pos+=2; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv & 0x3) << (dci_size - pos); // HARQ process number 4bit - for (int i = 0; i < 4; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->harq_pid >> (3 - i)) & 1) << (dci_size - pos++); - // TPC command for scheduled PUSCH – 2 bits - for (int i = 0; i < 2; i++) - *dci_pdu |= (((uint64_t)dci_pdu_rel15->tpc >> (1 - i)) & 1) << (dci_size - pos++); + pos+=4; + *dci_pdu |= ((uint64_t)dci_pdu_rel15->harq_pid & 0xf) << (dci_size - pos); // Padding bits for (int a = pos; a < 32; a++) *dci_pdu |= ((uint64_t)dci_pdu_rel15->padding & 1) << (dci_size - pos++); // UL/SUL indicator – 1 bit - /* - commented for now (RK): need to get this information from BWP descriptor - if (cfg->pucch_config.pucch_GroupHopping.value) + /* commented for now (RK): need to get this from BWP descriptor + if (cfg->pucch_config.pucch_GroupHopping.value) *dci_pdu |= - ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos++); + ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos++); */ + LOG_D(NR_MAC,"N_RB = %i\n", N_RB); + LOG_D(NR_MAC,"dci_size = %i\n", dci_size); + LOG_D(NR_MAC,"fsize = %i\n", fsize); + LOG_D(NR_MAC,"dci_pdu_rel15->frequency_domain_assignment.val = %i\n", dci_pdu_rel15->frequency_domain_assignment.val); + LOG_D(NR_MAC,"dci_pdu_rel15->time_domain_assignment.val = %i\n", dci_pdu_rel15->time_domain_assignment.val); + LOG_D(NR_MAC,"dci_pdu_rel15->frequency_hopping_flag.val = %i\n", dci_pdu_rel15->frequency_hopping_flag.val); + LOG_D(NR_MAC,"dci_pdu_rel15->mcs = %i\n", dci_pdu_rel15->mcs); + LOG_D(NR_MAC,"dci_pdu_rel15->ndi = %i\n", dci_pdu_rel15->ndi); + LOG_D(NR_MAC,"dci_pdu_rel15->rv = %i\n", dci_pdu_rel15->rv); + LOG_D(NR_MAC,"dci_pdu_rel15->harq_pid = %i\n", dci_pdu_rel15->harq_pid); + LOG_D(NR_MAC,"dci_pdu_rel15->tpc = %i\n", dci_pdu_rel15->tpc); + LOG_D(NR_MAC,"dci_pdu_rel15->padding = %i\n", dci_pdu_rel15->padding); + break; } break; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index fbd8a4b5ca..c854dd7b2e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -174,60 +174,63 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub tdd->nrofUplinkSlots); } +// For both UL-SCH except: +// - UL-SCH: fixed-size MAC CE(known by LCID) +// - UL-SCH: padding +// - UL-SCH: MSG3 48-bits +// |0|1|2|3|4|5|6|7| bit-wise +// |R|F| LCID | +// | L | +// |0|1|2|3|4|5|6|7| bit-wise +// |R|F| LCID | +// | L | +// | L | +// +// For: +// - UL-SCH: fixed-size MAC CE(known by LCID) +// - UL-SCH: padding, for single/multiple 1-oct padding CE(s) +// - UL-SCH: MSG3 48-bits +// |0|1|2|3|4|5|6|7| bit-wise +// |R|R| LCID | +// +// LCID: The Logical Channel ID field identifies the logical channel instance of the corresponding MAC SDU or the type of the corresponding MAC CE or padding as described in Tables 6.2.1-1 and 6.2.1-2 for the DL-SCH and UL-SCH respectively. There is one LCID field per MAC subheader. The LCID field size is 6 bits; +// L: The Length field indicates the length of the corresponding MAC SDU or variable-sized MAC CE in bytes. There is one L field per MAC subheader except for subheaders corresponding to fixed-sized MAC CEs and padding. The size of the L field is indicated by the F field; +// F: length of L is 0:8 or 1:16 bits wide +// R: Reserved bit, set to zero. + void nr_process_mac_pdu(module_id_t module_idP, int UE_id, uint8_t CC_id, frame_t frameP, sub_frame_t slot, uint8_t *pduP, - uint16_t mac_pdu_len) + int pdu_len) { - // This function is adapting code from the old - // parse_header(...) and ue_send_sdu(...) functions of OAI LTE - - uint8_t *pdu_ptr = pduP, rx_lcid, done = 0; - int pdu_len = mac_pdu_len; - uint16_t mac_ce_len, mac_subheader_len, mac_sdu_len; + uint8_t rx_lcid; + uint8_t done = 0; + uint16_t mac_ce_len; + uint16_t mac_subheader_len; + uint16_t mac_sdu_len; NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; if ( pduP[0] != UL_SCH_LCID_PADDING ) - trace_NRpdu(DIRECTION_UPLINK, pduP, mac_pdu_len ,UE_id, WS_C_RNTI, UE_info->rnti[UE_id], frameP, 0, 0, 0); + trace_NRpdu(DIRECTION_UPLINK, pduP, pdu_len, UE_id, WS_C_RNTI, UE_info->rnti[UE_id], frameP, 0, 0, 0); - // For both DL/UL-SCH - // Except: - // - UL/DL-SCH: fixed-size MAC CE(known by LCID) - // - UL/DL-SCH: padding - // - UL-SCH: MSG3 48-bits - // |0|1|2|3|4|5|6|7| bit-wise - // |R|F| LCID | - // | L | - // |0|1|2|3|4|5|6|7| bit-wise - // |R|F| LCID | - // | L | - // | L | - - // For both DL/UL-SCH - // For: - // - UL/DL-SCH: fixed-size MAC CE(known by LCID) - // - UL/DL-SCH: padding, for single/multiple 1-oct padding CE(s) - // - UL-SCH: MSG3 48-bits - // |0|1|2|3|4|5|6|7| bit-wise - // |R|R| LCID | - // LCID: The Logical Channel ID field identifies the logical channel instance of the corresponding MAC SDU or the type of the corresponding MAC CE or padding as described in Tables 6.2.1-1 and 6.2.1-2 for the DL-SCH and UL-SCH respectively. There is one LCID field per MAC subheader. The LCID field size is 6 bits; - // L: The Length field indicates the length of the corresponding MAC SDU or variable-sized MAC CE in bytes. There is one L field per MAC subheader except for subheaders corresponding to fixed-sized MAC CEs and padding. The size of the L field is indicated by the F field; - // F: lenght of L is 0:8 or 1:16 bits wide - // R: Reserved bit, set to zero. + #ifdef ENABLE_MAC_PAYLOAD_DEBUG + LOG_I(NR_MAC, "In %s: dumping MAC PDU in %d.%d:\n", __func__, frameP, slot); + log_dump(NR_MAC, pduP, pdu_len, LOG_DUMP_CHAR, "\n"); + #endif while (!done && pdu_len > 0){ mac_ce_len = 0; - mac_subheader_len = 1; // default to fixed-length subheader = 1-oct + mac_subheader_len = sizeof(NR_MAC_SUBHEADER_FIXED); mac_sdu_len = 0; - rx_lcid = ((NR_MAC_SUBHEADER_FIXED *)pdu_ptr)->LCID; + rx_lcid = ((NR_MAC_SUBHEADER_FIXED *)pduP)->LCID; - LOG_D(NR_MAC, "LCID received at gNB side: %d \n", rx_lcid); + LOG_D(NR_MAC, "In %s: received UL-SCH sub-PDU with LCID 0x%x in %d.%d (remaining PDU length %d)\n", __func__, rx_lcid, frameP, slot, pdu_len); unsigned char *ce_ptr; int n_Lcg = 0; @@ -236,7 +239,7 @@ void nr_process_mac_pdu(module_id_t module_idP, // MAC CE /*#ifdef DEBUG_HEADER_PARSING - LOG_D(NR_MAC, "[UE] LCID %d, PDU length %d\n", ((NR_MAC_SUBHEADER_FIXED *)pdu_ptr)->LCID, pdu_len); + LOG_D(NR_MAC, "[UE] LCID %d, PDU length %d\n", ((NR_MAC_SUBHEADER_FIXED *)pduP)->LCID, pdu_len); #endif*/ case UL_SCH_LCID_RECOMMENDED_BITRATE_QUERY: // 38.321 Ch6.1.3.20 @@ -252,7 +255,7 @@ void nr_process_mac_pdu(module_id_t module_idP, //fixed length mac_ce_len =1; /* Extract short BSR value */ - ce_ptr = &pdu_ptr[mac_subheader_len]; + ce_ptr = &pduP[mac_subheader_len]; NR_BSR_SHORT *bsr_s = (NR_BSR_SHORT *) ce_ptr; sched_ctrl->estimated_ul_buffer = 0; sched_ctrl->estimated_ul_buffer = NR_SHORT_BSR_TABLE[bsr_s->Buffer_size]; @@ -270,14 +273,14 @@ void nr_process_mac_pdu(module_id_t module_idP, case UL_SCH_LCID_L_TRUNCATED_BSR: //38.321 section 6.1.3.1 //variable length - mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L; + mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pduP)->L; mac_subheader_len = 2; - if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){ - mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8; + if(((NR_MAC_SUBHEADER_SHORT *)pduP)->F){ + mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pduP)->L2)<<8; mac_subheader_len = 3; } /* Extract long BSR value */ - ce_ptr = &pdu_ptr[mac_subheader_len]; + ce_ptr = &pduP[mac_subheader_len]; NR_BSR_LONG *bsr_l = (NR_BSR_LONG *) ce_ptr; sched_ctrl->estimated_ul_buffer = 0; @@ -290,18 +293,18 @@ void nr_process_mac_pdu(module_id_t module_idP, for (int n = 0; n < n_Lcg; n++){ LOG_D(NR_MAC, "LONG BSR, %d/%d (n/n_Lcg), BS Index %d, BS value < %d", - n, n_Lcg, pdu_ptr[mac_subheader_len + 1 + n], - NR_LONG_BSR_TABLE[pdu_ptr[mac_subheader_len + 1 + n]]); + n, n_Lcg, pduP[mac_subheader_len + 1 + n], + NR_LONG_BSR_TABLE[pduP[mac_subheader_len + 1 + n]]); sched_ctrl->estimated_ul_buffer += - NR_LONG_BSR_TABLE[pdu_ptr[mac_subheader_len + 1 + n]]; + NR_LONG_BSR_TABLE[pduP[mac_subheader_len + 1 + n]]; LOG_D(NR_MAC, "LONG BSR at %4d.%2d, %d/%d (n/n_Lcg), BS Index %d, BS value < %d, total %d\n", frameP, slot, n, n_Lcg, - pdu_ptr[mac_subheader_len + 1 + n], - NR_LONG_BSR_TABLE[pdu_ptr[mac_subheader_len + 1 + n]], + pduP[mac_subheader_len + 1 + n], + NR_LONG_BSR_TABLE[pduP[mac_subheader_len + 1 + n]], sched_ctrl->estimated_ul_buffer); } @@ -319,7 +322,7 @@ void nr_process_mac_pdu(module_id_t module_idP, //fixed length mac_ce_len = 2; /* Extract SINGLE ENTRY PHR elements for PHR calculation */ - ce_ptr = &pdu_ptr[mac_subheader_len]; + ce_ptr = &pduP[mac_subheader_len]; NR_SINGLE_ENTRY_PHR_MAC_CE *phr = (NR_SINGLE_ENTRY_PHR_MAC_CE *) ce_ptr; /* Save the phr info */ const int PH = phr->PH; @@ -338,10 +341,10 @@ void nr_process_mac_pdu(module_id_t module_idP, case UL_SCH_LCID_MULTI_ENTRY_PHR_1_OCT: //38.321 section 6.1.3.9 // varialbe length - mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L; + mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pduP)->L; mac_subheader_len = 2; - if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){ - mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8; + if(((NR_MAC_SUBHEADER_SHORT *)pduP)->F){ + mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pduP)->L2)<<8; mac_subheader_len = 3; } /* Extract MULTI ENTRY PHR elements from single octet bitmap for PHR calculation */ @@ -350,10 +353,10 @@ void nr_process_mac_pdu(module_id_t module_idP, case UL_SCH_LCID_MULTI_ENTRY_PHR_4_OCT: //38.321 section 6.1.3.9 // varialbe length - mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L; + mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pduP)->L; mac_subheader_len = 2; - if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){ - mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8; + if(((NR_MAC_SUBHEADER_SHORT *)pduP)->F){ + mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pduP)->L2)<<8; mac_subheader_len = 3; } /* Extract MULTI ENTRY PHR elements from four octets bitmap for PHR calculation */ @@ -366,13 +369,13 @@ void nr_process_mac_pdu(module_id_t module_idP, case UL_SCH_LCID_SRB1: case UL_SCH_LCID_SRB2: - if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){ - //mac_sdu_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8; + if(((NR_MAC_SUBHEADER_SHORT *)pduP)->F){ + //mac_sdu_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pduP)->L2)<<8; mac_subheader_len = 3; - mac_sdu_len = ((uint16_t)(((NR_MAC_SUBHEADER_LONG *) pdu_ptr)->L1 & 0x7f) << 8) - | ((uint16_t)((NR_MAC_SUBHEADER_LONG *) pdu_ptr)->L2 & 0xff); + mac_sdu_len = ((uint16_t)(((NR_MAC_SUBHEADER_LONG *) pduP)->L1 & 0x7f) << 8) + | ((uint16_t)((NR_MAC_SUBHEADER_LONG *) pduP)->L2 & 0xff); } else { - mac_sdu_len = (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L; + mac_sdu_len = (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pduP)->L; mac_subheader_len = 2; } LOG_D(NR_MAC, "[UE %d] Frame %d : ULSCH -> UL-DCCH %d (gNB %d, %d bytes), rnti: %d \n", module_idP, frameP, rx_lcid, module_idP, mac_sdu_len, UE_info->rnti[UE_id]); @@ -383,7 +386,7 @@ void nr_process_mac_pdu(module_id_t module_idP, ENB_FLAG_YES, MBMS_FLAG_NO, rx_lcid, - (char *) (pdu_ptr + mac_subheader_len), + (char *) (pduP + mac_subheader_len), mac_sdu_len, 1, NULL); @@ -404,7 +407,7 @@ void nr_process_mac_pdu(module_id_t module_idP, // Check if it is a valid CCCH1 message, we get all 00's messages very often int i = 0; for(i=0; i<(mac_subheader_len+mac_sdu_len); i++) { - if(pdu_ptr[i] != 0) { + if(pduP[i] != 0) { break; } } @@ -425,35 +428,34 @@ void nr_process_mac_pdu(module_id_t module_idP, 0, UE_info->rnti[UE_id], CCCH, - pdu_ptr+mac_subheader_len, + pduP + mac_subheader_len, mac_sdu_len, 0); break; case UL_SCH_LCID_DTCH: // check if LCID is valid at current time. - if (((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F) { - // mac_sdu_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8; + if (((NR_MAC_SUBHEADER_SHORT *)pduP)->F) { + // mac_sdu_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pduP)->L2)<<8; mac_subheader_len = 3; - mac_sdu_len = ((uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L1 & 0x7f) << 8) - | ((uint16_t)((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2 & 0xff); + mac_sdu_len = ((uint16_t)(((NR_MAC_SUBHEADER_LONG *)pduP)->L1 & 0x7f) << 8) + | ((uint16_t)((NR_MAC_SUBHEADER_LONG *)pduP)->L2 & 0xff); } else { - mac_sdu_len = (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L; + mac_sdu_len = (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pduP)->L; mac_subheader_len = 2; } - LOG_D(NR_MAC, "[UE %d] Frame %d : ULSCH -> UL-%s %d (gNB %d, %d bytes)\n", + LOG_D(NR_MAC, "In %s: [UE %d] %d.%d : ULSCH -> UL-%s %d (gNB %d, %d bytes)\n", + __func__, module_idP, frameP, + slot, rx_lcid<4?"DCCH":"DTCH", rx_lcid, module_idP, mac_sdu_len); UE_info->mac_stats[UE_id].lc_bytes_rx[rx_lcid] += mac_sdu_len; -#if defined(ENABLE_MAC_PAYLOAD_DEBUG) - log_dump(NR_MAC, pdu_ptr + mac_subheader_len, 32, LOG_DUMP_CHAR, "\n"); -#endif mac_rlc_data_ind(module_idP, UE_info->rnti[UE_id], @@ -462,7 +464,7 @@ void nr_process_mac_pdu(module_id_t module_idP, ENB_FLAG_YES, MBMS_FLAG_NO, rx_lcid, - (char *)(pdu_ptr + mac_subheader_len), + (char *)(pduP + mac_subheader_len), mac_sdu_len, 1, NULL); @@ -479,14 +481,27 @@ void nr_process_mac_pdu(module_id_t module_idP, return; break; } - pdu_ptr += ( mac_subheader_len + mac_ce_len + mac_sdu_len ); + + #ifdef ENABLE_MAC_PAYLOAD_DEBUG + if (rx_lcid < 45 || rx_lcid == 52 || rx_lcid == 63) { + LOG_I(NR_MAC, "In %s: dumping UL MAC SDU sub-header with length %d (LCID = 0x%02x):\n", __func__, mac_subheader_len, rx_lcid); + log_dump(NR_MAC, pduP, mac_subheader_len, LOG_DUMP_CHAR, "\n"); + LOG_I(NR_MAC, "In %s: dumping UL MAC SDU with length %d (LCID = 0x%02x):\n", __func__, mac_sdu_len, rx_lcid); + log_dump(NR_MAC, pduP + mac_subheader_len, mac_sdu_len, LOG_DUMP_CHAR, "\n"); + } else { + LOG_I(NR_MAC, "In %s: dumping UL MAC CE with length %d (LCID = 0x%02x):\n", __func__, mac_ce_len, rx_lcid); + log_dump(NR_MAC, pduP + mac_subheader_len + mac_sdu_len, mac_ce_len, LOG_DUMP_CHAR, "\n"); + } + #endif + + pduP += ( mac_subheader_len + mac_ce_len + mac_sdu_len ); pdu_len -= ( mac_subheader_len + mac_ce_len + mac_sdu_len ); if (pdu_len < 0) { - LOG_E(NR_MAC, "%s() residual mac pdu length < 0!, pdu_len: %d\n", __func__, pdu_len); + LOG_E(NR_MAC, "In %s: residual UL MAC PDU in %d.%d with length < 0!, pdu_len %d \n", __func__, frameP, slot, pdu_len); LOG_E(NR_MAC, "MAC PDU "); for (int i = 0; i < 20; i++) // Only printf 1st - 20nd bytes - printf("%02x ", pdu_ptr[i]); + printf("%02x ", pduP[i]); printf("\n"); return; } @@ -511,19 +526,26 @@ void abort_nr_ul_harq(module_id_t mod_id, int UE_id, int8_t harq_pid) sched_ctrl->sched_ul_bytes = 0; } -void handle_nr_ul_harq(module_id_t mod_id, +void handle_nr_ul_harq(const int CC_idP, + module_id_t mod_id, frame_t frame, sub_frame_t slot, const nfapi_nr_crc_t *crc_pdu) { + gNB_MAC_INST *gNB_mac = RC.nrmac[mod_id]; int UE_id = find_nr_UE_id(mod_id, crc_pdu->rnti); if (UE_id < 0) { + for (int i = 0; i < NR_NB_RA_PROC_MAX; ++i) { + NR_RA_t *ra = &gNB_mac->common_channels[CC_idP].ra[i]; + if (ra->state >= WAIT_Msg3 && + ra->rnti == crc_pdu->rnti) + return; + } LOG_E(NR_MAC, "%s(): unknown RNTI %04x in PUSCH\n", __func__, crc_pdu->rnti); return; } NR_UE_info_t *UE_info = &RC.nrmac[mod_id]->UE_info; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - int8_t harq_pid = sched_ctrl->feedback_ul_harq.head; while (crc_pdu->harq_id != harq_pid || harq_pid < 0) { LOG_W(NR_MAC, @@ -586,6 +608,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, const uint16_t timing_advance, const uint8_t ul_cqi, const uint16_t rssi){ + gNB_MAC_INST *gNB_mac = RC.nrmac[gnb_mod_idP]; NR_UE_info_t *UE_info = &gNB_mac->UE_info; @@ -784,9 +807,19 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, if (ra->state != WAIT_Msg3) continue; - LOG_W(NR_MAC, "Random Access %i failed at state %i (state is not WAIT_Msg3)\n", i, ra->state); - nr_mac_remove_ra_rnti(gnb_mod_idP, ra->rnti); - nr_clear_ra_proc(gnb_mod_idP, CC_idP, frameP, ra); + if( (frameP!=ra->Msg3_frame) || (slotP!=ra->Msg3_slot)) + continue; + + if (ra->msg3_round >= MAX_HARQ_ROUNDS - 1) { + LOG_W(NR_MAC, "Random Access %i failed at state %i (Reached msg3 max harq rounds)\n", i, ra->state); + nr_mac_remove_ra_rnti(gnb_mod_idP, ra->rnti); + nr_clear_ra_proc(gnb_mod_idP, CC_idP, frameP, ra); + return; + } + + LOG_W(NR_MAC, "Random Access %i Msg3 CRC did not pass)\n", i); + ra->msg3_round++; + ra->state = Msg3_retransmission; } } } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 7af01850ae..83de1ba49b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -124,6 +124,8 @@ void nr_get_Msg3alloc(module_id_t module_id, NR_RA_t *ra, int16_t *tdd_beam_association); +void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t slotP, NR_RA_t *ra); + /* \brief Function in gNB to fill RAR pdu when requested by PHY. @param ra Instance of RA resources of gNB @param dlsch_buffer Pointer to RAR input buffer @@ -134,6 +136,15 @@ void nr_fill_rar(uint8_t Mod_idP, uint8_t * dlsch_buffer, nfapi_nr_pusch_pdu_t *pusch_pdu); +void fill_msg3_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_ServingCellConfigCommon_t *scc, + int round, + int startSymbolAndLength, + rnti_t rnti, int scs, + int bwp_size, int bwp_start, + int mappingtype, int fh, + int msg3_first_rb, int msg3_nb_rb); + void schedule_nr_prach(module_id_t module_idP, frame_t frameP, sub_frame_t slotP); @@ -388,7 +399,8 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, const uint8_t ul_cqi, const uint16_t rssi); -void handle_nr_ul_harq(module_id_t mod_id, +void handle_nr_ul_harq(const int CC_idP, + module_id_t mod_id, frame_t frame, sub_frame_t slot, const nfapi_nr_crc_t *crc_pdu); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 56b8566f22..5d546a57c9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -93,8 +93,9 @@ typedef enum { RA_IDLE = 0, Msg2 = 1, WAIT_Msg3 = 2, - Msg4 = 3, - WAIT_Msg4_ACK = 4 + Msg3_retransmission = 3, + Msg4 = 4, + WAIT_Msg4_ACK = 5 } RA_gNB_state_t; typedef struct NR_preamble_ue { diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index e196b56f1c..2d28343f9b 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -208,7 +208,10 @@ tbs_size_t mac_rlc_data_req( switch (channel_idP) { case 1 ... 3: rb = ue->srb[channel_idP - 1]; break; case 4 ... 8: rb = ue->drb[channel_idP - 4]; break; - default: rb = NULL; break; + default: + rb = NULL; + LOG_E(RLC, "In %s:%d:%s: data request for unknown RB with LCID 0x%02x !\n", __FILE__, __LINE__, __FUNCTION__, channel_idP); + break; } if (rb != NULL) { @@ -216,10 +219,6 @@ tbs_size_t mac_rlc_data_req( maxsize = tb_sizeP; ret = rb->generate_pdu(rb, buffer_pP, maxsize); } else { - // Laurent: the query loop was checking all possible RB, but by mac_rlc_get_buffer_occupancy_ind - // so it is more straitforward to try to get data - //LOG_E(RLC, "%s:%d:%s: fatal: data req for unknown RB, channel_idP: %d\n", __FILE__, __LINE__, __FUNCTION__, channel_idP); - //exit(1); ret = 0; } diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index fa76fd00bc..cb5bef5ab0 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -48,59 +48,83 @@ extern int oai_nfapi_crc_indication(nfapi_crc_indication_t *crc_ind); extern int oai_nfapi_cqi_indication(nfapi_cqi_indication_t *cqi_ind); extern int oai_nfapi_sr_indication(nfapi_sr_indication_t *ind); extern int oai_nfapi_rx_ind(nfapi_rx_indication_t *ind); +extern int oai_nfapi_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind); +extern int oai_nfapi_nr_rx_data_indication(nfapi_nr_rx_data_indication_t *ind); +extern int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind); +extern int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind); +extern int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind); +extern int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind); extern uint8_t nfapi_mode; extern uint16_t sf_ahead; extern uint16_t sl_ahead; void handle_nr_rach(NR_UL_IND_t *UL_info) { - - if (UL_info->rach_ind.number_of_pdus>0) { - LOG_D(MAC,"UL_info[Frame %d, Slot %d] Calling initiate_ra_proc RACH:SFN/SLOT:%d/%d\n",UL_info->frame,UL_info->slot, UL_info->rach_ind.sfn,UL_info->rach_ind.slot); - int npdus = UL_info->rach_ind.number_of_pdus; - for(int i = 0; i < npdus; i++) { - UL_info->rach_ind.number_of_pdus--; - if (UL_info->rach_ind.pdu_list[i].num_preamble>0) - AssertFatal(UL_info->rach_ind.pdu_list[i].num_preamble==1, - "More than 1 preamble not supported\n"); - - nr_initiate_ra_proc(UL_info->module_id, - UL_info->CC_id, - UL_info->rach_ind.sfn, - UL_info->rach_ind.slot, - UL_info->rach_ind.pdu_list[i].preamble_list[0].preamble_index, - UL_info->rach_ind.pdu_list[i].freq_index, - UL_info->rach_ind.pdu_list[i].symbol_index, - UL_info->rach_ind.pdu_list[i].preamble_list[0].timing_advance); + if(NFAPI_MODE == NFAPI_MODE_PNF) { + if (UL_info->rach_ind.number_of_pdus>0) { + LOG_D(PHY,"UL_info->UL_info->rach_ind.number_of_pdus:%d SFN/Slot:%d.%d \n", UL_info->rach_ind.number_of_pdus, UL_info->rach_ind.sfn,UL_info->rach_ind.slot); + oai_nfapi_nr_rach_indication(&UL_info->rach_ind); + UL_info->rach_ind.number_of_pdus = 0; + } + } + else{ + if (UL_info->rach_ind.number_of_pdus>0) { + LOG_D(MAC,"UL_info[Frame %d, Slot %d] Calling initiate_ra_proc RACH:SFN/SLOT:%d/%d\n",UL_info->frame,UL_info->slot, UL_info->rach_ind.sfn,UL_info->rach_ind.slot); + int npdus = UL_info->rach_ind.number_of_pdus; + for(int i = 0; i < npdus; i++) { + UL_info->rach_ind.number_of_pdus--; + if (UL_info->rach_ind.pdu_list[i].num_preamble>0) + AssertFatal(UL_info->rach_ind.pdu_list[i].num_preamble==1, + "More than 1 preamble not supported\n"); + + nr_initiate_ra_proc(UL_info->module_id, + UL_info->CC_id, + UL_info->rach_ind.sfn, + UL_info->rach_ind.slot, + UL_info->rach_ind.pdu_list[i].preamble_list[0].preamble_index, + UL_info->rach_ind.pdu_list[i].freq_index, + UL_info->rach_ind.pdu_list[i].symbol_index, + UL_info->rach_ind.pdu_list[i].preamble_list[0].timing_advance); + } } } } void handle_nr_uci(NR_UL_IND_t *UL_info) -{ - const module_id_t mod_id = UL_info->module_id; - const frame_t frame = UL_info->frame; - const sub_frame_t slot = UL_info->slot; - int num_ucis = UL_info->uci_ind.num_ucis; - nfapi_nr_uci_t *uci_list = UL_info->uci_ind.uci_list; +{ + if(NFAPI_MODE == NFAPI_MODE_PNF) { + if (UL_info->uci_ind.num_ucis>0) { + LOG_D(PHY,"PNF Sending UL_info->num_ucis:%d PDU_type: %d, SFN/SF:%d.%d \n", UL_info->uci_ind.num_ucis, UL_info->uci_ind.uci_list[0].pdu_type ,UL_info->frame, UL_info->slot); + oai_nfapi_nr_uci_indication(&UL_info->uci_ind); + UL_info->uci_ind.num_ucis = 0; + } + } + else{ + const module_id_t mod_id = UL_info->module_id; + const frame_t frame = UL_info->frame; + const sub_frame_t slot = UL_info->slot; + int num_ucis = UL_info->uci_ind.num_ucis; + nfapi_nr_uci_t *uci_list = UL_info->uci_ind.uci_list; + LOG_D(MAC,"handling UCI SFN/slot: %d.%d, num_ucis: %d \n", frame,slot, num_ucis); + for (int i = 0; i < num_ucis; i++) { + switch (uci_list[i].pdu_type) { + case NFAPI_NR_UCI_PUSCH_PDU_TYPE: + LOG_E(MAC, "%s(): unhandled NFAPI_NR_UCI_PUSCH_PDU_TYPE\n", __func__); + break; - for (int i = 0; i < num_ucis; i++) { - switch (uci_list[i].pdu_type) { - case NFAPI_NR_UCI_PUSCH_PDU_TYPE: - LOG_E(MAC, "%s(): unhandled NFAPI_NR_UCI_PUSCH_PDU_TYPE\n", __func__); - break; + case NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE: { + const nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu = &uci_list[i].pucch_pdu_format_0_1; + handle_nr_uci_pucch_0_1(mod_id, frame, slot, uci_pdu); + break; + } - case NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE: { - const nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu = &uci_list[i].pucch_pdu_format_0_1; - handle_nr_uci_pucch_0_1(mod_id, frame, slot, uci_pdu); - break; - } - - case NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE: { - const nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu = &uci_list[i].pucch_pdu_format_2_3_4; - handle_nr_uci_pucch_2_3_4(mod_id, frame, slot, uci_pdu); - break; + case NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE: { + const nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu = &uci_list[i].pucch_pdu_format_2_3_4; + handle_nr_uci_pucch_2_3_4(mod_id, frame, slot, uci_pdu); + break; + } } + LOG_D(MAC, "UCI handled \n"); } } @@ -109,60 +133,75 @@ void handle_nr_uci(NR_UL_IND_t *UL_info) void handle_nr_ulsch(NR_UL_IND_t *UL_info) { - if (UL_info->rx_ind.number_of_pdus > 0 && UL_info->crc_ind.number_crcs > 0) { - for (int i = 0; i < UL_info->rx_ind.number_of_pdus; i++) { - for (int j = 0; j < UL_info->crc_ind.number_crcs; j++) { - // find crc_indication j corresponding rx_indication i - const nfapi_nr_rx_data_pdu_t *rx = &UL_info->rx_ind.pdu_list[i]; - const nfapi_nr_crc_t *crc = &UL_info->crc_ind.crc_list[j]; - LOG_D(PHY, - "UL_info->crc_ind.pdu_list[%d].rnti:%04x " - "UL_info->rx_ind.pdu_list[%d].rnti:%04x\n", - j, - crc->rnti, - i, - rx->rnti); + if(NFAPI_MODE == NFAPI_MODE_PNF) { + if (UL_info->crc_ind.number_crcs>0) { + LOG_D(PHY,"UL_info->UL_info->crc_ind.number_crcs:%d CRC_IND:SFN/Slot:%d.%d\n", UL_info->crc_ind.number_crcs, UL_info->crc_ind.sfn, UL_info->crc_ind.slot); + oai_nfapi_nr_crc_indication(&UL_info->crc_ind); + UL_info->crc_ind.number_crcs = 0; + } - if (crc->rnti != rx->rnti) - continue; + if (UL_info->rx_ind.number_of_pdus>0) { + LOG_D(PHY,"UL_info->rx_ind.number_of_pdus:%d RX_IND:SFN/Slot:%d.%d \n", UL_info->rx_ind.number_of_pdus, UL_info->rx_ind.sfn, UL_info->rx_ind.slot); + oai_nfapi_nr_rx_data_indication(&UL_info->rx_ind); + UL_info->rx_ind.number_of_pdus = 0; + } + } + else{ + if (UL_info->rx_ind.number_of_pdus > 0 && UL_info->crc_ind.number_crcs > 0) { + for (int i = 0; i < UL_info->rx_ind.number_of_pdus; i++) { + for (int j = 0; j < UL_info->crc_ind.number_crcs; j++) { + // find crc_indication j corresponding rx_indication i + const nfapi_nr_rx_data_pdu_t *rx = &UL_info->rx_ind.pdu_list[i]; + const nfapi_nr_crc_t *crc = &UL_info->crc_ind.crc_list[j]; + LOG_D(PHY, + "UL_info->crc_ind.pdu_list[%d].rnti:%04x " + "UL_info->rx_ind.pdu_list[%d].rnti:%04x\n", + j, + crc->rnti, + i, + rx->rnti); - LOG_D(MAC, - "%4d.%2d Calling rx_sdu (CRC %s/tb_crc_status %d)\n", - UL_info->frame, - UL_info->slot, - crc->tb_crc_status ? "error" : "ok", - crc->tb_crc_status); + if (crc->rnti != rx->rnti) + continue; - /* if CRC passes, pass PDU, otherwise pass NULL as error indication */ - nr_rx_sdu(UL_info->module_id, - UL_info->CC_id, - UL_info->rx_ind.sfn, - UL_info->rx_ind.slot, - rx->rnti, - crc->tb_crc_status ? NULL : rx->pdu, - rx->pdu_length, - rx->timing_advance, - rx->ul_cqi, - rx->rssi); - handle_nr_ul_harq(UL_info->module_id, UL_info->frame, UL_info->slot, crc); - break; - } // for (j=0;jcrc_ind.number_crcs;j++) - } // for (i=0;irx_ind.number_of_pdus;i++) + LOG_D(MAC, + "%4d.%2d Calling rx_sdu (CRC %s/tb_crc_status %d)\n", + UL_info->frame, + UL_info->slot, + crc->tb_crc_status ? "error" : "ok", + crc->tb_crc_status); - UL_info->crc_ind.number_crcs = 0; - UL_info->rx_ind.number_of_pdus = 0; - } else if (UL_info->rx_ind.number_of_pdus != 0 - || UL_info->crc_ind.number_crcs != 0) { - LOG_E(PHY, - "hoping not to have mis-match between CRC ind and RX ind - " - "hopefully the missing message is coming shortly " - "rx_ind:%d(SFN/SL:%d/%d) crc_ind:%d(SFN/SL:%d/%d) \n", - UL_info->rx_ind.number_of_pdus, - UL_info->rx_ind.sfn, - UL_info->rx_ind.slot, - UL_info->crc_ind.number_crcs, - UL_info->rx_ind.sfn, - UL_info->rx_ind.slot); + /* if CRC passes, pass PDU, otherwise pass NULL as error indication */ + nr_rx_sdu(UL_info->module_id, + UL_info->CC_id, + UL_info->rx_ind.sfn, + UL_info->rx_ind.slot, + rx->rnti, + crc->tb_crc_status ? NULL : rx->pdu, + rx->pdu_length, + rx->timing_advance, + rx->ul_cqi, + rx->rssi); + handle_nr_ul_harq(UL_info->CC_id, UL_info->module_id, UL_info->frame, UL_info->slot, crc); + break; + } // for (j=0;jcrc_ind.number_crcs;j++) + } // for (i=0;irx_ind.number_of_pdus;i++) + + UL_info->crc_ind.number_crcs = 0; + UL_info->rx_ind.number_of_pdus = 0; + } else if (UL_info->rx_ind.number_of_pdus != 0 + || UL_info->crc_ind.number_crcs != 0) { + LOG_E(PHY, + "hoping not to have mis-match between CRC ind and RX ind - " + "hopefully the missing message is coming shortly " + "rx_ind:%d(SFN/SL:%d/%d) crc_ind:%d(SFN/SL:%d/%d) \n", + UL_info->rx_ind.number_of_pdus, + UL_info->rx_ind.sfn, + UL_info->rx_ind.slot, + UL_info->crc_ind.number_crcs, + UL_info->rx_ind.sfn, + UL_info->rx_ind.slot); + } } } @@ -181,7 +220,15 @@ void NR_UL_indication(NR_UL_IND_t *UL_info) { module_id,CC_id, UL_info->rach_ind.number_of_pdus, UL_info->rx_ind.number_of_pdus, UL_info->crc_ind.number_crcs); + handle_nr_rach(UL_info); + + handle_nr_uci(UL_info); + // clear UL DCI prior to handling ULSCH + mac->UL_dci_req[CC_id].numPdus = 0; + handle_nr_ulsch(UL_info); + if (NFAPI_MODE != NFAPI_MODE_PNF) { + if (ifi->CC_mask==0) { ifi->current_frame = UL_info->frame; ifi->current_slot = UL_info->slot; @@ -191,16 +238,7 @@ void NR_UL_indication(NR_UL_IND_t *UL_info) { } ifi->CC_mask |= (1<UL_dci_req[CC_id].numPdus = 0; - handle_nr_ulsch(UL_info); - - if (NFAPI_MODE != NFAPI_MODE_PNF) { if (ifi->CC_mask == ((1<) { syswrite OUTPUT_FILE,"END\n\n"; - while() { - if( m/NR-UE-Variables DEFINITIONS AUTOMATIC TAGS ::=/){ - - syswrite OUTPUT_FILE,"$_ \n"; - syswrite OUTPUT_FILE,"BEGIN\n\n"; + while() { + if( m/PC5-RRC-Definitions DEFINITIONS AUTOMATIC TAGS ::=/){ + syswrite OUTPUT_FILE,"$_ \n"; + syswrite OUTPUT_FILE,"BEGIN\n\n"; - # Get all the text delimited by -- ASN1START and -- ASN1STOP - extract_asn1(); + # Get all the text delimited by -- ASN1START and -- ASN1STOP + extract_asn1(); - syswrite OUTPUT_FILE,"END\n\n"; + syswrite OUTPUT_FILE,"END\n\n"; - while() { + while() { + if( m/NR-UE-Variables DEFINITIONS AUTOMATIC TAGS ::=/){ + syswrite OUTPUT_FILE,"$_ \n"; + syswrite OUTPUT_FILE,"BEGIN\n\n"; + + # Get all the text delimited by -- ASN1START and -- ASN1STOP + extract_asn1(); + syswrite OUTPUT_FILE,"END\n\n"; + while() { + if( m/NR-Sidelink-Preconf DEFINITIONS AUTOMATIC TAGS ::=/){ + syswrite OUTPUT_FILE,"$_ \n"; + syswrite OUTPUT_FILE,"BEGIN\n\n"; + + # Get all the text delimited by -- ASN1START and -- ASN1STOP + extract_asn1(); + + syswrite OUTPUT_FILE,"END\n\n"; + while() { if( m/NR-InterNodeDefinitions DEFINITIONS AUTOMATIC TAGS ::=/){ - - syswrite OUTPUT_FILE,"$_ \n"; - syswrite OUTPUT_FILE,"BEGIN\n\n"; + syswrite OUTPUT_FILE,"$_ \n"; + syswrite OUTPUT_FILE,"BEGIN\n\n"; - # Get all the text delimited by -- ASN1START and -- ASN1STOP - extract_asn1(); + # Get all the text delimited by -- ASN1START and -- ASN1STOP + extract_asn1(); - syswrite OUTPUT_FILE,"END\n\n"; - - } + syswrite OUTPUT_FILE,"END\n\n"; + } } - - } - } - + } + } + } + } + } + } close(OUTPUT_FILE); } - - } close(INPUT_FILE); diff --git a/openair2/RRC/NR/MESSAGES/asn1c/ASN1_files/nr-rrc-16.4.1.asn1 b/openair2/RRC/NR/MESSAGES/asn1c/ASN1_files/nr-rrc-16.4.1.asn1 new file mode 100644 index 0000000000..80112fa2ff --- /dev/null +++ b/openair2/RRC/NR/MESSAGES/asn1c/ASN1_files/nr-rrc-16.4.1.asn1 @@ -0,0 +1,14304 @@ +NR-RRC-Definitions DEFINITIONS AUTOMATIC TAGS ::= + +BEGIN + +-- TAG-BCCH-BCH-MESSAGE-START + +BCCH-BCH-Message ::= SEQUENCE { + message BCCH-BCH-MessageType +} + +BCCH-BCH-MessageType ::= CHOICE { + mib MIB, + messageClassExtension SEQUENCE {} +} + +-- TAG-BCCH-BCH-MESSAGE-STOP +-- TAG-BCCH-DL-SCH-MESSAGE-START + +BCCH-DL-SCH-Message ::= SEQUENCE { + message BCCH-DL-SCH-MessageType +} + +BCCH-DL-SCH-MessageType ::= CHOICE { + c1 CHOICE { + systemInformation SystemInformation, + systemInformationBlockType1 SIB1 + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-BCCH-DL-SCH-MESSAGE-STOP +-- TAG-DL-CCCH-MESSAGE-START + +DL-CCCH-Message ::= SEQUENCE { + message DL-CCCH-MessageType +} + +DL-CCCH-MessageType ::= CHOICE { + c1 CHOICE { + rrcReject RRCReject, + rrcSetup RRCSetup, + spare2 NULL, + spare1 NULL + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-DL-CCCH-MESSAGE-STOP +-- TAG-DL-DCCH-MESSAGE-START + +DL-DCCH-Message ::= SEQUENCE { + message DL-DCCH-MessageType +} + +DL-DCCH-MessageType ::= CHOICE { + c1 CHOICE { + rrcReconfiguration RRCReconfiguration, + rrcResume RRCResume, + rrcRelease RRCRelease, + rrcReestablishment RRCReestablishment, + securityModeCommand SecurityModeCommand, + dlInformationTransfer DLInformationTransfer, + ueCapabilityEnquiry UECapabilityEnquiry, + counterCheck CounterCheck, + mobilityFromNRCommand MobilityFromNRCommand, + dlDedicatedMessageSegment-r16 DLDedicatedMessageSegment-r16, + ueInformationRequest-r16 UEInformationRequest-r16, + dlInformationTransferMRDC-r16 DLInformationTransferMRDC-r16, + loggedMeasurementConfiguration-r16 LoggedMeasurementConfiguration-r16, + spare3 NULL, spare2 NULL, spare1 NULL + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-DL-DCCH-MESSAGE-STOP +-- TAG-PCCH-PCH-MESSAGE-START + +PCCH-Message ::= SEQUENCE { + message PCCH-MessageType +} + +PCCH-MessageType ::= CHOICE { + c1 CHOICE { + paging Paging, + spare1 NULL + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-PCCH-PCH-MESSAGE-STOP +-- TAG-UL-CCCH-MESSAGE-START + + +UL-CCCH-Message ::= SEQUENCE { + message UL-CCCH-MessageType +} + +UL-CCCH-MessageType ::= CHOICE { + c1 CHOICE { + rrcSetupRequest RRCSetupRequest, + rrcResumeRequest RRCResumeRequest, + rrcReestablishmentRequest RRCReestablishmentRequest, + rrcSystemInfoRequest RRCSystemInfoRequest + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-UL-CCCH-MESSAGE-STOP +-- TAG-UL-CCCH1-MESSAGE-START + + +UL-CCCH1-Message ::= SEQUENCE { + message UL-CCCH1-MessageType +} + +UL-CCCH1-MessageType ::= CHOICE { + c1 CHOICE { + rrcResumeRequest1 RRCResumeRequest1, + spare3 NULL, + spare2 NULL, + spare1 NULL + + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-UL-CCCH1-MESSAGE-STOP +-- TAG-UL-DCCH-MESSAGE-START + +UL-DCCH-Message ::= SEQUENCE { + message UL-DCCH-MessageType +} + +UL-DCCH-MessageType ::= CHOICE { + c1 CHOICE { + measurementReport MeasurementReport, + rrcReconfigurationComplete RRCReconfigurationComplete, + rrcSetupComplete RRCSetupComplete, + rrcReestablishmentComplete RRCReestablishmentComplete, + rrcResumeComplete RRCResumeComplete, + securityModeComplete SecurityModeComplete, + securityModeFailure SecurityModeFailure, + ulInformationTransfer ULInformationTransfer, + locationMeasurementIndication LocationMeasurementIndication, + ueCapabilityInformation UECapabilityInformation, + counterCheckResponse CounterCheckResponse, + ueAssistanceInformation UEAssistanceInformation, + failureInformation FailureInformation, + ulInformationTransferMRDC ULInformationTransferMRDC, + scgFailureInformation SCGFailureInformation, + scgFailureInformationEUTRA SCGFailureInformationEUTRA + }, + messageClassExtension CHOICE { + c2 CHOICE { + ulDedicatedMessageSegment-r16 ULDedicatedMessageSegment-r16, + dedicatedSIBRequest-r16 DedicatedSIBRequest-r16, + mcgFailureInformation-r16 MCGFailureInformation-r16, + ueInformationResponse-r16 UEInformationResponse-r16, + sidelinkUEInformationNR-r16 SidelinkUEInformationNR-r16, + ulInformationTransferIRAT-r16 ULInformationTransferIRAT-r16, + iabOtherInformation-r16 IABOtherInformation-r16, + spare9 NULL, spare8 NULL, spare7 NULL, spare6 NULL, + spare5 NULL, spare4 NULL, spare3 NULL, spare2 NULL, spare1 NULL + }, + messageClassExtensionFuture-r16 SEQUENCE {} + } +} + +-- TAG-UL-DCCH-MESSAGE-STOP +-- TAG-COUNTERCHECK-START + + +CounterCheck ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + counterCheck CounterCheck-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +CounterCheck-IEs ::= SEQUENCE { + drb-CountMSB-InfoList DRB-CountMSB-InfoList, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +DRB-CountMSB-InfoList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-CountMSB-Info + +DRB-CountMSB-Info ::= SEQUENCE { + drb-Identity DRB-Identity, + countMSB-Uplink INTEGER(0..33554431), + countMSB-Downlink INTEGER(0..33554431) +} + +-- TAG-COUNTERCHECK-STOP +-- TAG-COUNTERCHECKRESPONSE-START + +CounterCheckResponse ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + counterCheckResponse CounterCheckResponse-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +CounterCheckResponse-IEs ::= SEQUENCE { + drb-CountInfoList DRB-CountInfoList, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL + +} + +DRB-CountInfoList ::= SEQUENCE (SIZE (0..maxDRB)) OF DRB-CountInfo + +DRB-CountInfo ::= SEQUENCE { + drb-Identity DRB-Identity, + count-Uplink INTEGER(0..4294967295), + count-Downlink INTEGER(0..4294967295) +} + +-- TAG-COUNTERCHECKRESPONSE-STOP +-- TAG-DEDICATEDSIBREQUEST-START + +DedicatedSIBRequest-r16 ::= SEQUENCE { + criticalExtensions CHOICE { + dedicatedSIBRequest-r16 DedicatedSIBRequest-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +DedicatedSIBRequest-r16-IEs ::= SEQUENCE { + onDemandSIB-RequestList-r16 SEQUENCE { + + requestedSIB-List-r16 SEQUENCE (SIZE (1..maxOnDemandSIB-r16)) OF SIB-ReqInfo-r16 OPTIONAL, + requestedPosSIB-List-r16 SEQUENCE (SIZE (1..maxOnDemandPosSIB-r16)) OF PosSIB-ReqInfo-r16 OPTIONAL + } OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +SIB-ReqInfo-r16 ::= ENUMERATED { sib12, sib13, sib14, spare5, spare4, spare3, spare2, spare1 } + +PosSIB-ReqInfo-r16 ::= SEQUENCE { + gnss-id-r16 GNSS-ID-r16 OPTIONAL, + sbas-id-r16 SBAS-ID-r16 OPTIONAL, + posSibType-r16 ENUMERATED { posSibType1-1, posSibType1-2, posSibType1-3, posSibType1-4, posSibType1-5, posSibType1-6, + posSibType1-7, posSibType1-8, posSibType2-1, posSibType2-2, posSibType2-3, posSibType2-4, + posSibType2-5, posSibType2-6, posSibType2-7, posSibType2-8, posSibType2-9, posSibType2-10, + posSibType2-11, posSibType2-12, posSibType2-13, posSibType2-14, posSibType2-15, + posSibType2-16, posSibType2-17, posSibType2-18, posSibType2-19, posSibType2-20, + posSibType2-21, posSibType2-22, posSibType2-23, posSibType3-1, posSibType4-1, + posSibType5-1, posSibType6-1, posSibType6-2, posSibType6-3,... } +} + +-- TAG-DEDICATEDSIBREQUEST-STOP +-- TAG-DLDEDICATEDMESSAGESEGMENT-START + + +DLDedicatedMessageSegment-r16 ::= SEQUENCE { + criticalExtensions CHOICE { + dlDedicatedMessageSegment-r16 DLDedicatedMessageSegment-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +DLDedicatedMessageSegment-r16-IEs ::= SEQUENCE { + segmentNumber-r16 INTEGER(0..4), + rrc-MessageSegmentContainer-r16 OCTET STRING, + rrc-MessageSegmentType-r16 ENUMERATED {notLastSegment, lastSegment}, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-DLDEDICATEDMESSAGESEGMENT-STOP +-- TAG-DLINFORMATIONTRANSFER-START + +DLInformationTransfer ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + dlInformationTransfer DLInformationTransfer-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +DLInformationTransfer-IEs ::= SEQUENCE { + dedicatedNAS-Message DedicatedNAS-Message OPTIONAL, -- Need N + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension DLInformationTransfer-v1610-IEs OPTIONAL +} + +DLInformationTransfer-v1610-IEs ::= SEQUENCE { + referenceTimeInfo-r16 ReferenceTimeInfo-r16 OPTIONAL, -- Need R + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-DLINFORMATIONTRANSFER-STOP +-- TAG-DLINFORMATIONTRANSFERMRDC-START + +DLInformationTransferMRDC-r16 ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE { + dlInformationTransferMRDC-r16 DLInformationTransferMRDC-r16-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +DLInformationTransferMRDC-r16-IEs::= SEQUENCE { + dl-DCCH-MessageNR-r16 OCTET STRING OPTIONAL, -- Need N + dl-DCCH-MessageEUTRA-r16 OCTET STRING OPTIONAL, -- Need N + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-DLINFORMATIONTRANSFERMRDC-STOP +-- TAG-FAILUREINFORMATION-START + +FailureInformation ::= SEQUENCE { + criticalExtensions CHOICE { + failureInformation FailureInformation-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +FailureInformation-IEs ::= SEQUENCE { + failureInfoRLC-Bearer FailureInfoRLC-Bearer OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension FailureInformation-v1610-IEs OPTIONAL +} + +FailureInfoRLC-Bearer ::= SEQUENCE { + cellGroupId CellGroupId, + logicalChannelIdentity LogicalChannelIdentity, + failureType ENUMERATED {rlc-failure, spare3, spare2, spare1} +} + +FailureInformation-v1610-IEs ::= SEQUENCE { + failureInfoDAPS-r16 FailureInfoDAPS-r16 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +FailureInfoDAPS-r16 ::= SEQUENCE { + failureType-r16 ENUMERATED {daps-failure, spare3, spare2, spare1} +} + +-- TAG-FAILUREINFORMATION-STOP +-- TAG-IABOTHERINFORMATION-START + +IABOtherInformation-r16 ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + iabOtherInformation-r16 IABOtherInformation-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +IABOtherInformation-r16-IEs ::= SEQUENCE { + ip-InfoType-r16 CHOICE { + iab-IP-Request-r16 SEQUENCE { + iab-IPv4-AddressNumReq-r16 IAB-IP-AddressNumReq-r16 OPTIONAL, + iab-IPv6-AddressReq-r16 CHOICE { + iab-IPv6-AddressNumReq-r16 IAB-IP-AddressNumReq-r16, + iab-IPv6-AddressPrefixReq-r16 IAB-IP-AddressPrefixReq-r16, + ... + } OPTIONAL + }, + iab-IP-Report-r16 SEQUENCE { + iab-IPv4-AddressReport-r16 IAB-IP-AddressAndTraffic-r16 OPTIONAL, + iab-IPv6-Report-r16 CHOICE { + iab-IPv6-AddressReport-r16 IAB-IP-AddressAndTraffic-r16, + iab-IPv6-PrefixReport-r16 IAB-IP-PrefixAndTraffic-r16, + ... + } OPTIONAL + }, + ... + }, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +IAB-IP-AddressNumReq-r16 ::= SEQUENCE { + all-Traffic-NumReq-r16 INTEGER (1..8) OPTIONAL, + f1-C-Traffic-NumReq-r16 INTEGER (1..8) OPTIONAL, + f1-U-Traffic-NumReq-r16 INTEGER (1..8) OPTIONAL, + non-F1-Traffic-NumReq-r16 INTEGER (1..8) OPTIONAL, + ... +} + +IAB-IP-AddressPrefixReq-r16 ::= SEQUENCE { + all-Traffic-PrefixReq-r16 ENUMERATED {true} OPTIONAL, + f1-C-Traffic-PrefixReq-r16 ENUMERATED {true} OPTIONAL, + f1-U-Traffic-PrefixReq-r16 ENUMERATED {true} OPTIONAL, + non-F1-Traffic-PrefixReq-r16 ENUMERATED {true} OPTIONAL, + ... +} + +IAB-IP-AddressAndTraffic-r16 ::= SEQUENCE { + all-Traffic-IAB-IP-Address-r16 SEQUENCE (SIZE(1..8)) OF IAB-IP-Address-r16 OPTIONAL, + f1-C-Traffic-IP-Address-r16 SEQUENCE (SIZE(1..8)) OF IAB-IP-Address-r16 OPTIONAL, + f1-U-Traffic-IP-Address-r16 SEQUENCE (SIZE(1..8)) OF IAB-IP-Address-r16 OPTIONAL, + non-F1-Traffic-IP-Address-r16 SEQUENCE (SIZE(1..8)) OF IAB-IP-Address-r16 OPTIONAL +} + +IAB-IP-PrefixAndTraffic-r16 ::= SEQUENCE { + all-Traffic-IAB-IP-Address-r16 IAB-IP-Address-r16 OPTIONAL, + f1-C-Traffic-IP-Address-r16 IAB-IP-Address-r16 OPTIONAL, + f1-U-Traffic-IP-Address-r16 IAB-IP-Address-r16 OPTIONAL, + non-F1-Traffic-IP-Address-r16 IAB-IP-Address-r16 OPTIONAL +} + +-- TAG-IABOTHERINFORMATION-STOP +-- TAG-LOCATIONMEASUREMENTINDICATION-START + +LocationMeasurementIndication ::= SEQUENCE { + criticalExtensions CHOICE { + locationMeasurementIndication LocationMeasurementIndication-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +LocationMeasurementIndication-IEs ::= SEQUENCE { + measurementIndication SetupRelease {LocationMeasurementInfo}, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-LOCATIONMEASUREMENTINDICATION-STOP +-- TAG-LOGGEDMEASUREMENTCONFIGURATION-START + +LoggedMeasurementConfiguration-r16 ::= SEQUENCE { + criticalExtensions CHOICE { + loggedMeasurementConfiguration-r16 LoggedMeasurementConfiguration-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +LoggedMeasurementConfiguration-r16-IEs ::= SEQUENCE { + traceReference-r16 TraceReference-r16, + traceRecordingSessionRef-r16 OCTET STRING (SIZE (2)), + tce-Id-r16 OCTET STRING (SIZE (1)), + absoluteTimeInfo-r16 AbsoluteTimeInfo-r16, + areaConfiguration-r16 AreaConfiguration-r16 OPTIONAL, --Need R + plmn-IdentityList-r16 PLMN-IdentityList2-r16 OPTIONAL, --Need R + bt-NameList-r16 SetupRelease {BT-NameList-r16} OPTIONAL, --Need M + wlan-NameList-r16 SetupRelease {WLAN-NameList-r16} OPTIONAL, --Need M + sensor-NameList-r16 SetupRelease {Sensor-NameList-r16} OPTIONAL, --Need M + loggingDuration-r16 LoggingDuration-r16, + reportType CHOICE { + periodical LoggedPeriodicalReportConfig-r16, + eventTriggered LoggedEventTriggerConfig-r16, + ... + }, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +LoggedPeriodicalReportConfig-r16 ::= SEQUENCE { + loggingInterval-r16 LoggingInterval-r16, + ... + } + +LoggedEventTriggerConfig-r16 ::= SEQUENCE { + eventType-r16 EventType-r16, + loggingInterval-r16 LoggingInterval-r16, + ... +} + +EventType-r16 ::= CHOICE { + outOfCoverage NULL, + eventL1 SEQUENCE { + l1-Threshold MeasTriggerQuantity, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger + }, + ... +} + +-- TAG-LOGGEDMEASUREMENTCONFIGURATION-STOP +-- TAG-MCGFAILUREINFORMATION-START + +MCGFailureInformation-r16 ::= SEQUENCE { + criticalExtensions CHOICE { + mcgFailureInformation-r16 MCGFailureInformation-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +MCGFailureInformation-r16-IEs ::= SEQUENCE { + failureReportMCG-r16 FailureReportMCG-r16 OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +FailureReportMCG-r16 ::= SEQUENCE { + failureType-r16 ENUMERATED {t310-Expiry, randomAccessProblem, rlc-MaxNumRetx, + t312-Expiry-r16, lbt-Failure-r16, beamFailureRecoveryFailure-r16, + bh-RLF-r16, spare1} OPTIONAL, + measResultFreqList-r16 MeasResultList2NR OPTIONAL, + measResultFreqListEUTRA-r16 MeasResultList2EUTRA OPTIONAL, + measResultSCG-r16 OCTET STRING (CONTAINING MeasResultSCG-Failure) OPTIONAL, + measResultSCG-EUTRA-r16 OCTET STRING OPTIONAL, + measResultFreqListUTRA-FDD-r16 MeasResultList2UTRA OPTIONAL, + ... +} + +MeasResultList2UTRA ::= SEQUENCE (SIZE (1..maxFreq)) OF MeasResult2UTRA-FDD-r16 + +MeasResult2UTRA-FDD-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueUTRA-FDD-r16, + measResultNeighCellList-r16 MeasResultListUTRA-FDD-r16 +} + +MeasResultList2EUTRA ::= SEQUENCE (SIZE (1..maxFreq)) OF MeasResult2EUTRA-r16 + +-- TAG-MCGFAILUREINFORMATION-STOP +-- TAG-MEASUREMENTREPORT-START + +MeasurementReport ::= SEQUENCE { + criticalExtensions CHOICE { + measurementReport MeasurementReport-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +MeasurementReport-IEs ::= SEQUENCE { + measResults MeasResults, + + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-MEASUREMENTREPORT-STOP +-- TAG-MIB-START + +MIB ::= SEQUENCE { + systemFrameNumber BIT STRING (SIZE (6)), + subCarrierSpacingCommon ENUMERATED {scs15or60, scs30or120}, + ssb-SubcarrierOffset INTEGER (0..15), + dmrs-TypeA-Position ENUMERATED {pos2, pos3}, + pdcch-ConfigSIB1 PDCCH-ConfigSIB1, + cellBarred ENUMERATED {barred, notBarred}, + intraFreqReselection ENUMERATED {allowed, notAllowed}, + spare BIT STRING (SIZE (1)) +} + +-- TAG-MIB-STOP +-- TAG-MOBILITYFROMNRCOMMAND-START + +MobilityFromNRCommand ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + mobilityFromNRCommand MobilityFromNRCommand-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +MobilityFromNRCommand-IEs ::= SEQUENCE { + targetRAT-Type ENUMERATED { eutra, utra-fdd-v1610, spare2, spare1, ...}, + targetRAT-MessageContainer OCTET STRING, + nas-SecurityParamFromNR OCTET STRING OPTIONAL, -- Cond HO-ToEPCUTRAN + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension MobilityFromNRCommand-v1610-IEs OPTIONAL +} + +MobilityFromNRCommand-v1610-IEs ::= SEQUENCE { + voiceFallbackIndication-r16 ENUMERATED {true} OPTIONAL, -- Need N + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-MOBILITYFROMNRCOMMAND-STOP +-- TAG-PAGING-START + +Paging ::= SEQUENCE { + pagingRecordList PagingRecordList OPTIONAL, -- Need N + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +PagingRecordList ::= SEQUENCE (SIZE(1..maxNrofPageRec)) OF PagingRecord + +PagingRecord ::= SEQUENCE { + ue-Identity PagingUE-Identity, + accessType ENUMERATED {non3GPP} OPTIONAL, -- Need N + ... +} + +PagingUE-Identity ::= CHOICE { + ng-5G-S-TMSI NG-5G-S-TMSI, + fullI-RNTI I-RNTI-Value, + ... +} + +-- TAG-PAGING-STOP +-- TAG-RRCREESTABLISHMENT-START + +RRCReestablishment ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcReestablishment RRCReestablishment-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReestablishment-IEs ::= SEQUENCE { + nextHopChainingCount NextHopChainingCount, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-RRCREESTABLISHMENT-STOP +-- TAG-RRCREESTABLISHMENTCOMPLETE-START + +RRCReestablishmentComplete ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcReestablishmentComplete RRCReestablishmentComplete-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReestablishmentComplete-IEs ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension RRCReestablishmentComplete-v1610-IEs OPTIONAL +} + +RRCReestablishmentComplete-v1610-IEs ::= SEQUENCE { + ue-MeasurementsAvailable-r16 UE-MeasurementsAvailable-r16 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-RRCREESTABLISHMENTCOMPLETE-STOP +-- TAG-RRCREESTABLISHMENTREQUEST-START + + +RRCReestablishmentRequest ::= SEQUENCE { + rrcReestablishmentRequest RRCReestablishmentRequest-IEs +} + +RRCReestablishmentRequest-IEs ::= SEQUENCE { + ue-Identity ReestabUE-Identity, + reestablishmentCause ReestablishmentCause, + spare BIT STRING (SIZE (1)) +} + +ReestabUE-Identity ::= SEQUENCE { + c-RNTI RNTI-Value, + physCellId PhysCellId, + shortMAC-I ShortMAC-I +} + +ReestablishmentCause ::= ENUMERATED {reconfigurationFailure, handoverFailure, otherFailure, spare1} + +-- TAG-RRCREESTABLISHMENTREQUEST-STOP +-- TAG-RRCRECONFIGURATION-START + +RRCReconfiguration ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcReconfiguration RRCReconfiguration-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReconfiguration-IEs ::= SEQUENCE { + radioBearerConfig RadioBearerConfig OPTIONAL, -- Need M + secondaryCellGroup OCTET STRING (CONTAINING CellGroupConfig) OPTIONAL, -- Cond SCG + measConfig MeasConfig OPTIONAL, -- Need M + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension RRCReconfiguration-v1530-IEs OPTIONAL +} + +RRCReconfiguration-v1530-IEs ::= SEQUENCE { + masterCellGroup OCTET STRING (CONTAINING CellGroupConfig) OPTIONAL, -- Need M + fullConfig ENUMERATED {true} OPTIONAL, -- Cond FullConfig + dedicatedNAS-MessageList SEQUENCE (SIZE(1..maxDRB)) OF DedicatedNAS-Message OPTIONAL, -- Cond nonHO + masterKeyUpdate MasterKeyUpdate OPTIONAL, -- Cond MasterKeyChange + dedicatedSIB1-Delivery OCTET STRING (CONTAINING SIB1) OPTIONAL, -- Need N + dedicatedSystemInformationDelivery OCTET STRING (CONTAINING SystemInformation) OPTIONAL, -- Need N + otherConfig OtherConfig OPTIONAL, -- Need M + nonCriticalExtension RRCReconfiguration-v1540-IEs OPTIONAL +} + +RRCReconfiguration-v1540-IEs ::= SEQUENCE { + otherConfig-v1540 OtherConfig-v1540 OPTIONAL, -- Need M + nonCriticalExtension RRCReconfiguration-v1560-IEs OPTIONAL +} + +RRCReconfiguration-v1560-IEs ::= SEQUENCE { + mrdc-SecondaryCellGroupConfig SetupRelease { MRDC-SecondaryCellGroupConfig } OPTIONAL, -- Need M + radioBearerConfig2 OCTET STRING (CONTAINING RadioBearerConfig) OPTIONAL, -- Need M + sk-Counter SK-Counter OPTIONAL, -- Need N + nonCriticalExtension RRCReconfiguration-v1610-IEs OPTIONAL +} +RRCReconfiguration-v1610-IEs ::= SEQUENCE { + otherConfig-v1610 OtherConfig-v1610 OPTIONAL, -- Need M + bap-Config-r16 SetupRelease { BAP-Config-r16 } OPTIONAL, -- Need M + iab-IP-AddressConfigurationList-r16 IAB-IP-AddressConfigurationList-r16 OPTIONAL, -- Need M + conditionalReconfiguration-r16 ConditionalReconfiguration-r16 OPTIONAL, -- Need M + daps-SourceRelease-r16 ENUMERATED{true} OPTIONAL, -- Need N + t316-r16 SetupRelease {T316-r16} OPTIONAL, -- Need M + needForGapsConfigNR-r16 SetupRelease {NeedForGapsConfigNR-r16} OPTIONAL, -- Need M + onDemandSIB-Request-r16 SetupRelease { OnDemandSIB-Request-r16 } OPTIONAL, -- Need M + dedicatedPosSysInfoDelivery-r16 OCTET STRING (CONTAINING PosSystemInformation-r16-IEs) OPTIONAL, -- Need N + sl-ConfigDedicatedNR-r16 SetupRelease {SL-ConfigDedicatedNR-r16} OPTIONAL, -- Need M + sl-ConfigDedicatedEUTRA-Info-r16 SetupRelease {SL-ConfigDedicatedEUTRA-Info-r16} OPTIONAL, -- Need M + targetCellSMTC-SCG-r16 SSB-MTC OPTIONAL, -- Need S + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +MRDC-SecondaryCellGroupConfig ::= SEQUENCE { + mrdc-ReleaseAndAdd ENUMERATED {true} OPTIONAL, -- Need N + mrdc-SecondaryCellGroup CHOICE { + nr-SCG OCTET STRING (CONTAINING RRCReconfiguration), + eutra-SCG OCTET STRING + } +} + +BAP-Config-r16 ::= SEQUENCE { + bap-Address-r16 BIT STRING (SIZE (10)) OPTIONAL, -- Need M + defaultUL-BAP-RoutingID-r16 BAP-RoutingID-r16 OPTIONAL, -- Need M + defaultUL-BH-RLC-Channel-r16 BH-RLC-ChannelID-r16 OPTIONAL, -- Need M + flowControlFeedbackType-r16 ENUMERATED {perBH-RLC-Channel, perRoutingID, both} OPTIONAL, -- Need R + ... +} + +MasterKeyUpdate ::= SEQUENCE { + keySetChangeIndicator BOOLEAN, + nextHopChainingCount NextHopChainingCount, + nas-Container OCTET STRING OPTIONAL, -- Cond securityNASC + ... +} + +OnDemandSIB-Request-r16 ::= SEQUENCE { + onDemandSIB-RequestProhibitTimer-r16 ENUMERATED {s0, s0dot5, s1, s2, s5, s10, s20, s30} +} + +T316-r16 ::= ENUMERATED {ms50, ms100, ms200, ms300, ms400, ms500, ms600, ms1000, ms1500, ms2000} + +IAB-IP-AddressConfigurationList-r16 ::= SEQUENCE { + iab-IP-AddressToAddModList-r16 SEQUENCE (SIZE(1..maxIAB-IP-Address-r16)) OF IAB-IP-AddressConfiguration-r16 OPTIONAL, -- Need N + iab-IP-AddressToReleaseList-r16 SEQUENCE (SIZE(1..maxIAB-IP-Address-r16)) OF IAB-IP-AddressIndex-r16 OPTIONAL, -- Need N + ... +} + +IAB-IP-AddressConfiguration-r16 ::= SEQUENCE { + iab-IP-AddressIndex-r16 IAB-IP-AddressIndex-r16, + iab-IP-Address-r16 IAB-IP-Address-r16 OPTIONAL, -- Need M + iab-IP-Usage-r16 IAB-IP-Usage-r16 OPTIONAL, -- Need M + iab-donor-DU-BAP-Address-r16 BIT STRING (SIZE(10)) OPTIONAL, -- Need M +... +} + +SL-ConfigDedicatedEUTRA-Info-r16 ::= SEQUENCE { + sl-ConfigDedicatedEUTRA-r16 OCTET STRING OPTIONAL, -- Need M + sl-TimeOffsetEUTRA-List-r16 SEQUENCE (SIZE (8)) OF SL-TimeOffsetEUTRA-r16 OPTIONAL -- Need M +} + +SL-TimeOffsetEUTRA-r16 ::= ENUMERATED {ms0, ms0dot25, ms0dot5, ms0dot625, ms0dot75, ms1, ms1dot25, ms1dot5, ms1dot75, + ms2, ms2dot5, ms3, ms4, ms5, ms6, ms8, ms10, ms20} + +-- TAG-RRCRECONFIGURATION-STOP +-- TAG-RRCRECONFIGURATIONCOMPLETE-START + +RRCReconfigurationComplete ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcReconfigurationComplete RRCReconfigurationComplete-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReconfigurationComplete-IEs ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension RRCReconfigurationComplete-v1530-IEs OPTIONAL +} + +RRCReconfigurationComplete-v1530-IEs ::= SEQUENCE { + uplinkTxDirectCurrentList UplinkTxDirectCurrentList OPTIONAL, + nonCriticalExtension RRCReconfigurationComplete-v1560-IEs OPTIONAL +} + +RRCReconfigurationComplete-v1560-IEs ::= SEQUENCE { + scg-Response CHOICE { + nr-SCG-Response OCTET STRING (CONTAINING RRCReconfigurationComplete), + eutra-SCG-Response OCTET STRING + } OPTIONAL, + nonCriticalExtension RRCReconfigurationComplete-v1610-IEs OPTIONAL +} + +RRCReconfigurationComplete-v1610-IEs ::= SEQUENCE { + ue-MeasurementsAvailable-r16 UE-MeasurementsAvailable-r16 OPTIONAL, + needForGapsInfoNR-r16 NeedForGapsInfoNR-r16 OPTIONAL, + nonCriticalExtension RRCReconfigurationComplete-v1640-IEs OPTIONAL +} + +RRCReconfigurationComplete-v1640-IEs ::= SEQUENCE { + uplinkTxDirectCurrentTwoCarrierList-r16 UplinkTxDirectCurrentTwoCarrierList-r16 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-RRCRECONFIGURATIONCOMPLETE-STOP +-- TAG-RRCREJECT-START + +RRCReject ::= SEQUENCE { + criticalExtensions CHOICE { + rrcReject RRCReject-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReject-IEs ::= SEQUENCE { + waitTime RejectWaitTime OPTIONAL, -- Need N + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-RRCREJECT-STOP +-- TAG-RRCRELEASE-START + +RRCRelease ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcRelease RRCRelease-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCRelease-IEs ::= SEQUENCE { + redirectedCarrierInfo RedirectedCarrierInfo OPTIONAL, -- Need N + cellReselectionPriorities CellReselectionPriorities OPTIONAL, -- Need R + suspendConfig SuspendConfig OPTIONAL, -- Need R + deprioritisationReq SEQUENCE { + deprioritisationType ENUMERATED {frequency, nr}, + deprioritisationTimer ENUMERATED {min5, min10, min15, min30} + } OPTIONAL, -- Need N + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension RRCRelease-v1540-IEs OPTIONAL +} + +RRCRelease-v1540-IEs ::= SEQUENCE { + waitTime RejectWaitTime OPTIONAL, -- Need N + nonCriticalExtension RRCRelease-v1610-IEs OPTIONAL +} + +RRCRelease-v1610-IEs ::= SEQUENCE { + voiceFallbackIndication-r16 ENUMERATED {true} OPTIONAL, -- Need N + measIdleConfig-r16 SetupRelease {MeasIdleConfigDedicated-r16} OPTIONAL, -- Need M + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +RedirectedCarrierInfo ::= CHOICE { + nr CarrierInfoNR, + eutra RedirectedCarrierInfo-EUTRA, + ... +} + +RedirectedCarrierInfo-EUTRA ::= SEQUENCE { + eutraFrequency ARFCN-ValueEUTRA, + cnType ENUMERATED {epc,fiveGC} OPTIONAL -- Need N +} + +CarrierInfoNR ::= SEQUENCE { + carrierFreq ARFCN-ValueNR, + ssbSubcarrierSpacing SubcarrierSpacing, + smtc SSB-MTC OPTIONAL, -- Need S + ... +} + +SuspendConfig ::= SEQUENCE { + fullI-RNTI I-RNTI-Value, + shortI-RNTI ShortI-RNTI-Value, + ran-PagingCycle PagingCycle, + ran-NotificationAreaInfo RAN-NotificationAreaInfo OPTIONAL, -- Need M + t380 PeriodicRNAU-TimerValue OPTIONAL, -- Need R + nextHopChainingCount NextHopChainingCount, + ... +} + +PeriodicRNAU-TimerValue ::= ENUMERATED { min5, min10, min20, min30, min60, min120, min360, min720} + + +CellReselectionPriorities ::= SEQUENCE { + freqPriorityListEUTRA FreqPriorityListEUTRA OPTIONAL, -- Need M + freqPriorityListNR FreqPriorityListNR OPTIONAL, -- Need M + t320 ENUMERATED {min5, min10, min20, min30, min60, min120, min180, spare1} OPTIONAL, -- Need R + ... +} + +PagingCycle ::= ENUMERATED {rf32, rf64, rf128, rf256} + +FreqPriorityListEUTRA ::= SEQUENCE (SIZE (1..maxFreq)) OF FreqPriorityEUTRA + +FreqPriorityListNR ::= SEQUENCE (SIZE (1..maxFreq)) OF FreqPriorityNR + +FreqPriorityEUTRA ::= SEQUENCE { + carrierFreq ARFCN-ValueEUTRA, + cellReselectionPriority CellReselectionPriority, + cellReselectionSubPriority CellReselectionSubPriority OPTIONAL -- Need R +} + +FreqPriorityNR ::= SEQUENCE { + carrierFreq ARFCN-ValueNR, + cellReselectionPriority CellReselectionPriority, + cellReselectionSubPriority CellReselectionSubPriority OPTIONAL -- Need R +} + +RAN-NotificationAreaInfo ::= CHOICE { + cellList PLMN-RAN-AreaCellList, + ran-AreaConfigList PLMN-RAN-AreaConfigList, + ... +} + +PLMN-RAN-AreaCellList ::= SEQUENCE (SIZE (1.. maxPLMNIdentities)) OF PLMN-RAN-AreaCell + +PLMN-RAN-AreaCell ::= SEQUENCE { + plmn-Identity PLMN-Identity OPTIONAL, -- Need S + ran-AreaCells SEQUENCE (SIZE (1..32)) OF CellIdentity +} + +PLMN-RAN-AreaConfigList ::= SEQUENCE (SIZE (1..maxPLMNIdentities)) OF PLMN-RAN-AreaConfig + +PLMN-RAN-AreaConfig ::= SEQUENCE { + plmn-Identity PLMN-Identity OPTIONAL, -- Need S + ran-Area SEQUENCE (SIZE (1..16)) OF RAN-AreaConfig +} + +RAN-AreaConfig ::= SEQUENCE { + trackingAreaCode TrackingAreaCode, + ran-AreaCodeList SEQUENCE (SIZE (1..32)) OF RAN-AreaCode OPTIONAL -- Need R +} + +-- TAG-RRCRELEASE-STOP +-- TAG-RRCRESUME-START + +RRCResume ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcResume RRCResume-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCResume-IEs ::= SEQUENCE { + radioBearerConfig RadioBearerConfig OPTIONAL, -- Need M + masterCellGroup OCTET STRING (CONTAINING CellGroupConfig) OPTIONAL, -- Need M + measConfig MeasConfig OPTIONAL, -- Need M + fullConfig ENUMERATED {true} OPTIONAL, -- Need N + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension RRCResume-v1560-IEs OPTIONAL +} + +RRCResume-v1560-IEs ::= SEQUENCE { + radioBearerConfig2 OCTET STRING (CONTAINING RadioBearerConfig) OPTIONAL, -- Need M + sk-Counter SK-Counter OPTIONAL, -- Need N + nonCriticalExtension RRCResume-v1610-IEs OPTIONAL +} + +RRCResume-v1610-IEs ::= SEQUENCE { + idleModeMeasurementReq-r16 ENUMERATED {true} OPTIONAL, -- Need N + restoreMCG-SCells-r16 ENUMERATED {true} OPTIONAL, -- Need N + restoreSCG-r16 ENUMERATED {true} OPTIONAL, -- Need N + mrdc-SecondaryCellGroup-r16 CHOICE { + nr-SCG-r16 OCTET STRING (CONTAINING RRCReconfiguration), + eutra-SCG-r16 OCTET STRING + } OPTIONAL, -- Cond RestoreSCG + needForGapsConfigNR-r16 SetupRelease {NeedForGapsConfigNR-r16} OPTIONAL, -- Need M + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-RRCRESUME-STOP +-- TAG-RRCRESUMECOMPLETE-START + +RRCResumeComplete ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcResumeComplete RRCResumeComplete-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCResumeComplete-IEs ::= SEQUENCE { + dedicatedNAS-Message DedicatedNAS-Message OPTIONAL, + selectedPLMN-Identity INTEGER (1..maxPLMN) OPTIONAL, + uplinkTxDirectCurrentList UplinkTxDirectCurrentList OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension RRCResumeComplete-v1610-IEs OPTIONAL +} + +RRCResumeComplete-v1610-IEs ::= SEQUENCE { + idleMeasAvailable-r16 ENUMERATED {true} OPTIONAL, + measResultIdleEUTRA-r16 MeasResultIdleEUTRA-r16 OPTIONAL, + measResultIdleNR-r16 MeasResultIdleNR-r16 OPTIONAL, + scg-Response-r16 CHOICE { + nr-SCG-Response OCTET STRING (CONTAINING RRCReconfigurationComplete), + eutra-SCG-Response OCTET STRING + } OPTIONAL, + ue-MeasurementsAvailable-r16 UE-MeasurementsAvailable-r16 OPTIONAL, + mobilityHistoryAvail-r16 ENUMERATED {true} OPTIONAL, + mobilityState-r16 ENUMERATED {normal, medium, high, spare} OPTIONAL, + needForGapsInfoNR-r16 NeedForGapsInfoNR-r16 OPTIONAL, + nonCriticalExtension RRCResumeComplete-v1640-IEs OPTIONAL +} + +RRCResumeComplete-v1640-IEs ::= SEQUENCE { + uplinkTxDirectCurrentTwoCarrierList-r16 UplinkTxDirectCurrentTwoCarrierList-r16 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-RRCRESUMECOMPLETE-STOP +-- TAG-RRCRESUMEREQUEST-START + +RRCResumeRequest ::= SEQUENCE { + rrcResumeRequest RRCResumeRequest-IEs +} + +RRCResumeRequest-IEs ::= SEQUENCE { + resumeIdentity ShortI-RNTI-Value, + resumeMAC-I BIT STRING (SIZE (16)), + resumeCause ResumeCause, + spare BIT STRING (SIZE (1)) +} + +-- TAG-RRCRESUMEREQUEST-STOP +-- TAG-RRCRESUMEREQUEST1-START + +RRCResumeRequest1 ::= SEQUENCE { + rrcResumeRequest1 RRCResumeRequest1-IEs +} + +RRCResumeRequest1-IEs ::= SEQUENCE { + resumeIdentity I-RNTI-Value, + resumeMAC-I BIT STRING (SIZE (16)), + resumeCause ResumeCause, + spare BIT STRING (SIZE (1)) +} + +-- TAG-RRCRESUMEREQUEST1-STOP +-- TAG-RRCSETUP-START + +RRCSetup ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcSetup RRCSetup-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCSetup-IEs ::= SEQUENCE { + radioBearerConfig RadioBearerConfig, + masterCellGroup OCTET STRING (CONTAINING CellGroupConfig), + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-RRCSETUP-STOP +-- TAG-RRCSETUPCOMPLETE-START + +RRCSetupComplete ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcSetupComplete RRCSetupComplete-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCSetupComplete-IEs ::= SEQUENCE { + selectedPLMN-Identity INTEGER (1..maxPLMN), + registeredAMF RegisteredAMF OPTIONAL, + guami-Type ENUMERATED {native, mapped} OPTIONAL, + s-NSSAI-List SEQUENCE (SIZE (1..maxNrofS-NSSAI)) OF S-NSSAI OPTIONAL, + dedicatedNAS-Message DedicatedNAS-Message, + ng-5G-S-TMSI-Value CHOICE { + ng-5G-S-TMSI NG-5G-S-TMSI, + ng-5G-S-TMSI-Part2 BIT STRING (SIZE (9)) + } OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension RRCSetupComplete-v1610-IEs OPTIONAL +} + +RRCSetupComplete-v1610-IEs ::= SEQUENCE { + iab-NodeIndication-r16 ENUMERATED {true} OPTIONAL, + idleMeasAvailable-r16 ENUMERATED {true} OPTIONAL, + ue-MeasurementsAvailable-r16 UE-MeasurementsAvailable-r16 OPTIONAL, + mobilityHistoryAvail-r16 ENUMERATED {true} OPTIONAL, + mobilityState-r16 ENUMERATED {normal, medium, high, spare} OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +RegisteredAMF ::= SEQUENCE { + plmn-Identity PLMN-Identity OPTIONAL, + amf-Identifier AMF-Identifier +} + +-- TAG-RRCSETUPCOMPLETE-STOP +-- TAG-RRCSETUPREQUEST-START + +RRCSetupRequest ::= SEQUENCE { + rrcSetupRequest RRCSetupRequest-IEs +} + +RRCSetupRequest-IEs ::= SEQUENCE { + ue-Identity InitialUE-Identity, + establishmentCause EstablishmentCause, + spare BIT STRING (SIZE (1)) +} + +InitialUE-Identity ::= CHOICE { + ng-5G-S-TMSI-Part1 BIT STRING (SIZE (39)), + randomValue BIT STRING (SIZE (39)) +} + +EstablishmentCause ::= ENUMERATED { + emergency, highPriorityAccess, mt-Access, mo-Signalling, + mo-Data, mo-VoiceCall, mo-VideoCall, mo-SMS, mps-PriorityAccess, mcs-PriorityAccess, + spare6, spare5, spare4, spare3, spare2, spare1} + +-- TAG-RRCSETUPREQUEST-STOP +-- TAG-RRCSYSTEMINFOREQUEST-START + +RRCSystemInfoRequest ::= SEQUENCE { + criticalExtensions CHOICE { + rrcSystemInfoRequest RRCSystemInfoRequest-IEs, + criticalExtensionsFuture-r16 CHOICE { + rrcPosSystemInfoRequest-r16 RRC-PosSystemInfoRequest-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } + } +} + +RRCSystemInfoRequest-IEs ::= SEQUENCE { + requested-SI-List BIT STRING (SIZE (maxSI-Message)), --32bits + spare BIT STRING (SIZE (12)) +} + +RRC-PosSystemInfoRequest-r16-IEs ::= SEQUENCE { + requestedPosSI-List BIT STRING (SIZE (maxSI-Message)), --32bits + spare BIT STRING (SIZE (11)) +} + +-- TAG-RRCSYSTEMINFOREQUEST-STOP +-- TAG-SCGFAILUREINFORMATION-START + +SCGFailureInformation ::= SEQUENCE { + criticalExtensions CHOICE { + scgFailureInformation SCGFailureInformation-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +SCGFailureInformation-IEs ::= SEQUENCE { + failureReportSCG FailureReportSCG OPTIONAL, + nonCriticalExtension SCGFailureInformation-v1590-IEs OPTIONAL +} + +SCGFailureInformation-v1590-IEs ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +FailureReportSCG ::= SEQUENCE { + failureType ENUMERATED { + t310-Expiry, randomAccessProblem, + rlc-MaxNumRetx, + synchReconfigFailureSCG, scg-ReconfigFailure, + srb3-IntegrityFailure, other-r16, spare1}, + measResultFreqList MeasResultFreqList OPTIONAL, + measResultSCG-Failure OCTET STRING (CONTAINING MeasResultSCG-Failure) OPTIONAL, + ..., + [[ + locationInfo-r16 LocationInfo-r16 OPTIONAL, + failureType-v1610 ENUMERATED {scg-lbtFailure-r16, beamFailureRecoveryFailure-r16, + t312-Expiry-r16, bh-RLF-r16, spare4, spare3, spare2, spare1} OPTIONAL + ]] +} + +MeasResultFreqList ::= SEQUENCE (SIZE (1..maxFreq)) OF MeasResult2NR + + +-- TAG-SCGFAILUREINFORMATION-STOP +-- TAG-SCGFAILUREINFORMATIONEUTRA-START + +SCGFailureInformationEUTRA ::= SEQUENCE { + criticalExtensions CHOICE { + scgFailureInformationEUTRA SCGFailureInformationEUTRA-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +SCGFailureInformationEUTRA-IEs ::= SEQUENCE { + failureReportSCG-EUTRA FailureReportSCG-EUTRA OPTIONAL, + nonCriticalExtension SCGFailureInformationEUTRA-v1590-IEs OPTIONAL +} + +SCGFailureInformationEUTRA-v1590-IEs ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +FailureReportSCG-EUTRA ::= SEQUENCE { + failureType ENUMERATED { + t313-Expiry, randomAccessProblem,rlc-MaxNumRetx, + scg-ChangeFailure,scg-lbtFailure-r16, + beamFailureRecoveryFailure-r16, t312-Expiry-r16, spare}, + measResultFreqListMRDC MeasResultFreqListFailMRDC OPTIONAL, + measResultSCG-FailureMRDC OCTET STRING OPTIONAL, + ..., + [[ + locationInfo-r16 LocationInfo-r16 OPTIONAL + ]] +} + +MeasResultFreqListFailMRDC ::= SEQUENCE (SIZE (1.. maxFreq)) OF MeasResult2EUTRA + +-- TAG-SCGFAILUREINFORMATIONEUTRA-STOP +-- TAG-SECURITYMODECOMMAND-START + +SecurityModeCommand ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + securityModeCommand SecurityModeCommand-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +SecurityModeCommand-IEs ::= SEQUENCE { + securityConfigSMC SecurityConfigSMC, + + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +SecurityConfigSMC ::= SEQUENCE { + securityAlgorithmConfig SecurityAlgorithmConfig, + ... +} + +-- TAG-SECURITYMODECOMMAND-STOP +-- TAG-SECURITYMODECOMPLETE-START + +SecurityModeComplete ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + securityModeComplete SecurityModeComplete-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +SecurityModeComplete-IEs ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-SECURITYMODECOMPLETE-STOP +-- TAG-SECURITYMODEFAILURE-START + +SecurityModeFailure ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + securityModeFailure SecurityModeFailure-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +SecurityModeFailure-IEs ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-SECURITYMODEFAILURE-STOP +-- TAG-SIB1-START + +SIB1 ::= SEQUENCE { + cellSelectionInfo SEQUENCE { + q-RxLevMin Q-RxLevMin, + q-RxLevMinOffset INTEGER (1..8) OPTIONAL, -- Need S + q-RxLevMinSUL Q-RxLevMin OPTIONAL, -- Need R + q-QualMin Q-QualMin OPTIONAL, -- Need S + q-QualMinOffset INTEGER (1..8) OPTIONAL -- Need S + } OPTIONAL, -- Cond Standalone + cellAccessRelatedInfo CellAccessRelatedInfo, + connEstFailureControl ConnEstFailureControl OPTIONAL, -- Need R + si-SchedulingInfo SI-SchedulingInfo OPTIONAL, -- Need R + servingCellConfigCommon ServingCellConfigCommonSIB OPTIONAL, -- Need R + ims-EmergencySupport ENUMERATED {true} OPTIONAL, -- Need R + eCallOverIMS-Support ENUMERATED {true} OPTIONAL, -- Need R + ue-TimersAndConstants UE-TimersAndConstants OPTIONAL, -- Need R + uac-BarringInfo SEQUENCE { + uac-BarringForCommon UAC-BarringPerCatList OPTIONAL, -- Need S + uac-BarringPerPLMN-List UAC-BarringPerPLMN-List OPTIONAL, -- Need S + uac-BarringInfoSetList UAC-BarringInfoSetList, + uac-AccessCategory1-SelectionAssistanceInfo CHOICE { + plmnCommon UAC-AccessCategory1-SelectionAssistanceInfo, + individualPLMNList SEQUENCE (SIZE (2..maxPLMN)) OF UAC-AccessCategory1-SelectionAssistanceInfo + } OPTIONAL -- Need S + } OPTIONAL, -- Need R + useFullResumeID ENUMERATED {true} OPTIONAL, -- Need R + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SIB1-v1610-IEs OPTIONAL +} + +SIB1-v1610-IEs ::= SEQUENCE { + idleModeMeasurementsEUTRA-r16 ENUMERATED{true} OPTIONAL, -- Need R + idleModeMeasurementsNR-r16 ENUMERATED{true} OPTIONAL, -- Need R + posSI-SchedulingInfo-r16 PosSI-SchedulingInfo-r16 OPTIONAL, -- Need R + nonCriticalExtension SIB1-v1630-IEs OPTIONAL +} + +SIB1-v1630-IEs ::= SEQUENCE { + uac-BarringInfo-v1630 SEQUENCE { + uac-AC1-SelectAssistInfo-r16 SEQUENCE (SIZE (2..maxPLMN)) OF UAC-AC1-SelectAssistInfo-r16 + } OPTIONAL, -- Need R + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +UAC-AccessCategory1-SelectionAssistanceInfo ::= ENUMERATED {a, b, c} + +UAC-AC1-SelectAssistInfo-r16 ::= ENUMERATED {a, b, c, notConfigured} + +-- TAG-SIB1-STOP +-- TAG-SIDELINKUEINFORMATIONNR-START + +SidelinkUEInformationNR-r16::= SEQUENCE { + criticalExtensions CHOICE { + sidelinkUEInformationNR-r16 SidelinkUEInformationNR-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +SidelinkUEInformationNR-r16-IEs ::= SEQUENCE { + sl-RxInterestedFreqList-r16 SL-InterestedFreqList-r16 OPTIONAL, + sl-TxResourceReqList-r16 SL-TxResourceReqList-r16 OPTIONAL, + sl-FailureList-r16 SL-FailureList-r16 OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +SL-InterestedFreqList-r16 ::= SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF INTEGER (1..maxNrofFreqSL-r16) + +SL-TxResourceReqList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-Dest-r16)) OF SL-TxResourceReq-r16 + +SL-TxResourceReq-r16 ::= SEQUENCE { + sl-DestinationIdentity-r16 SL-DestinationIdentity-r16, + sl-CastType-r16 ENUMERATED {broadcast, groupcast, unicast, spare1}, + sl-RLC-ModeIndicationList-r16 SEQUENCE (SIZE (1.. maxNrofSLRB-r16)) OF SL-RLC-ModeIndication-r16 OPTIONAL, + sl-QoS-InfoList-r16 SEQUENCE (SIZE (1..maxNrofSL-QFIsPerDest-r16)) OF SL-QoS-Info-r16 OPTIONAL, + sl-TypeTxSyncList-r16 SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF SL-TypeTxSync-r16 OPTIONAL, + sl-TxInterestedFreqList-r16 SL-TxInterestedFreqList-r16 OPTIONAL, + sl-CapabilityInformationSidelink-r16 OCTET STRING OPTIONAL +} + +SL-TxInterestedFreqList-r16 ::= SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF INTEGER (1..maxNrofFreqSL-r16) + +SL-QoS-Info-r16 ::= SEQUENCE { + sl-QoS-FlowIdentity-r16 SL-QoS-FlowIdentity-r16, + sl-QoS-Profile-r16 SL-QoS-Profile-r16 OPTIONAL +} + +SL-RLC-ModeIndication-r16 ::= SEQUENCE { + sl-Mode-r16 CHOICE { + sl-AM-Mode-r16 NULL, + sl-UM-Mode-r16 NULL + }, + sl-QoS-InfoList-r16 SEQUENCE (SIZE (1..maxNrofSL-QFIsPerDest-r16)) OF SL-QoS-Info-r16 +} + +SL-FailureList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-Dest-r16)) OF SL-Failure-r16 + +SL-Failure-r16 ::= SEQUENCE { + sl-DestinationIdentity-r16 SL-DestinationIdentity-r16, + sl-Failure-r16 ENUMERATED {rlf,configFailure, spare6, spare5, spare4, spare3, spare2, spare1} +} + +-- TAG-SIDELINKUEINFORMATIONNR-STOP +-- TAG-SYSTEMINFORMATION-START + +SystemInformation ::= SEQUENCE { + criticalExtensions CHOICE { + systemInformation SystemInformation-IEs, + criticalExtensionsFuture-r16 CHOICE { + posSystemInformation-r16 PosSystemInformation-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } + } +} + +SystemInformation-IEs ::= SEQUENCE { + sib-TypeAndInfo SEQUENCE (SIZE (1..maxSIB)) OF CHOICE { + sib2 SIB2, + sib3 SIB3, + sib4 SIB4, + sib5 SIB5, + sib6 SIB6, + sib7 SIB7, + sib8 SIB8, + sib9 SIB9, + ..., + sib10-v1610 SIB10-r16, + sib11-v1610 SIB11-r16, + sib12-v1610 SIB12-r16, + sib13-v1610 SIB13-r16, + sib14-v1610 SIB14-r16 + }, + + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-SYSTEMINFORMATION-STOP +-- TAG-UEASSISTANCEINFORMATION-START + +UEAssistanceInformation ::= SEQUENCE { + criticalExtensions CHOICE { + ueAssistanceInformation UEAssistanceInformation-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +UEAssistanceInformation-IEs ::= SEQUENCE { + delayBudgetReport DelayBudgetReport OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension UEAssistanceInformation-v1540-IEs OPTIONAL +} + +DelayBudgetReport::= CHOICE { + type1 ENUMERATED { + msMinus1280, msMinus640, msMinus320, msMinus160,msMinus80, msMinus60, msMinus40, + msMinus20, ms0, ms20,ms40, ms60, ms80, ms160, ms320, ms640, ms1280}, + ... +} + +UEAssistanceInformation-v1540-IEs ::= SEQUENCE { + overheatingAssistance OverheatingAssistance OPTIONAL, + nonCriticalExtension UEAssistanceInformation-v1610-IEs OPTIONAL +} + +OverheatingAssistance ::= SEQUENCE { + reducedMaxCCs ReducedMaxCCs-r16 OPTIONAL, + reducedMaxBW-FR1 ReducedMaxBW-FRx-r16 OPTIONAL, + reducedMaxBW-FR2 ReducedMaxBW-FRx-r16 OPTIONAL, + reducedMaxMIMO-LayersFR1 SEQUENCE { + reducedMIMO-LayersFR1-DL MIMO-LayersDL, + reducedMIMO-LayersFR1-UL MIMO-LayersUL + } OPTIONAL, + reducedMaxMIMO-LayersFR2 SEQUENCE { + reducedMIMO-LayersFR2-DL MIMO-LayersDL, + reducedMIMO-LayersFR2-UL MIMO-LayersUL + } OPTIONAL +} + +ReducedAggregatedBandwidth ::= ENUMERATED {mhz0, mhz10, mhz20, mhz30, mhz40, mhz50, mhz60, mhz80, mhz100, mhz200, mhz300, mhz400} + +UEAssistanceInformation-v1610-IEs ::= SEQUENCE { + idc-Assistance-r16 IDC-Assistance-r16 OPTIONAL, + drx-Preference-r16 DRX-Preference-r16 OPTIONAL, + maxBW-Preference-r16 MaxBW-Preference-r16 OPTIONAL, + maxCC-Preference-r16 MaxCC-Preference-r16 OPTIONAL, + maxMIMO-LayerPreference-r16 MaxMIMO-LayerPreference-r16 OPTIONAL, + minSchedulingOffsetPreference-r16 MinSchedulingOffsetPreference-r16 OPTIONAL, + releasePreference-r16 ReleasePreference-r16 OPTIONAL, + sl-UE-AssistanceInformationNR-r16 SL-UE-AssistanceInformationNR-r16 OPTIONAL, + referenceTimeInfoPreference-r16 BOOLEAN OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +IDC-Assistance-r16 ::= SEQUENCE { + affectedCarrierFreqList-r16 AffectedCarrierFreqList-r16 OPTIONAL, + affectedCarrierFreqCombList-r16 AffectedCarrierFreqCombList-r16 OPTIONAL, + ... +} + +AffectedCarrierFreqList-r16 ::= SEQUENCE (SIZE (1.. maxFreqIDC-r16)) OF AffectedCarrierFreq-r16 + +AffectedCarrierFreq-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueNR, + interferenceDirection-r16 ENUMERATED {nr, other, both, spare} +} + +AffectedCarrierFreqCombList-r16 ::= SEQUENCE (SIZE (1..maxCombIDC-r16)) OF AffectedCarrierFreqComb-r16 + +AffectedCarrierFreqComb-r16 ::= SEQUENCE { + affectedCarrierFreqComb-r16 SEQUENCE (SIZE (2..maxNrofServingCells)) OF ARFCN-ValueNR OPTIONAL, + victimSystemType-r16 VictimSystemType-r16 +} + +VictimSystemType-r16 ::= SEQUENCE { + gps-r16 ENUMERATED {true} OPTIONAL, + glonass-r16 ENUMERATED {true} OPTIONAL, + bds-r16 ENUMERATED {true} OPTIONAL, + galileo-r16 ENUMERATED {true} OPTIONAL, + navIC-r16 ENUMERATED {true} OPTIONAL, + wlan-r16 ENUMERATED {true} OPTIONAL, + bluetooth-r16 ENUMERATED {true} OPTIONAL, + ... +} + +DRX-Preference-r16 ::= SEQUENCE { + preferredDRX-InactivityTimer-r16 ENUMERATED { + ms0, ms1, ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, ms40, ms50, ms60, ms80, + ms100, ms200, ms300, ms500, ms750, ms1280, ms1920, ms2560, spare9, spare8, + spare7, spare6, spare5, spare4, spare3, spare2, spare1} OPTIONAL, + preferredDRX-LongCycle-r16 ENUMERATED { + ms10, ms20, ms32, ms40, ms60, ms64, ms70, ms80, ms128, ms160, ms256, ms320, ms512, + ms640, ms1024, ms1280, ms2048, ms2560, ms5120, ms10240, spare12, spare11, spare10, + spare9, spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 } OPTIONAL, + preferredDRX-ShortCycle-r16 ENUMERATED { + ms2, ms3, ms4, ms5, ms6, ms7, ms8, ms10, ms14, ms16, ms20, ms30, ms32, + ms35, ms40, ms64, ms80, ms128, ms160, ms256, ms320, ms512, ms640, spare9, + spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 } OPTIONAL, + preferredDRX-ShortCycleTimer-r16 INTEGER (1..16) OPTIONAL +} + +MaxBW-Preference-r16 ::= SEQUENCE { + reducedMaxBW-FR1-r16 ReducedMaxBW-FRx-r16 OPTIONAL, + reducedMaxBW-FR2-r16 ReducedMaxBW-FRx-r16 OPTIONAL +} + +MaxCC-Preference-r16 ::= SEQUENCE { + reducedMaxCCs-r16 ReducedMaxCCs-r16 OPTIONAL +} + +MaxMIMO-LayerPreference-r16 ::= SEQUENCE { + reducedMaxMIMO-LayersFR1-r16 SEQUENCE { + reducedMIMO-LayersFR1-DL-r16 INTEGER (1..8), + reducedMIMO-LayersFR1-UL-r16 INTEGER (1..4) + } OPTIONAL, + reducedMaxMIMO-LayersFR2-r16 SEQUENCE { + reducedMIMO-LayersFR2-DL-r16 INTEGER (1..8), + reducedMIMO-LayersFR2-UL-r16 INTEGER (1..4) + } OPTIONAL +} + +MinSchedulingOffsetPreference-r16 ::= SEQUENCE { + preferredK0-r16 SEQUENCE { + preferredK0-SCS-15kHz-r16 ENUMERATED {sl1, sl2, sl4, sl6} OPTIONAL, + preferredK0-SCS-30kHz-r16 ENUMERATED {sl1, sl2, sl4, sl6} OPTIONAL, + preferredK0-SCS-60kHz-r16 ENUMERATED {sl2, sl4, sl8, sl12} OPTIONAL, + preferredK0-SCS-120kHz-r16 ENUMERATED {sl2, sl4, sl8, sl12} OPTIONAL + } OPTIONAL, + preferredK2-r16 SEQUENCE { + preferredK2-SCS-15kHz-r16 ENUMERATED {sl1, sl2, sl4, sl6} OPTIONAL, + preferredK2-SCS-30kHz-r16 ENUMERATED {sl1, sl2, sl4, sl6} OPTIONAL, + preferredK2-SCS-60kHz-r16 ENUMERATED {sl2, sl4, sl8, sl12} OPTIONAL, + preferredK2-SCS-120kHz-r16 ENUMERATED {sl2, sl4, sl8, sl12} OPTIONAL + } OPTIONAL +} + +ReleasePreference-r16 ::= SEQUENCE { + preferredRRC-State-r16 ENUMERATED {idle, inactive, connected, outOfConnected} +} + +ReducedMaxBW-FRx-r16 ::= SEQUENCE { + reducedBW-DL-r16 ReducedAggregatedBandwidth, + reducedBW-UL-r16 ReducedAggregatedBandwidth +} + +ReducedMaxCCs-r16 ::= SEQUENCE { + reducedCCsDL-r16 INTEGER (0..31), + reducedCCsUL-r16 INTEGER (0..31) +} + +SL-UE-AssistanceInformationNR-r16 ::= SEQUENCE (SIZE (1..maxNrofTrafficPattern-r16)) OF SL-TrafficPatternInfo-r16 + +SL-TrafficPatternInfo-r16::= SEQUENCE { + trafficPeriodicity-r16 ENUMERATED {ms20, ms50, ms100, ms200, ms300, ms400, ms500, ms600, ms700, ms800, ms900, ms1000}, + timingOffset-r16 INTEGER (0..10239), + messageSize-r16 BIT STRING (SIZE (8)), + sl-QoS-FlowIdentity-r16 SL-QoS-FlowIdentity-r16 +} + +-- TAG-UEASSISTANCEINFORMATION-STOP +-- TAG-UECAPABILITYENQUIRY-START + +UECapabilityEnquiry ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + ueCapabilityEnquiry UECapabilityEnquiry-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +UECapabilityEnquiry-IEs ::= SEQUENCE { + ue-CapabilityRAT-RequestList UE-CapabilityRAT-RequestList, + lateNonCriticalExtension OCTET STRING OPTIONAL, + ue-CapabilityEnquiryExt OCTET STRING (CONTAINING UECapabilityEnquiry-v1560-IEs) OPTIONAL +} + +UECapabilityEnquiry-v1560-IEs ::= SEQUENCE { + capabilityRequestFilterCommon UE-CapabilityRequestFilterCommon OPTIONAL, -- Need N + nonCriticalExtension UECapabilityEnquiry-v1610-IEs OPTIONAL +} + +UECapabilityEnquiry-v1610-IEs ::= SEQUENCE { + rrc-SegAllowed-r16 ENUMERATED {enabled} OPTIONAL, -- Need N + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-UECAPABILITYENQUIRY-STOP +-- TAG-UECAPABILITYINFORMATION-START + +UECapabilityInformation ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + ueCapabilityInformation UECapabilityInformation-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +UECapabilityInformation-IEs ::= SEQUENCE { + ue-CapabilityRAT-ContainerList UE-CapabilityRAT-ContainerList OPTIONAL, + + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-UECAPABILITYINFORMATION-STOP +-- TAG-UEINFORMATIONREQUEST-START + +UEInformationRequest-r16 ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + ueInformationRequest-r16 UEInformationRequest-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +UEInformationRequest-r16-IEs ::= SEQUENCE { + idleModeMeasurementReq-r16 ENUMERATED{true} OPTIONAL, -- Need N + logMeasReportReq-r16 ENUMERATED {true} OPTIONAL, -- Need N + connEstFailReportReq-r16 ENUMERATED {true} OPTIONAL, -- Need N + ra-ReportReq-r16 ENUMERATED {true} OPTIONAL, -- Need N + rlf-ReportReq-r16 ENUMERATED {true} OPTIONAL, -- Need N + mobilityHistoryReportReq-r16 ENUMERATED {true} OPTIONAL, -- Need N + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-UEINFORMATIONREQUEST-STOP +-- TAG-UEINFORMATIONRESPONSE-START + +UEInformationResponse-r16 ::= SEQUENCE { + rrc-TransactionIdentifier RRC-TransactionIdentifier, + criticalExtensions CHOICE { + ueInformationResponse-r16 UEInformationResponse-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +UEInformationResponse-r16-IEs ::= SEQUENCE { + measResultIdleEUTRA-r16 MeasResultIdleEUTRA-r16 OPTIONAL, + measResultIdleNR-r16 MeasResultIdleNR-r16 OPTIONAL, + logMeasReport-r16 LogMeasReport-r16 OPTIONAL, + connEstFailReport-r16 ConnEstFailReport-r16 OPTIONAL, + ra-ReportList-r16 RA-ReportList-r16 OPTIONAL, + rlf-Report-r16 RLF-Report-r16 OPTIONAL, + mobilityHistoryReport-r16 MobilityHistoryReport-r16 OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +LogMeasReport-r16 ::= SEQUENCE { + absoluteTimeStamp-r16 AbsoluteTimeInfo-r16, + traceReference-r16 TraceReference-r16, + traceRecordingSessionRef-r16 OCTET STRING (SIZE (2)), + tce-Id-r16 OCTET STRING (SIZE (1)), + logMeasInfoList-r16 LogMeasInfoList-r16, + logMeasAvailable-r16 ENUMERATED {true} OPTIONAL, + logMeasAvailableBT-r16 ENUMERATED {true} OPTIONAL, + logMeasAvailableWLAN-r16 ENUMERATED {true} OPTIONAL, + ... +} + +LogMeasInfoList-r16 ::= SEQUENCE (SIZE (1..maxLogMeasReport-r16)) OF LogMeasInfo-r16 + +LogMeasInfo-r16 ::= SEQUENCE { + locationInfo-r16 LocationInfo-r16 OPTIONAL, + relativeTimeStamp-r16 INTEGER (0..7200), + servCellIdentity-r16 CGI-Info-Logging-r16 OPTIONAL, + measResultServingCell-r16 MeasResultServingCell-r16 OPTIONAL, + measResultNeighCells-r16 SEQUENCE { + measResultNeighCellListNR MeasResultListLogging2NR-r16 OPTIONAL, + measResultNeighCellListEUTRA MeasResultList2EUTRA-r16 OPTIONAL + }, + anyCellSelectionDetected-r16 ENUMERATED {true} OPTIONAL, + ... +} + +ConnEstFailReport-r16 ::= SEQUENCE { + measResultFailedCell-r16 MeasResultFailedCell-r16, + locationInfo-r16 LocationInfo-r16 OPTIONAL, + measResultNeighCells-r16 SEQUENCE { + measResultNeighCellListNR MeasResultList2NR-r16 OPTIONAL, + measResultNeighCellListEUTRA MeasResultList2EUTRA-r16 OPTIONAL + }, + numberOfConnFail-r16 INTEGER (1..8), + perRAInfoList-r16 PerRAInfoList-r16, + timeSinceFailure-r16 TimeSinceFailure-r16, + ... +} + +MeasResultServingCell-r16 ::= SEQUENCE { + resultsSSB-Cell MeasQuantityResults, + resultsSSB SEQUENCE{ + best-ssb-Index SSB-Index, + best-ssb-Results MeasQuantityResults, + numberOfGoodSSB INTEGER (1..maxNrofSSBs-r16) + } OPTIONAL +} + +MeasResultFailedCell-r16 ::= SEQUENCE { + cgi-Info CGI-Info-Logging-r16, + measResult-r16 SEQUENCE { + cellResults-r16 SEQUENCE{ + resultsSSB-Cell-r16 MeasQuantityResults + }, + rsIndexResults-r16 SEQUENCE{ + resultsSSB-Indexes-r16 ResultsPerSSB-IndexList + } + } +} + +RA-ReportList-r16 ::= SEQUENCE (SIZE (1..maxRAReport-r16)) OF RA-Report-r16 + +RA-Report-r16 ::= SEQUENCE { + cellId-r16 CHOICE { + cellGlobalId-r16 CGI-Info-Logging-r16, + pci-arfcn-r16 SEQUENCE { + physCellId-r16 PhysCellId, + carrierFreq-r16 ARFCN-ValueNR + } + }, + ra-InformationCommon-r16 RA-InformationCommon-r16 OPTIONAL, + raPurpose-r16 ENUMERATED {accessRelated, beamFailureRecovery, reconfigurationWithSync, ulUnSynchronized, + schedulingRequestFailure, noPUCCHResourceAvailable, requestForOtherSI, + spare9, spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1}, + ... +} + +RA-InformationCommon-r16 ::= SEQUENCE { + absoluteFrequencyPointA-r16 ARFCN-ValueNR, + locationAndBandwidth-r16 INTEGER (0..37949), + subcarrierSpacing-r16 SubcarrierSpacing, + msg1-FrequencyStart-r16 INTEGER (0..maxNrofPhysicalResourceBlocks-1) OPTIONAL, + msg1-FrequencyStartCFRA-r16 INTEGER (0..maxNrofPhysicalResourceBlocks-1) OPTIONAL, + msg1-SubcarrierSpacing-r16 SubcarrierSpacing OPTIONAL, + msg1-SubcarrierSpacingCFRA-r16 SubcarrierSpacing OPTIONAL, + msg1-FDM-r16 ENUMERATED {one, two, four, eight} OPTIONAL, + msg1-FDMCFRA-r16 ENUMERATED {one, two, four, eight} OPTIONAL, + perRAInfoList-r16 PerRAInfoList-r16, + ... +} + +PerRAInfoList-r16 ::= SEQUENCE (SIZE (1..200)) OF PerRAInfo-r16 + +PerRAInfo-r16 ::= CHOICE { + perRASSBInfoList-r16 PerRASSBInfo-r16, + perRACSI-RSInfoList-r16 PerRACSI-RSInfo-r16 +} + +PerRASSBInfo-r16 ::= SEQUENCE { + ssb-Index-r16 SSB-Index, + numberOfPreamblesSentOnSSB-r16 INTEGER (1..200), + perRAAttemptInfoList-r16 PerRAAttemptInfoList-r16 +} + +PerRACSI-RSInfo-r16 ::= SEQUENCE { + csi-RS-Index-r16 CSI-RS-Index, + numberOfPreamblesSentOnCSI-RS-r16 INTEGER (1..200) +} + +PerRAAttemptInfoList-r16 ::= SEQUENCE (SIZE (1..200)) OF PerRAAttemptInfo-r16 + +PerRAAttemptInfo-r16 ::= SEQUENCE { + contentionDetected-r16 BOOLEAN OPTIONAL, + dlRSRPAboveThreshold-r16 BOOLEAN OPTIONAL, + ... +} + +RLF-Report-r16 ::= CHOICE { + nr-RLF-Report-r16 SEQUENCE { + measResultLastServCell-r16 MeasResultRLFNR-r16, + measResultNeighCells-r16 SEQUENCE { + measResultListNR-r16 MeasResultList2NR-r16 OPTIONAL, + measResultListEUTRA-r16 MeasResultList2EUTRA-r16 OPTIONAL + } OPTIONAL, + c-RNTI-r16 RNTI-Value, + previousPCellId-r16 CHOICE { + nrPreviousCell-r16 CGI-Info-Logging-r16, + eutraPreviousCell-r16 CGI-InfoEUTRALogging + } OPTIONAL, + failedPCellId-r16 CHOICE { + nrFailedPCellId-r16 CHOICE { + cellGlobalId-r16 CGI-Info-Logging-r16, + pci-arfcn-r16 SEQUENCE { + physCellId-r16 PhysCellId, + carrierFreq-r16 ARFCN-ValueNR + } + }, + eutraFailedPCellId-r16 CHOICE { + cellGlobalId-r16 CGI-InfoEUTRALogging, + pci-arfcn-r16 SEQUENCE { + physCellId-r16 EUTRA-PhysCellId, + carrierFreq-r16 ARFCN-ValueEUTRA + } + } + }, + reconnectCellId-r16 CHOICE { + nrReconnectCellId-r16 CGI-Info-Logging-r16, + eutraReconnectCellId-r16 CGI-InfoEUTRALogging + } OPTIONAL, + timeUntilReconnection-16 TimeUntilReconnection-16 OPTIONAL, + reestablishmentCellId-r16 CGI-Info-Logging-r16 OPTIONAL, + timeConnFailure-r16 INTEGER (0..1023) OPTIONAL, + timeSinceFailure-r16 TimeSinceFailure-r16, + connectionFailureType-r16 ENUMERATED {rlf, hof}, + rlf-Cause-r16 ENUMERATED {t310-Expiry, randomAccessProblem, rlc-MaxNumRetx, + beamFailureRecoveryFailure, lbtFailure-r16, + bh-rlfRecoveryFailure, spare2, spare1}, + locationInfo-r16 LocationInfo-r16 OPTIONAL, + noSuitableCellFound-r16 ENUMERATED {true} OPTIONAL, + ra-InformationCommon-r16 RA-InformationCommon-r16 OPTIONAL, + ... + }, + eutra-RLF-Report-r16 SEQUENCE { + failedPCellId-EUTRA CGI-InfoEUTRALogging, + measResult-RLF-Report-EUTRA-r16 OCTET STRING, + ... + } +} + +MeasResultList2NR-r16 ::= SEQUENCE(SIZE (1..maxFreq)) OF MeasResult2NR-r16 +MeasResultList2EUTRA-r16 ::= SEQUENCE(SIZE (1..maxFreq)) OF MeasResult2EUTRA-r16 + +MeasResult2NR-r16 ::= SEQUENCE { + ssbFrequency-r16 ARFCN-ValueNR OPTIONAL, + refFreqCSI-RS-r16 ARFCN-ValueNR OPTIONAL, + measResultList-r16 MeasResultListNR +} + +MeasResultListLogging2NR-r16 ::= SEQUENCE(SIZE (1..maxFreq)) OF MeasResultLogging2NR-r16 + +MeasResultLogging2NR-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueNR, + measResultListLoggingNR-r16 MeasResultListLoggingNR-r16 +} + +MeasResultListLoggingNR-r16 ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultLoggingNR-r16 + +MeasResultLoggingNR-r16 ::= SEQUENCE { + physCellId-r16 PhysCellId, + resultsSSB-Cell-r16 MeasQuantityResults, + numberOfGoodSSB-r16 INTEGER (1..maxNrofSSBs-r16) OPTIONAL +} + +MeasResult2EUTRA-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueEUTRA, + measResultList-r16 MeasResultListEUTRA +} + +MeasResultRLFNR-r16 ::= SEQUENCE { + measResult-r16 SEQUENCE { + cellResults-r16 SEQUENCE{ + resultsSSB-Cell-r16 MeasQuantityResults OPTIONAL, + resultsCSI-RS-Cell-r16 MeasQuantityResults OPTIONAL + }, + rsIndexResults-r16 SEQUENCE{ + resultsSSB-Indexes-r16 ResultsPerSSB-IndexList OPTIONAL, + ssbRLMConfigBitmap-r16 BIT STRING (SIZE (64)) OPTIONAL, + resultsCSI-RS-Indexes-r16 ResultsPerCSI-RS-IndexList OPTIONAL, + csi-rsRLMConfigBitmap-r16 BIT STRING (SIZE (96)) OPTIONAL + } OPTIONAL + } +} + +TimeSinceFailure-r16 ::= INTEGER (0..172800) + +MobilityHistoryReport-r16 ::= VisitedCellInfoList-r16 + +TimeUntilReconnection-16 ::= INTEGER (0..172800) + +-- TAG-UEINFORMATIONRESPONSE-STOP +-- TAG-ULDEDICATEDMESSAGESEGMENT-START + +ULDedicatedMessageSegment-r16 ::= SEQUENCE { + criticalExtensions CHOICE { + ulDedicatedMessageSegment-r16 ULDedicatedMessageSegment-r16-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +ULDedicatedMessageSegment-r16-IEs ::= SEQUENCE { + segmentNumber-r16 INTEGER (0..15), + rrc-MessageSegmentContainer-r16 OCTET STRING, + rrc-MessageSegmentType-r16 ENUMERATED {notLastSegment, lastSegment}, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-ULDEDICATEDMESSAGESEGMENT-STOP +-- TAG-ULINFORMATIONTRANSFER-START + +ULInformationTransfer ::= SEQUENCE { + criticalExtensions CHOICE { + ulInformationTransfer ULInformationTransfer-IEs, + criticalExtensionsFuture SEQUENCE {} + } +} + +ULInformationTransfer-IEs ::= SEQUENCE { + dedicatedNAS-Message DedicatedNAS-Message OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-ULINFORMATIONTRANSFER-STOP +-- TAG-ULINFORMATIONTRANSFERIRAT-START + +ULInformationTransferIRAT-r16 ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE { + ulInformationTransferIRAT-r16 ULInformationTransferIRAT-r16-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +ULInformationTransferIRAT-r16-IEs ::= SEQUENCE { + ul-DCCH-MessageEUTRA-r16 OCTET STRING OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-ULINFORMATIONTRANSFERIRAT-STOP +-- TAG-ULINFORMATIONTRANSFERMRDC-START + +ULInformationTransferMRDC ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE { + ulInformationTransferMRDC ULInformationTransferMRDC-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +ULInformationTransferMRDC-IEs::= SEQUENCE { + ul-DCCH-MessageNR OCTET STRING OPTIONAL, + ul-DCCH-MessageEUTRA OCTET STRING OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-ULINFORMATIONTRANSFERMRDC-STOP +-- TAG-SETUPRELEASE-START + +SetupRelease { ElementTypeParam } ::= CHOICE { + release NULL, + setup ElementTypeParam +} + +-- TAG-SETUPRELEASE-STOP +-- TAG-SIB2-START + +SIB2 ::= SEQUENCE { + cellReselectionInfoCommon SEQUENCE { + nrofSS-BlocksToAverage INTEGER (2..maxNrofSS-BlocksToAverage) OPTIONAL, -- Need S + absThreshSS-BlocksConsolidation ThresholdNR OPTIONAL, -- Need S + rangeToBestCell RangeToBestCell OPTIONAL, -- Need R + q-Hyst ENUMERATED { + dB0, dB1, dB2, dB3, dB4, dB5, dB6, dB8, dB10, + dB12, dB14, dB16, dB18, dB20, dB22, dB24}, + speedStateReselectionPars SEQUENCE { + mobilityStateParameters MobilityStateParameters, + q-HystSF SEQUENCE { + sf-Medium ENUMERATED {dB-6, dB-4, dB-2, dB0}, + sf-High ENUMERATED {dB-6, dB-4, dB-2, dB0} + } + } OPTIONAL, -- Need R + ... + }, + cellReselectionServingFreqInfo SEQUENCE { + s-NonIntraSearchP ReselectionThreshold OPTIONAL, -- Need S + s-NonIntraSearchQ ReselectionThresholdQ OPTIONAL, -- Need S + threshServingLowP ReselectionThreshold, + threshServingLowQ ReselectionThresholdQ OPTIONAL, -- Need R + cellReselectionPriority CellReselectionPriority, + cellReselectionSubPriority CellReselectionSubPriority OPTIONAL, -- Need R + ... + }, + intraFreqCellReselectionInfo SEQUENCE { + q-RxLevMin Q-RxLevMin, + q-RxLevMinSUL Q-RxLevMin OPTIONAL, -- Need R + q-QualMin Q-QualMin OPTIONAL, -- Need S + s-IntraSearchP ReselectionThreshold, + s-IntraSearchQ ReselectionThresholdQ OPTIONAL, -- Need S + t-ReselectionNR T-Reselection, + frequencyBandList MultiFrequencyBandListNR-SIB OPTIONAL, -- Need S + frequencyBandListSUL MultiFrequencyBandListNR-SIB OPTIONAL, -- Need R + p-Max P-Max OPTIONAL, -- Need S + smtc SSB-MTC OPTIONAL, -- Need S + ss-RSSI-Measurement SS-RSSI-Measurement OPTIONAL, -- Need R + ssb-ToMeasure SSB-ToMeasure OPTIONAL, -- Need S + deriveSSB-IndexFromCell BOOLEAN, + ..., + [[ + t-ReselectionNR-SF SpeedStateScaleFactors OPTIONAL -- Need N + ]], + [[ + smtc2-LP-r16 SSB-MTC2-LP-r16 OPTIONAL, -- Need R + ssb-PositionQCL-Common-r16 SSB-PositionQCL-Relation-r16 OPTIONAL -- Cond SharedSpectrum + ]] + }, + ..., + [[ + relaxedMeasurement-r16 SEQUENCE { + lowMobilityEvaluation-r16 SEQUENCE { + s-SearchDeltaP-r16 ENUMERATED { + dB3, dB6, dB9, dB12, dB15, + spare3, spare2, spare1}, + t-SearchDeltaP-r16 ENUMERATED { + s5, s10, s20, s30, s60, s120, s180, + s240, s300, spare7, spare6, spare5, + spare4, spare3, spare2, spare1} + } OPTIONAL, -- Need R + cellEdgeEvaluation-r16 SEQUENCE { + s-SearchThresholdP-r16 ReselectionThreshold, + s-SearchThresholdQ-r16 ReselectionThresholdQ OPTIONAL -- Need R + } OPTIONAL, -- Need R + combineRelaxedMeasCondition-r16 ENUMERATED {true} OPTIONAL, -- Need R + highPriorityMeasRelax-r16 ENUMERATED {true} OPTIONAL -- Need R + } OPTIONAL -- Need R + ]] +} + +RangeToBestCell ::= Q-OffsetRange + +-- TAG-SIB2-STOP +-- TAG-SIB3-START + +SIB3 ::= SEQUENCE { + intraFreqNeighCellList IntraFreqNeighCellList OPTIONAL, -- Need R + intraFreqBlackCellList IntraFreqBlackCellList OPTIONAL, -- Need R + lateNonCriticalExtension OCTET STRING OPTIONAL, + ..., + [[ + intraFreqNeighCellList-v1610 IntraFreqNeighCellList-v1610 OPTIONAL, -- Need R + intraFreqWhiteCellList-r16 IntraFreqWhiteCellList-r16 OPTIONAL, -- Cond SharedSpectrum2 + intraFreqCAG-CellList-r16 SEQUENCE (SIZE (1..maxPLMN)) OF IntraFreqCAG-CellListPerPLMN-r16 OPTIONAL -- Need R + ]] +} + + +IntraFreqNeighCellList ::= SEQUENCE (SIZE (1..maxCellIntra)) OF IntraFreqNeighCellInfo + +IntraFreqNeighCellList-v1610::= SEQUENCE (SIZE (1..maxCellIntra)) OF IntraFreqNeighCellInfo-v1610 + +IntraFreqNeighCellInfo ::= SEQUENCE { + physCellId PhysCellId, + q-OffsetCell Q-OffsetRange, + q-RxLevMinOffsetCell INTEGER (1..8) OPTIONAL, -- Need R + q-RxLevMinOffsetCellSUL INTEGER (1..8) OPTIONAL, -- Need R + q-QualMinOffsetCell INTEGER (1..8) OPTIONAL, -- Need R + ... +} + +IntraFreqNeighCellInfo-v1610 ::= SEQUENCE { + ssb-PositionQCL-r16 SSB-PositionQCL-Relation-r16 OPTIONAL -- Cond SharedSpectrum2 +} + +IntraFreqBlackCellList ::= SEQUENCE (SIZE (1..maxCellBlack)) OF PCI-Range + +IntraFreqWhiteCellList-r16 ::= SEQUENCE (SIZE (1..maxCellWhite)) OF PCI-Range + +IntraFreqCAG-CellListPerPLMN-r16 ::= SEQUENCE { + plmn-IdentityIndex-r16 INTEGER (1..maxPLMN), + cag-CellList-r16 SEQUENCE (SIZE (1..maxCAG-Cell-r16)) OF PCI-Range +} + +-- TAG-SIB3-STOP +-- TAG-SIB4-START + +SIB4 ::= SEQUENCE { + interFreqCarrierFreqList InterFreqCarrierFreqList, + lateNonCriticalExtension OCTET STRING OPTIONAL, + ..., + [[ + interFreqCarrierFreqList-v1610 InterFreqCarrierFreqList-v1610 OPTIONAL -- Need R + ]] +} + +InterFreqCarrierFreqList ::= SEQUENCE (SIZE (1..maxFreq)) OF InterFreqCarrierFreqInfo + +InterFreqCarrierFreqList-v1610 ::= SEQUENCE (SIZE (1..maxFreq)) OF InterFreqCarrierFreqInfo-v1610 + +InterFreqCarrierFreqInfo ::= SEQUENCE { + dl-CarrierFreq ARFCN-ValueNR, + frequencyBandList MultiFrequencyBandListNR-SIB OPTIONAL, -- Cond Mandatory + frequencyBandListSUL MultiFrequencyBandListNR-SIB OPTIONAL, -- Need R + nrofSS-BlocksToAverage INTEGER (2..maxNrofSS-BlocksToAverage) OPTIONAL, -- Need S + absThreshSS-BlocksConsolidation ThresholdNR OPTIONAL, -- Need S + smtc SSB-MTC OPTIONAL, -- Need S + ssbSubcarrierSpacing SubcarrierSpacing, + ssb-ToMeasure SSB-ToMeasure OPTIONAL, -- Need S + deriveSSB-IndexFromCell BOOLEAN, + ss-RSSI-Measurement SS-RSSI-Measurement OPTIONAL, + q-RxLevMin Q-RxLevMin, + q-RxLevMinSUL Q-RxLevMin OPTIONAL, -- Need R + q-QualMin Q-QualMin OPTIONAL, -- Need S + p-Max P-Max OPTIONAL, -- Need S + t-ReselectionNR T-Reselection, + t-ReselectionNR-SF SpeedStateScaleFactors OPTIONAL, -- Need S + threshX-HighP ReselectionThreshold, + threshX-LowP ReselectionThreshold, + threshX-Q SEQUENCE { + threshX-HighQ ReselectionThresholdQ, + threshX-LowQ ReselectionThresholdQ + } OPTIONAL, -- Cond RSRQ + cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need R + cellReselectionSubPriority CellReselectionSubPriority OPTIONAL, -- Need R + q-OffsetFreq Q-OffsetRange DEFAULT dB0, + interFreqNeighCellList InterFreqNeighCellList OPTIONAL, -- Need R + interFreqBlackCellList InterFreqBlackCellList OPTIONAL, -- Need R + ... +} + +InterFreqCarrierFreqInfo-v1610 ::= SEQUENCE { + interFreqNeighCellList-v1610 InterFreqNeighCellList-v1610 OPTIONAL, -- Need R + smtc2-LP-r16 SSB-MTC2-LP-r16 OPTIONAL, -- Need R + interFreqWhiteCellList-r16 InterFreqWhiteCellList-r16 OPTIONAL, -- Cond SharedSpectrum2 + ssb-PositionQCL-Common-r16 SSB-PositionQCL-Relation-r16 OPTIONAL, -- Cond SharedSpectrum + interFreqCAG-CellList-r16 SEQUENCE (SIZE (1..maxPLMN)) OF InterFreqCAG-CellListPerPLMN-r16 OPTIONAL -- Need R +} + +InterFreqNeighCellList ::= SEQUENCE (SIZE (1..maxCellInter)) OF InterFreqNeighCellInfo + +InterFreqNeighCellList-v1610 ::= SEQUENCE (SIZE (1..maxCellInter)) OF InterFreqNeighCellInfo-v1610 + +InterFreqNeighCellInfo ::= SEQUENCE { + physCellId PhysCellId, + q-OffsetCell Q-OffsetRange, + q-RxLevMinOffsetCell INTEGER (1..8) OPTIONAL, -- Need R + q-RxLevMinOffsetCellSUL INTEGER (1..8) OPTIONAL, -- Need R + q-QualMinOffsetCell INTEGER (1..8) OPTIONAL, -- Need R + ... +} + +InterFreqNeighCellInfo-v1610 ::= SEQUENCE { + ssb-PositionQCL-r16 SSB-PositionQCL-Relation-r16 OPTIONAL -- Cond SharedSpectrum2 +} + +InterFreqBlackCellList ::= SEQUENCE (SIZE (1..maxCellBlack)) OF PCI-Range + +InterFreqWhiteCellList-r16 ::= SEQUENCE (SIZE (1..maxCellWhite)) OF PCI-Range + +InterFreqCAG-CellListPerPLMN-r16 ::= SEQUENCE { + plmn-IdentityIndex-r16 INTEGER (1..maxPLMN), + cag-CellList-r16 SEQUENCE (SIZE (1..maxCAG-Cell-r16)) OF PCI-Range +} + +-- TAG-SIB4-STOP +-- TAG-SIB5-START + +SIB5 ::= SEQUENCE { + carrierFreqListEUTRA CarrierFreqListEUTRA OPTIONAL, -- Need R + t-ReselectionEUTRA T-Reselection, + t-ReselectionEUTRA-SF SpeedStateScaleFactors OPTIONAL, -- Need S + lateNonCriticalExtension OCTET STRING OPTIONAL, + ..., + [[ + carrierFreqListEUTRA-v1610 CarrierFreqListEUTRA-v1610 OPTIONAL -- Need R + ]] +} + +CarrierFreqListEUTRA ::= SEQUENCE (SIZE (1..maxEUTRA-Carrier)) OF CarrierFreqEUTRA + +CarrierFreqListEUTRA-v1610 ::= SEQUENCE (SIZE (1..maxEUTRA-Carrier)) OF CarrierFreqEUTRA-v1610 + +CarrierFreqEUTRA ::= SEQUENCE { + carrierFreq ARFCN-ValueEUTRA, + eutra-multiBandInfoList EUTRA-MultiBandInfoList OPTIONAL, -- Need R + eutra-FreqNeighCellList EUTRA-FreqNeighCellList OPTIONAL, -- Need R + eutra-BlackCellList EUTRA-FreqBlackCellList OPTIONAL, -- Need R + allowedMeasBandwidth EUTRA-AllowedMeasBandwidth, + presenceAntennaPort1 EUTRA-PresenceAntennaPort1, + cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need R + cellReselectionSubPriority CellReselectionSubPriority OPTIONAL, -- Need R + threshX-High ReselectionThreshold, + threshX-Low ReselectionThreshold, + q-RxLevMin INTEGER (-70..-22), + q-QualMin INTEGER (-34..-3), + p-MaxEUTRA INTEGER (-30..33), + threshX-Q SEQUENCE { + threshX-HighQ ReselectionThresholdQ, + threshX-LowQ ReselectionThresholdQ + } OPTIONAL -- Cond RSRQ +} + +CarrierFreqEUTRA-v1610 ::= SEQUENCE { + highSpeedEUTRACarrier-r16 ENUMERATED {true} OPTIONAL -- Need R +} + +EUTRA-FreqBlackCellList ::= SEQUENCE (SIZE (1..maxEUTRA-CellBlack)) OF EUTRA-PhysCellIdRange + +EUTRA-FreqNeighCellList ::= SEQUENCE (SIZE (1..maxCellEUTRA)) OF EUTRA-FreqNeighCellInfo + +EUTRA-FreqNeighCellInfo ::= SEQUENCE { + physCellId EUTRA-PhysCellId, + dummy EUTRA-Q-OffsetRange, + q-RxLevMinOffsetCell INTEGER (1..8) OPTIONAL, -- Need R + q-QualMinOffsetCell INTEGER (1..8) OPTIONAL -- Need R +} + +-- TAG-SIB5-STOP +-- TAG-SIB6-START + +SIB6 ::= SEQUENCE { + messageIdentifier BIT STRING (SIZE (16)), + serialNumber BIT STRING (SIZE (16)), + warningType OCTET STRING (SIZE (2)), + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +-- TAG-SIB6-STOP +-- TAG-SIB7-START + +SIB7 ::= SEQUENCE { + messageIdentifier BIT STRING (SIZE (16)), + serialNumber BIT STRING (SIZE (16)), + warningMessageSegmentType ENUMERATED {notLastSegment, lastSegment}, + warningMessageSegmentNumber INTEGER (0..63), + warningMessageSegment OCTET STRING, + dataCodingScheme OCTET STRING (SIZE (1)) OPTIONAL, -- Cond Segment1 + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +-- TAG-SIB7-STOP +-- TAG-SIB8-START + +SIB8 ::= SEQUENCE { + messageIdentifier BIT STRING (SIZE (16)), + serialNumber BIT STRING (SIZE (16)), + warningMessageSegmentType ENUMERATED {notLastSegment, lastSegment}, + warningMessageSegmentNumber INTEGER (0..63), + warningMessageSegment OCTET STRING, + dataCodingScheme OCTET STRING (SIZE (1)) OPTIONAL, -- Cond Segment1 + warningAreaCoordinatesSegment OCTET STRING OPTIONAL, -- Need R + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +-- TAG-SIB8-STOP +-- TAG-SIB9-START + +SIB9 ::= SEQUENCE { + timeInfo SEQUENCE { + timeInfoUTC INTEGER (0..549755813887), + dayLightSavingTime BIT STRING (SIZE (2)) OPTIONAL, -- Need R + leapSeconds INTEGER (-127..128) OPTIONAL, -- Need R + localTimeOffset INTEGER (-63..64) OPTIONAL -- Need R + } OPTIONAL, -- Need R + lateNonCriticalExtension OCTET STRING OPTIONAL, + ..., + [[ + referenceTimeInfo-r16 ReferenceTimeInfo-r16 OPTIONAL -- Need R + ]] +} + +-- TAG-SIB9-STOP +-- TAG-SIB10-START + +SIB10-r16 ::= SEQUENCE { + hrnn-List-r16 HRNN-List-r16 OPTIONAL, -- Need R + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +HRNN-List-r16 ::= SEQUENCE (SIZE (1..maxNPN-r16)) OF HRNN-r16 + +HRNN-r16 ::= SEQUENCE { + hrnn-r16 OCTET STRING (SIZE(1.. maxHRNN-Len-r16)) OPTIONAL -- Need R +} + +-- TAG-SIB10-STOP +-- TAG-SIB11-START + +SIB11-r16 ::= SEQUENCE { + measIdleConfigSIB-r16 MeasIdleConfigSIB-r16 OPTIONAL, -- Need S + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +-- TAG-SIB11-STOP +-- TAG-SIB12-START + +SIB12-r16 ::= SEQUENCE { + segmentNumber-r16 INTEGER (0..63), + segmentType-r16 ENUMERATED {notLastSegment, lastSegment}, + segmentContainer-r16 OCTET STRING +} + +SIB12-IEs-r16 ::= SEQUENCE { + sl-ConfigCommonNR-r16 SL-ConfigCommonNR-r16, + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +SL-ConfigCommonNR-r16 ::= SEQUENCE { + sl-FreqInfoList-r16 SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF SL-FreqConfigCommon-r16 OPTIONAL, -- Need R + sl-UE-SelectedConfig-r16 SL-UE-SelectedConfig-r16 OPTIONAL, -- Need R + sl-NR-AnchorCarrierFreqList-r16 SL-NR-AnchorCarrierFreqList-r16 OPTIONAL, -- Need R + sl-EUTRA-AnchorCarrierFreqList-r16 SL-EUTRA-AnchorCarrierFreqList-r16 OPTIONAL, -- Need R + sl-RadioBearerConfigList-r16 SEQUENCE (SIZE (1..maxNrofSLRB-r16)) OF SL-RadioBearerConfig-r16 OPTIONAL, -- Need R + sl-RLC-BearerConfigList-r16 SEQUENCE (SIZE (1..maxSL-LCID-r16)) OF SL-RLC-BearerConfig-r16 OPTIONAL, -- Need R + sl-MeasConfigCommon-r16 SL-MeasConfigCommon-r16 OPTIONAL, -- Need R + sl-CSI-Acquisition-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + sl-OffsetDFN-r16 INTEGER (1..1000) OPTIONAL, -- Need R + t400-r16 ENUMERATED {ms100, ms200, ms300, ms400, ms600, ms1000, ms1500, ms2000} OPTIONAL, -- Need R + sl-MaxNumConsecutiveDTX-r16 ENUMERATED {n1, n2, n3, n4, n6, n8, n16, n32} OPTIONAL, -- Need R + sl-SSB-PriorityNR-r16 INTEGER (1..8) OPTIONAL -- Need R +} + +SL-NR-AnchorCarrierFreqList-r16 ::= SEQUENCE (SIZE (1..maxFreqSL-NR-r16)) OF ARFCN-ValueNR + +SL-EUTRA-AnchorCarrierFreqList-r16 ::= SEQUENCE (SIZE (1..maxFreqSL-EUTRA-r16)) OF ARFCN-ValueEUTRA + +-- TAG-SIB12-STOP +-- TAG-SIB13-START + +SIB13-r16 ::= SEQUENCE { + sl-V2X-ConfigCommon-r16 OCTET STRING, + dummy OCTET STRING, + tdd-Config-r16 OCTET STRING, + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +-- TAG-SIB13-STOP +-- TAG-SIB14-START + +SIB14-r16 ::= SEQUENCE { + sl-V2X-ConfigCommonExt-r16 OCTET STRING, + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +-- TAG-SIB14-STOP +-- TAG-POSSYSTEMINFORMATION-R16-IES-START + +PosSystemInformation-r16-IEs ::= SEQUENCE { + posSIB-TypeAndInfo-r16 SEQUENCE (SIZE (1..maxSIB)) OF CHOICE { + posSib1-1-r16 SIBpos-r16, + posSib1-2-r16 SIBpos-r16, + posSib1-3-r16 SIBpos-r16, + posSib1-4-r16 SIBpos-r16, + posSib1-5-r16 SIBpos-r16, + posSib1-6-r16 SIBpos-r16, + posSib1-7-r16 SIBpos-r16, + posSib1-8-r16 SIBpos-r16, + posSib2-1-r16 SIBpos-r16, + posSib2-2-r16 SIBpos-r16, + posSib2-3-r16 SIBpos-r16, + posSib2-4-r16 SIBpos-r16, + posSib2-5-r16 SIBpos-r16, + posSib2-6-r16 SIBpos-r16, + posSib2-7-r16 SIBpos-r16, + posSib2-8-r16 SIBpos-r16, + posSib2-9-r16 SIBpos-r16, + posSib2-10-r16 SIBpos-r16, + posSib2-11-r16 SIBpos-r16, + posSib2-12-r16 SIBpos-r16, + posSib2-13-r16 SIBpos-r16, + posSib2-14-r16 SIBpos-r16, + posSib2-15-r16 SIBpos-r16, + posSib2-16-r16 SIBpos-r16, + posSib2-17-r16 SIBpos-r16, + posSib2-18-r16 SIBpos-r16, + posSib2-19-r16 SIBpos-r16, + posSib2-20-r16 SIBpos-r16, + posSib2-21-r16 SIBpos-r16, + posSib2-22-r16 SIBpos-r16, + posSib2-23-r16 SIBpos-r16, + posSib3-1-r16 SIBpos-r16, + posSib4-1-r16 SIBpos-r16, + posSib5-1-r16 SIBpos-r16, + posSib6-1-r16 SIBpos-r16, + posSib6-2-r16 SIBpos-r16, + posSib6-3-r16 SIBpos-r16, + ... + }, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-POSSYSTEMINFORMATION-R16-IES-STOP +-- TAG-POSSI-SCHEDULINGINFO-START + +PosSI-SchedulingInfo-r16 ::= SEQUENCE { + posSchedulingInfoList-r16 SEQUENCE (SIZE (1..maxSI-Message)) OF PosSchedulingInfo-r16, + posSI-RequestConfig-r16 SI-RequestConfig OPTIONAL, -- Cond MSG-1 + posSI-RequestConfigSUL-r16 SI-RequestConfig OPTIONAL, -- Cond SUL-MSG-1 + ... +} + +PosSchedulingInfo-r16 ::= SEQUENCE { + offsetToSI-Used-r16 ENUMERATED {true} OPTIONAL, -- Need R + posSI-Periodicity-r16 ENUMERATED {rf8, rf16, rf32, rf64, rf128, rf256, rf512}, + posSI-BroadcastStatus-r16 ENUMERATED {broadcasting, notBroadcasting}, + posSIB-MappingInfo-r16 PosSIB-MappingInfo-r16, + ... +} + +PosSIB-MappingInfo-r16 ::= SEQUENCE (SIZE (1..maxSIB)) OF PosSIB-Type-r16 + +PosSIB-Type-r16 ::= SEQUENCE { + encrypted-r16 ENUMERATED { true } OPTIONAL, -- Need R + gnss-id-r16 GNSS-ID-r16 OPTIONAL, -- Need R + sbas-id-r16 SBAS-ID-r16 OPTIONAL, -- Need R + posSibType-r16 ENUMERATED { posSibType1-1, posSibType1-2, posSibType1-3, posSibType1-4, posSibType1-5, posSibType1-6, + posSibType1-7, posSibType1-8, posSibType2-1, posSibType2-2, posSibType2-3, posSibType2-4, + posSibType2-5, posSibType2-6, posSibType2-7, posSibType2-8, posSibType2-9, posSibType2-10, + posSibType2-11, posSibType2-12, posSibType2-13, posSibType2-14, posSibType2-15, + posSibType2-16, posSibType2-17, posSibType2-18, posSibType2-19, posSibType2-20, + posSibType2-21, posSibType2-22, posSibType2-23, posSibType3-1, posSibType4-1, + posSibType5-1,posSibType6-1, posSibType6-2, posSibType6-3,... }, + areaScope-r16 ENUMERATED {true} OPTIONAL -- Need S +} + +GNSS-ID-r16 ::= SEQUENCE { + gnss-id-r16 ENUMERATED{gps, sbas, qzss, galileo, glonass, bds, ...}, + ... +} + +SBAS-ID-r16 ::= SEQUENCE { + sbas-id-r16 ENUMERATED { waas, egnos, msas, gagan, ...}, + ... +} + +-- TAG-POSSI-SCHEDULINGINFO-STOP +-- TAG-SIPOS-START + +SIBpos-r16 ::= SEQUENCE { + assistanceDataSIB-Element-r16 OCTET STRING, + lateNonCriticalExtension OCTET STRING OPTIONAL, + ... +} + +-- TAG-SIPOS-STOP +-- TAG-ADDITIONALSPECTRUMEMISSION-START + +AdditionalSpectrumEmission ::= INTEGER (0..7) + +-- TAG-ADDITIONALSPECTRUMEMISSION-STOP +-- TAG-ALPHA-START + +Alpha ::= ENUMERATED {alpha0, alpha04, alpha05, alpha06, alpha07, alpha08, alpha09, alpha1} + +-- TAG-ALPHA-STOP +-- TAG-AMF-IDENTIFIER-START + +AMF-Identifier ::= BIT STRING (SIZE (24)) + +-- TAG-AMF-IDENTIFIER-STOP +-- TAG-ARFCN-VALUEEUTRA-START + +ARFCN-ValueEUTRA ::= INTEGER (0..maxEARFCN) + +-- TAG-ARFCN-VALUEEUTRA-STOP +-- TAG-ARFCN-VALUENR-START + +ARFCN-ValueNR ::= INTEGER (0..maxNARFCN) + +-- TAG-ARFCN-VALUENR-STOP +-- TAG-ARFCN-ValueUTRA-FDD-START + +ARFCN-ValueUTRA-FDD-r16 ::= INTEGER (0..16383) + +-- TAG-ARFCN-ValueUTRA-FDD-STOP +-- TAG-AVAILABILITYCOMBINATIONSPERCELL-START + +AvailabilityCombinationsPerCell-r16 ::= SEQUENCE { + availabilityCombinationsPerCellIndex-r16 AvailabilityCombinationsPerCellIndex-r16, + iab-DU-CellIdentity-r16 CellIdentity, + positionInDCI-AI-r16 INTEGER(0..maxAI-DCI-PayloadSize-r16-1) OPTIONAL, -- Need M + availabilityCombinations-r16 SEQUENCE (SIZE (1..maxNrofAvailabilityCombinationsPerSet-r16)) OF AvailabilityCombination-r16, + ... +} + +AvailabilityCombinationsPerCellIndex-r16 ::= INTEGER(0..maxNrofDUCells-r16) + +AvailabilityCombination-r16 ::= SEQUENCE { + availabilityCombinationId-r16 AvailabilityCombinationId-r16, + resourceAvailability-r16 SEQUENCE (SIZE (1..maxNrofResourceAvailabilityPerCombination-r16)) OF INTEGER (0..7) +} + +AvailabilityCombinationId-r16 ::= INTEGER (0..maxNrofAvailabilityCombinationsPerSet-r16-1) + +-- TAG-AVAILABILITYCOMBINATIONSPERCELL-STOP +-- TAG-AVAILABILITYINDICATOR-START + +AvailabilityIndicator-r16 ::= SEQUENCE { + ai-RNTI-r16 AI-RNTI-r16, + dci-PayloadSizeAI-r16 INTEGER (1..maxAI-DCI-PayloadSize-r16), + availableCombToAddModList-r16 SEQUENCE (SIZE(1..maxNrofDUCells-r16)) OF AvailabilityCombinationsPerCell-r16 OPTIONAL, -- Need N + availableCombToReleaseList-r16 SEQUENCE (SIZE(1..maxNrofDUCells-r16)) OF AvailabilityCombinationsPerCellIndex-r16 OPTIONAL, -- Need N + ... +} + +AI-RNTI-r16 ::= RNTI-Value + +-- TAG-AVAILABILITYINDICATOR-STOP +-- TAG-BAPROUTINGID-START + +BAP-RoutingID-r16::= SEQUENCE{ + bap-Address-r16 BIT STRING (SIZE (10)), + bap-PathId-r16 BIT STRING (SIZE (10)) +} + +-- TAG-BAPROUTINGID-STOP +-- TAG-BEAMFAILURERECOVERYCONFIG-START + +BeamFailureRecoveryConfig ::= SEQUENCE { + rootSequenceIndex-BFR INTEGER (0..137) OPTIONAL, -- Need M + rach-ConfigBFR RACH-ConfigGeneric OPTIONAL, -- Need M + rsrp-ThresholdSSB RSRP-Range OPTIONAL, -- Need M + candidateBeamRSList SEQUENCE (SIZE(1..maxNrofCandidateBeams)) OF PRACH-ResourceDedicatedBFR OPTIONAL, -- Need M + ssb-perRACH-Occasion ENUMERATED {oneEighth, oneFourth, oneHalf, one, two, + four, eight, sixteen} OPTIONAL, -- Need M + ra-ssb-OccasionMaskIndex INTEGER (0..15) OPTIONAL, -- Need M + recoverySearchSpaceId SearchSpaceId OPTIONAL, -- Need R + ra-Prioritization RA-Prioritization OPTIONAL, -- Need R + beamFailureRecoveryTimer ENUMERATED {ms10, ms20, ms40, ms60, ms80, ms100, ms150, ms200} OPTIONAL, -- Need M + ..., + [[ + msg1-SubcarrierSpacing SubcarrierSpacing OPTIONAL -- Need M + ]], + [[ + ra-PrioritizationTwoStep-r16 RA-Prioritization OPTIONAL, -- Need R + candidateBeamRSListExt-v1610 SetupRelease{ CandidateBeamRSListExt-r16 } OPTIONAL -- Need M + ]], + [[ + spCell-BFR-CBRA-r16 ENUMERATED {true} OPTIONAL -- Need R + ]] +} + +PRACH-ResourceDedicatedBFR ::= CHOICE { + ssb BFR-SSB-Resource, + csi-RS BFR-CSIRS-Resource +} + +BFR-SSB-Resource ::= SEQUENCE { + ssb SSB-Index, + ra-PreambleIndex INTEGER (0..63), + ... +} + +BFR-CSIRS-Resource ::= SEQUENCE { + csi-RS NZP-CSI-RS-ResourceId, + ra-OccasionList SEQUENCE (SIZE(1..maxRA-OccasionsPerCSIRS)) OF INTEGER (0..maxRA-Occasions-1) OPTIONAL, -- Need R + ra-PreambleIndex INTEGER (0..63) OPTIONAL, -- Need R + ... +} + +CandidateBeamRSListExt-r16::= SEQUENCE (SIZE(1.. maxNrofCandidateBeamsExt-r16)) OF PRACH-ResourceDedicatedBFR + +-- TAG-BEAMFAILURERECOVERYCONFIG-STOP +-- TAG-BEAMFAILURERECOVERYSCELLCONFIG-START + +BeamFailureRecoverySCellConfig-r16 ::= SEQUENCE { + rsrp-ThresholdBFR-r16 RSRP-Range OPTIONAL, -- Need M + candidateBeamRSSCellList-r16 SEQUENCE (SIZE(1..maxNrofCandidateBeams-r16)) OF CandidateBeamRS-r16 OPTIONAL, -- Need M + ... +} + +CandidateBeamRS-r16 ::= SEQUENCE { + candidateBeamConfig-r16 CHOICE { + ssb-r16 SSB-Index, + csi-RS-r16 NZP-CSI-RS-ResourceId + }, + servingCellId ServCellIndex OPTIONAL -- Need R +} + +-- TAG-BEAMFAILURERECOVERYSCELLCONFIG-STOP +-- TAG-BETAOFFSETS-START + +BetaOffsets ::= SEQUENCE { + betaOffsetACK-Index1 INTEGER(0..31) OPTIONAL, -- Need S + betaOffsetACK-Index2 INTEGER(0..31) OPTIONAL, -- Need S + betaOffsetACK-Index3 INTEGER(0..31) OPTIONAL, -- Need S + betaOffsetCSI-Part1-Index1 INTEGER(0..31) OPTIONAL, -- Need S + betaOffsetCSI-Part1-Index2 INTEGER(0..31) OPTIONAL, -- Need S + betaOffsetCSI-Part2-Index1 INTEGER(0..31) OPTIONAL, -- Need S + betaOffsetCSI-Part2-Index2 INTEGER(0..31) OPTIONAL -- Need S +} + +-- TAG-BETAOFFSETS-STOP +-- TAG-BHLOGICALCHANNELIDENTITY-START + +BH-LogicalChannelIdentity-r16 ::= CHOICE { + bh-LogicalChannelIdentity-r16 LogicalChannelIdentity, + bh-LogicalChannelIdentityExt-r16 BH-LogicalChannelIdentity-Ext-r16 +} + +-- TAG-BHLOGICALCHANNELIDENTITY-STOP +-- TAG-BHLOGICALCHANNELIDENTITYEXT-START + +BH-LogicalChannelIdentity-Ext-r16 ::= INTEGER (320.. maxLC-ID-Iab-r16) + +-- TAG-BHLOGICALCHANNELIDENTITYEXT-STOP +-- TAG-BHRLCCHANNELCONFIG-START + +BH-RLC-ChannelConfig-r16::= SEQUENCE { + bh-LogicalChannelIdentity-r16 BH-LogicalChannelIdentity-r16 OPTIONAL, -- Cond LCH-SetupOnly + bh-RLC-ChannelID-r16 BH-RLC-ChannelID-r16, + reestablishRLC-r16 ENUMERATED {true} OPTIONAL, -- Need N + rlc-Config-r16 RLC-Config OPTIONAL, -- Cond LCH-Setup + mac-LogicalChannelConfig-r16 LogicalChannelConfig OPTIONAL, -- Cond LCH-Setup + ... +} + +-- TAG-BHRLCCHANNELCONFIG-STOP +-- TAG-BHRLCCHANNELID-START + +BH-RLC-ChannelID-r16 ::= BIT STRING (SIZE (16)) + +-- TAG-BHRLCCHANNELID-STOP +-- TAG-BSR-CONFIG-START + +BSR-Config ::= SEQUENCE { + periodicBSR-Timer ENUMERATED { sf1, sf5, sf10, sf16, sf20, sf32, sf40, sf64, + sf80, sf128, sf160, sf320, sf640, sf1280, sf2560, infinity }, + retxBSR-Timer ENUMERATED { sf10, sf20, sf40, sf80, sf160, sf320, sf640, sf1280, sf2560, + sf5120, sf10240, spare5, spare4, spare3, spare2, spare1}, + logicalChannelSR-DelayTimer ENUMERATED { sf20, sf40, sf64, sf128, sf512, sf1024, sf2560, spare1} OPTIONAL, -- Need R + ... +} + +-- TAG-BSR-CONFIG-STOP +-- TAG-BWP-START + +BWP ::= SEQUENCE { + locationAndBandwidth INTEGER (0..37949), + subcarrierSpacing SubcarrierSpacing, + cyclicPrefix ENUMERATED { extended } OPTIONAL -- Need R +} + +-- TAG-BWP-STOP +-- TAG-BWP-DOWNLINK-START + +BWP-Downlink ::= SEQUENCE { + bwp-Id BWP-Id, + bwp-Common BWP-DownlinkCommon OPTIONAL, -- Cond SetupOtherBWP + bwp-Dedicated BWP-DownlinkDedicated OPTIONAL, -- Cond SetupOtherBWP + ... +} + +-- TAG-BWP-DOWNLINK-STOP +-- TAG-BWP-DOWNLINKCOMMON-START + +BWP-DownlinkCommon ::= SEQUENCE { + genericParameters BWP, + pdcch-ConfigCommon SetupRelease { PDCCH-ConfigCommon } OPTIONAL, -- Need M + pdsch-ConfigCommon SetupRelease { PDSCH-ConfigCommon } OPTIONAL, -- Need M + ... +} + +-- TAG-BWP-DOWNLINKCOMMON-STOP +-- TAG-BWP-DOWNLINKDEDICATED-START + +BWP-DownlinkDedicated ::= SEQUENCE { + pdcch-Config SetupRelease { PDCCH-Config } OPTIONAL, -- Need M + pdsch-Config SetupRelease { PDSCH-Config } OPTIONAL, -- Need M + sps-Config SetupRelease { SPS-Config } OPTIONAL, -- Need M + radioLinkMonitoringConfig SetupRelease { RadioLinkMonitoringConfig } OPTIONAL, -- Need M + ..., + [[ + sps-ConfigToAddModList-r16 SPS-ConfigToAddModList-r16 OPTIONAL, -- Need N + sps-ConfigToReleaseList-r16 SPS-ConfigToReleaseList-r16 OPTIONAL, -- Need N + sps-ConfigDeactivationStateList-r16 SPS-ConfigDeactivationStateList-r16 OPTIONAL, -- Need R + beamFailureRecoverySCellConfig-r16 SetupRelease {BeamFailureRecoverySCellConfig-r16} OPTIONAL, -- Cond SCellOnly + sl-PDCCH-Config-r16 SetupRelease { PDCCH-Config } OPTIONAL, -- Need M + sl-V2X-PDCCH-Config-r16 SetupRelease { PDCCH-Config } OPTIONAL -- Need M + ]] +} + +SPS-ConfigToAddModList-r16 ::= SEQUENCE (SIZE (1..maxNrofSPS-Config-r16)) OF SPS-Config + +SPS-ConfigToReleaseList-r16 ::= SEQUENCE (SIZE (1..maxNrofSPS-Config-r16)) OF SPS-ConfigIndex-r16 + +SPS-ConfigDeactivationState-r16 ::= SEQUENCE (SIZE (1..maxNrofSPS-Config-r16)) OF SPS-ConfigIndex-r16 + +SPS-ConfigDeactivationStateList-r16 ::= SEQUENCE (SIZE (1..maxNrofSPS-DeactivationState)) OF SPS-ConfigDeactivationState-r16 + +-- TAG-BWP-DOWNLINKDEDICATED-STOP +-- TAG-BWP-ID-START + +BWP-Id ::= INTEGER (0..maxNrofBWPs) + +-- TAG-BWP-ID-STOP +-- TAG-BWP-UPLINK-START + +BWP-Uplink ::= SEQUENCE { + bwp-Id BWP-Id, + bwp-Common BWP-UplinkCommon OPTIONAL, -- Cond SetupOtherBWP + bwp-Dedicated BWP-UplinkDedicated OPTIONAL, -- Cond SetupOtherBWP + ... +} + +-- TAG-BWP-UPLINK-STOP +-- TAG-BWP-UPLINKCOMMON-START + +BWP-UplinkCommon ::= SEQUENCE { + genericParameters BWP, + rach-ConfigCommon SetupRelease { RACH-ConfigCommon } OPTIONAL, -- Need M + pusch-ConfigCommon SetupRelease { PUSCH-ConfigCommon } OPTIONAL, -- Need M + pucch-ConfigCommon SetupRelease { PUCCH-ConfigCommon } OPTIONAL, -- Need M + ..., + [[ + rach-ConfigCommonIAB-r16 SetupRelease { RACH-ConfigCommon } OPTIONAL, -- Need M + useInterlacePUCCH-PUSCH-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + msgA-ConfigCommon-r16 SetupRelease { MsgA-ConfigCommon-r16 } OPTIONAL -- Cond SpCellOnly2 + ]] +} + +-- TAG-BWP-UPLINKCOMMON-STOP +-- TAG-BWP-UPLINKDEDICATED-START + +BWP-UplinkDedicated ::= SEQUENCE { + pucch-Config SetupRelease { PUCCH-Config } OPTIONAL, -- Need M + pusch-Config SetupRelease { PUSCH-Config } OPTIONAL, -- Need M + configuredGrantConfig SetupRelease { ConfiguredGrantConfig } OPTIONAL, -- Need M + srs-Config SetupRelease { SRS-Config } OPTIONAL, -- Need M + beamFailureRecoveryConfig SetupRelease { BeamFailureRecoveryConfig } OPTIONAL, -- Cond SpCellOnly + ..., + [[ + sl-PUCCH-Config-r16 SetupRelease { PUCCH-Config } OPTIONAL, -- Need M + cp-ExtensionC2-r16 INTEGER (1..28) OPTIONAL, -- Need R + cp-ExtensionC3-r16 INTEGER (1..28) OPTIONAL, -- Need R + useInterlacePUCCH-PUSCH-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + pucch-ConfigurationList-r16 SetupRelease { PUCCH-ConfigurationList-r16 } OPTIONAL, -- Need M + lbt-FailureRecoveryConfig-r16 SetupRelease { LBT-FailureRecoveryConfig-r16 } OPTIONAL, -- Need M + configuredGrantConfigToAddModList-r16 ConfiguredGrantConfigToAddModList-r16 OPTIONAL, -- Need N + configuredGrantConfigToReleaseList-r16 ConfiguredGrantConfigToReleaseList-r16 OPTIONAL, -- Need N + configuredGrantConfigType2DeactivationStateList-r16 ConfiguredGrantConfigType2DeactivationStateList-r16 OPTIONAL -- Need R + ]] + +} + +ConfiguredGrantConfigToAddModList-r16 ::= SEQUENCE (SIZE (1..maxNrofConfiguredGrantConfig-r16)) OF ConfiguredGrantConfig + +ConfiguredGrantConfigToReleaseList-r16 ::= SEQUENCE (SIZE (1..maxNrofConfiguredGrantConfig-r16)) OF ConfiguredGrantConfigIndex-r16 + +ConfiguredGrantConfigType2DeactivationState-r16 ::= SEQUENCE (SIZE (1..maxNrofConfiguredGrantConfig-r16)) OF ConfiguredGrantConfigIndex-r16 + +ConfiguredGrantConfigType2DeactivationStateList-r16 ::= + SEQUENCE (SIZE (1..maxNrofCG-Type2DeactivationState)) OF ConfiguredGrantConfigType2DeactivationState-r16 + +-- TAG-BWP-UPLINKDEDICATED-STOP +-- TAG-CELLACCESSRELATEDINFO-START + +CellAccessRelatedInfo ::= SEQUENCE { + plmn-IdentityList PLMN-IdentityInfoList, + cellReservedForOtherUse ENUMERATED {true} OPTIONAL, -- Need R + ..., + [[ + cellReservedForFutureUse-r16 ENUMERATED {true} OPTIONAL, -- Need R + npn-IdentityInfoList-r16 NPN-IdentityInfoList-r16 OPTIONAL -- Need R + ]] +} + +-- TAG-CELLACCESSRELATEDINFO-STOP +-- TAG-CELLACCESSRELATEDINFOEUTRA-5GC-START + +CellAccessRelatedInfo-EUTRA-5GC ::= SEQUENCE { + plmn-IdentityList-eutra-5gc PLMN-IdentityList-EUTRA-5GC, + trackingAreaCode-eutra-5gc TrackingAreaCode, + ranac-5gc RAN-AreaCode OPTIONAL, + cellIdentity-eutra-5gc CellIdentity-EUTRA-5GC +} + +PLMN-IdentityList-EUTRA-5GC::= SEQUENCE (SIZE (1..maxPLMN)) OF PLMN-Identity-EUTRA-5GC + +PLMN-Identity-EUTRA-5GC ::= CHOICE { + plmn-Identity-EUTRA-5GC PLMN-Identity, + plmn-index INTEGER (1..maxPLMN) +} + +CellIdentity-EUTRA-5GC ::= CHOICE { + cellIdentity-EUTRA BIT STRING (SIZE (28)), + cellId-index INTEGER (1..maxPLMN) +} + +-- TAG-CELLACCESSRELATEDINFOEUTRA-5GC-STOP +-- TAG-CELLACCESSRELATEDINFOEUTRA-EPC-START + +CellAccessRelatedInfo-EUTRA-EPC ::= SEQUENCE { + plmn-IdentityList-eutra-epc PLMN-IdentityList-EUTRA-EPC, + trackingAreaCode-eutra-epc BIT STRING (SIZE (16)), + cellIdentity-eutra-epc BIT STRING (SIZE (28)) +} + +PLMN-IdentityList-EUTRA-EPC::= SEQUENCE (SIZE (1..maxPLMN)) OF PLMN-Identity + +-- TAG-CELLACCESSRELATEDINFOEUTRA-EPC-STOP +-- TAG-CELLGROUPCONFIG-START + +-- Configuration of one Cell-Group: +CellGroupConfig ::= SEQUENCE { + cellGroupId CellGroupId, + rlc-BearerToAddModList SEQUENCE (SIZE(1..maxLC-ID)) OF RLC-BearerConfig OPTIONAL, -- Need N + rlc-BearerToReleaseList SEQUENCE (SIZE(1..maxLC-ID)) OF LogicalChannelIdentity OPTIONAL, -- Need N + mac-CellGroupConfig MAC-CellGroupConfig OPTIONAL, -- Need M + physicalCellGroupConfig PhysicalCellGroupConfig OPTIONAL, -- Need M + spCellConfig SpCellConfig OPTIONAL, -- Need M + sCellToAddModList SEQUENCE (SIZE (1..maxNrofSCells)) OF SCellConfig OPTIONAL, -- Need N + sCellToReleaseList SEQUENCE (SIZE (1..maxNrofSCells)) OF SCellIndex OPTIONAL, -- Need N + ..., + [[ + reportUplinkTxDirectCurrent ENUMERATED {true} OPTIONAL -- Cond BWP-Reconfig + ]], + [[ + bap-Address-r16 BIT STRING (SIZE (10)) OPTIONAL, -- Need M + bh-RLC-ChannelToAddModList-r16 SEQUENCE (SIZE(1..maxBH-RLC-ChannelID-r16)) OF BH-RLC-ChannelConfig-r16 OPTIONAL, -- Need N + bh-RLC-ChannelToReleaseList-r16 SEQUENCE (SIZE(1..maxBH-RLC-ChannelID-r16)) OF BH-RLC-ChannelID-r16 OPTIONAL, -- Need N + f1c-TransferPath-r16 ENUMERATED {lte, nr, both} OPTIONAL, -- Need M + simultaneousTCI-UpdateList1-r16 SEQUENCE (SIZE (1..maxNrofServingCellsTCI-r16)) OF ServCellIndex OPTIONAL, -- Need R + simultaneousTCI-UpdateList2-r16 SEQUENCE (SIZE (1..maxNrofServingCellsTCI-r16)) OF ServCellIndex OPTIONAL, -- Need R + simultaneousSpatial-UpdatedList1-r16 SEQUENCE (SIZE (1..maxNrofServingCellsTCI-r16)) OF ServCellIndex OPTIONAL, -- Need R + simultaneousSpatial-UpdatedList2-r16 SEQUENCE (SIZE (1..maxNrofServingCellsTCI-r16)) OF ServCellIndex OPTIONAL, -- Need R + uplinkTxSwitchingOption-r16 ENUMERATED {switchedUL, dualUL} OPTIONAL, -- Need R + uplinkTxSwitchingPowerBoosting-r16 ENUMERATED {enabled} OPTIONAL -- Need R + ]], + [[ + reportUplinkTxDirectCurrentTwoCarrier-r16 ENUMERATED {true} OPTIONAL -- Need N + ]] +} + +-- Serving cell specific MAC and PHY parameters for a SpCell: +SpCellConfig ::= SEQUENCE { + servCellIndex ServCellIndex OPTIONAL, -- Cond SCG + reconfigurationWithSync ReconfigurationWithSync OPTIONAL, -- Cond ReconfWithSync + rlf-TimersAndConstants SetupRelease { RLF-TimersAndConstants } OPTIONAL, -- Need M + rlmInSyncOutOfSyncThreshold ENUMERATED {n1} OPTIONAL, -- Need S + spCellConfigDedicated ServingCellConfig OPTIONAL, -- Need M + ... +} + +ReconfigurationWithSync ::= SEQUENCE { + spCellConfigCommon ServingCellConfigCommon OPTIONAL, -- Need M + newUE-Identity RNTI-Value, + t304 ENUMERATED {ms50, ms100, ms150, ms200, ms500, ms1000, ms2000, ms10000}, + rach-ConfigDedicated CHOICE { + uplink RACH-ConfigDedicated, + supplementaryUplink RACH-ConfigDedicated + } OPTIONAL, -- Need N + ..., + [[ + smtc SSB-MTC OPTIONAL -- Need S + ]], + [[ + daps-UplinkPowerConfig-r16 DAPS-UplinkPowerConfig-r16 OPTIONAL -- Need N + ]] +} + +DAPS-UplinkPowerConfig-r16 ::= SEQUENCE { + p-DAPS-Source-r16 P-Max, + p-DAPS-Target-r16 P-Max, + uplinkPowerSharingDAPS-Mode-r16 ENUMERATED {semi-static-mode1, semi-static-mode2, dynamic } +} + +SCellConfig ::= SEQUENCE { + sCellIndex SCellIndex, + sCellConfigCommon ServingCellConfigCommon OPTIONAL, -- Cond SCellAdd + sCellConfigDedicated ServingCellConfig OPTIONAL, -- Cond SCellAddMod + ..., + [[ + smtc SSB-MTC OPTIONAL -- Need S + ]], + [[ + sCellState-r16 ENUMERATED {activated} OPTIONAL, -- Cond SCellAddSync + secondaryDRX-GroupConfig-r16 ENUMERATED {true} OPTIONAL -- Cond DRX-Config2 + ]]} + +-- TAG-CELLGROUPCONFIG-STOP +-- TAG-CELLGROUPID-START + +CellGroupId ::= INTEGER (0.. maxSecondaryCellGroups) + +-- TAG-CELLGROUPID-STOP +-- TAG-CELLIDENTITY-START + +CellIdentity ::= BIT STRING (SIZE (36)) + +-- TAG-CELLIDENTITY-STOP +-- TAG-CELLRESELECTIONPRIORITY-START + +CellReselectionPriority ::= INTEGER (0..7) + +-- TAG-CELLRESELECTIONPRIORITY-STOP +-- TAG-CELLRESELECTIONSUBPRIORITY-START + +CellReselectionSubPriority ::= ENUMERATED {oDot2, oDot4, oDot6, oDot8} + +-- TAG-CELLRESELECTIONSUBPRIORITY-STOP +-- TAG-CGI-INFOEUTRA-START + +CGI-InfoEUTRA ::= SEQUENCE { + cgi-info-EPC SEQUENCE { + cgi-info-EPC-legacy CellAccessRelatedInfo-EUTRA-EPC, + cgi-info-EPC-list SEQUENCE (SIZE (1..maxPLMN)) OF CellAccessRelatedInfo-EUTRA-EPC OPTIONAL + } OPTIONAL, + cgi-info-5GC SEQUENCE (SIZE (1..maxPLMN)) OF CellAccessRelatedInfo-EUTRA-5GC OPTIONAL, + freqBandIndicator FreqBandIndicatorEUTRA, + multiBandInfoList MultiBandInfoListEUTRA OPTIONAL, + freqBandIndicatorPriority ENUMERATED {true} OPTIONAL +} + +-- TAG-CGI-INFOEUTRA-STOP +-- TAG-CGI-INFOEUTRALOGGING-START + +CGI-InfoEUTRALogging ::= SEQUENCE { + plmn-Identity-eutra-5gc PLMN-Identity OPTIONAL, + trackingAreaCode-eutra-5gc TrackingAreaCode OPTIONAL, + cellIdentity-eutra-5gc BIT STRING (SIZE (28)) OPTIONAL, + plmn-Identity-eutra-epc PLMN-Identity OPTIONAL, + trackingAreaCode-eutra-epc BIT STRING (SIZE (16)) OPTIONAL, + cellIdentity-eutra-epc BIT STRING (SIZE (28)) OPTIONAL +} + +-- TAG-CGI-INFOEUTRALOGGING-STOP +-- TAG-CGI-INFO-NR-START + +CGI-InfoNR ::= SEQUENCE { + plmn-IdentityInfoList PLMN-IdentityInfoList OPTIONAL, + frequencyBandList MultiFrequencyBandListNR OPTIONAL, + noSIB1 SEQUENCE { + ssb-SubcarrierOffset INTEGER (0..15), + pdcch-ConfigSIB1 PDCCH-ConfigSIB1 + } OPTIONAL, + ..., + [[ + npn-IdentityInfoList-r16 NPN-IdentityInfoList-r16 OPTIONAL + ]] +} + +-- TAG-CGI-INFO-NR-STOP +-- TAG-CGI-INFO-LOGGING-START + +CGI-Info-Logging-r16 ::= SEQUENCE { + plmn-Identity-r16 PLMN-Identity, + cellIdentity-r16 CellIdentity, + trackingAreaCode-r16 TrackingAreaCode OPTIONAL +} + +-- TAG-CGI-INFO-LOGGING-STOP +-- TAG-CLI-RSSI-RANGE-START + +CLI-RSSI-Range-r16 ::= INTEGER(0..76) + +-- TAG-CLI-RSSI-RANGE-STOP +-- TAG-CODEBOOKCONFIG-START + +CodebookConfig ::= SEQUENCE { + codebookType CHOICE { + type1 SEQUENCE { + subType CHOICE { + typeI-SinglePanel SEQUENCE { + nrOfAntennaPorts CHOICE { + two SEQUENCE { + twoTX-CodebookSubsetRestriction BIT STRING (SIZE (6)) + }, + moreThanTwo SEQUENCE { + n1-n2 CHOICE { + two-one-TypeI-SinglePanel-Restriction BIT STRING (SIZE (8)), + two-two-TypeI-SinglePanel-Restriction BIT STRING (SIZE (64)), + four-one-TypeI-SinglePanel-Restriction BIT STRING (SIZE (16)), + three-two-TypeI-SinglePanel-Restriction BIT STRING (SIZE (96)), + six-one-TypeI-SinglePanel-Restriction BIT STRING (SIZE (24)), + four-two-TypeI-SinglePanel-Restriction BIT STRING (SIZE (128)), + eight-one-TypeI-SinglePanel-Restriction BIT STRING (SIZE (32)), + four-three-TypeI-SinglePanel-Restriction BIT STRING (SIZE (192)), + six-two-TypeI-SinglePanel-Restriction BIT STRING (SIZE (192)), + twelve-one-TypeI-SinglePanel-Restriction BIT STRING (SIZE (48)), + four-four-TypeI-SinglePanel-Restriction BIT STRING (SIZE (256)), + eight-two-TypeI-SinglePanel-Restriction BIT STRING (SIZE (256)), + sixteen-one-TypeI-SinglePanel-Restriction BIT STRING (SIZE (64)) + }, + typeI-SinglePanel-codebookSubsetRestriction-i2 BIT STRING (SIZE (16)) OPTIONAL -- Need R + } + }, + typeI-SinglePanel-ri-Restriction BIT STRING (SIZE (8)) + }, + typeI-MultiPanel SEQUENCE { + ng-n1-n2 CHOICE { + two-two-one-TypeI-MultiPanel-Restriction BIT STRING (SIZE (8)), + two-four-one-TypeI-MultiPanel-Restriction BIT STRING (SIZE (16)), + four-two-one-TypeI-MultiPanel-Restriction BIT STRING (SIZE (8)), + two-two-two-TypeI-MultiPanel-Restriction BIT STRING (SIZE (64)), + two-eight-one-TypeI-MultiPanel-Restriction BIT STRING (SIZE (32)), + four-four-one-TypeI-MultiPanel-Restriction BIT STRING (SIZE (16)), + two-four-two-TypeI-MultiPanel-Restriction BIT STRING (SIZE (128)), + four-two-two-TypeI-MultiPanel-Restriction BIT STRING (SIZE (64)) + }, + ri-Restriction BIT STRING (SIZE (4)) + } + }, + codebookMode INTEGER (1..2) + + }, + type2 SEQUENCE { + subType CHOICE { + typeII SEQUENCE { + n1-n2-codebookSubsetRestriction CHOICE { + two-one BIT STRING (SIZE (16)), + two-two BIT STRING (SIZE (43)), + four-one BIT STRING (SIZE (32)), + three-two BIT STRING (SIZE (59)), + six-one BIT STRING (SIZE (48)), + four-two BIT STRING (SIZE (75)), + eight-one BIT STRING (SIZE (64)), + four-three BIT STRING (SIZE (107)), + six-two BIT STRING (SIZE (107)), + twelve-one BIT STRING (SIZE (96)), + four-four BIT STRING (SIZE (139)), + eight-two BIT STRING (SIZE (139)), + sixteen-one BIT STRING (SIZE (128)) + }, + typeII-RI-Restriction BIT STRING (SIZE (2)) + }, + typeII-PortSelection SEQUENCE { + portSelectionSamplingSize ENUMERATED {n1, n2, n3, n4} OPTIONAL, -- Need R + typeII-PortSelectionRI-Restriction BIT STRING (SIZE (2)) + } + }, + phaseAlphabetSize ENUMERATED {n4, n8}, + subbandAmplitude BOOLEAN, + numberOfBeams ENUMERATED {two, three, four} + } + } +} + +CodebookConfig-r16 ::= SEQUENCE { + codebookType CHOICE { + type2 SEQUENCE { + subType CHOICE { + typeII-r16 SEQUENCE { + n1-n2-codebookSubsetRestriction-r16 CHOICE { + two-one BIT STRING (SIZE (16)), + two-two BIT STRING (SIZE (43)), + four-one BIT STRING (SIZE (32)), + three-two BIT STRING (SIZE (59)), + six-one BIT STRING (SIZE (48)), + four-two BIT STRING (SIZE (75)), + eight-one BIT STRING (SIZE (64)), + four-three BIT STRING (SIZE (107)), + six-two BIT STRING (SIZE (107)), + twelve-one BIT STRING (SIZE (96)), + four-four BIT STRING (SIZE (139)), + eight-two BIT STRING (SIZE (139)), + sixteen-one BIT STRING (SIZE (128)) + }, + typeII-RI-Restriction-r16 BIT STRING (SIZE(4)) + }, + typeII-PortSelection-r16 SEQUENCE { + portSelectionSamplingSize-r16 ENUMERATED {n1, n2, n3, n4}, + typeII-PortSelectionRI-Restriction-r16 BIT STRING (SIZE (4)) + } + }, + numberOfPMI-SubbandsPerCQI-Subband-r16 INTEGER (1..2), + paramCombination-r16 INTEGER (1..8) + } + } +} + +-- TAG-CODEBOOKCONFIG-STOP +-- TAG-COMMONLOCATIONINFO-START + +CommonLocationInfo-r16 ::= SEQUENCE { + gnss-TOD-msec-r16 OCTET STRING OPTIONAL, + locationTimestamp-r16 OCTET STRING OPTIONAL, + locationCoordinate-r16 OCTET STRING OPTIONAL, + locationError-r16 OCTET STRING OPTIONAL, + locationSource-r16 OCTET STRING OPTIONAL, + velocityEstimate-r16 OCTET STRING OPTIONAL +} + +-- TAG-COMMONLOCATIONINFO-STOP +-- TAG-CONDRECONFIGID-START + +CondReconfigId-r16 ::= INTEGER (1.. maxNrofCondCells-r16) + +-- TAG-CONDRECONFIGID-STOP +-- TAG-CONDRECONFIGTOADDMODLIST-START + +CondReconfigToAddModList-r16 ::= SEQUENCE (SIZE (1.. maxNrofCondCells-r16)) OF CondReconfigToAddMod-r16 + +CondReconfigToAddMod-r16 ::= SEQUENCE { + condReconfigId-r16 CondReconfigId-r16, + condExecutionCond-r16 SEQUENCE (SIZE (1..2)) OF MeasId OPTIONAL, -- Cond condReconfigAdd + condRRCReconfig-r16 OCTET STRING (CONTAINING RRCReconfiguration) OPTIONAL, -- Cond condReconfigAdd + ... +} + +-- TAG-CONDRECONFIGTOADDMODLIST-STOP +-- TAG-CONDITIONALRECONFIGURATION-START + +ConditionalReconfiguration-r16 ::= SEQUENCE { + attemptCondReconfig-r16 ENUMERATED {true} OPTIONAL, -- Cond CHO + condReconfigToRemoveList-r16 CondReconfigToRemoveList-r16 OPTIONAL, -- Need N + condReconfigToAddModList-r16 CondReconfigToAddModList-r16 OPTIONAL, -- Need N + ... +} + +CondReconfigToRemoveList-r16 ::= SEQUENCE (SIZE (1.. maxNrofCondCells-r16)) OF CondReconfigId-r16 + +-- TAG-CONDITIONALRECONFIGURATION-STOP +-- TAG-CONFIGUREDGRANTCONFIG-START + +ConfiguredGrantConfig ::= SEQUENCE { + frequencyHopping ENUMERATED {intraSlot, interSlot} OPTIONAL, -- Need S + cg-DMRS-Configuration DMRS-UplinkConfig, + mcs-Table ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + mcs-TableTransformPrecoder ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + uci-OnPUSCH SetupRelease { CG-UCI-OnPUSCH } OPTIONAL, -- Need M + resourceAllocation ENUMERATED { resourceAllocationType0, resourceAllocationType1, dynamicSwitch }, + rbg-Size ENUMERATED {config2} OPTIONAL, -- Need S + powerControlLoopToUse ENUMERATED {n0, n1}, + p0-PUSCH-Alpha P0-PUSCH-AlphaSetId, + transformPrecoder ENUMERATED {enabled, disabled} OPTIONAL, -- Need S + nrofHARQ-Processes INTEGER(1..16), + repK ENUMERATED {n1, n2, n4, n8}, + repK-RV ENUMERATED {s1-0231, s2-0303, s3-0000} OPTIONAL, -- Need R + periodicity ENUMERATED { + sym2, sym7, sym1x14, sym2x14, sym4x14, sym5x14, sym8x14, sym10x14, sym16x14, sym20x14, + sym32x14, sym40x14, sym64x14, sym80x14, sym128x14, sym160x14, sym256x14, sym320x14, sym512x14, + sym640x14, sym1024x14, sym1280x14, sym2560x14, sym5120x14, + sym6, sym1x12, sym2x12, sym4x12, sym5x12, sym8x12, sym10x12, sym16x12, sym20x12, sym32x12, + sym40x12, sym64x12, sym80x12, sym128x12, sym160x12, sym256x12, sym320x12, sym512x12, sym640x12, + sym1280x12, sym2560x12 + }, + configuredGrantTimer INTEGER (1..64) OPTIONAL, -- Need R + rrc-ConfiguredUplinkGrant SEQUENCE { + timeDomainOffset INTEGER (0..5119), + timeDomainAllocation INTEGER (0..15), + frequencyDomainAllocation BIT STRING (SIZE(18)), + antennaPort INTEGER (0..31), + dmrs-SeqInitialization INTEGER (0..1) OPTIONAL, -- Need R + precodingAndNumberOfLayers INTEGER (0..63), + srs-ResourceIndicator INTEGER (0..15) OPTIONAL, -- Need R + mcsAndTBS INTEGER (0..31), + frequencyHoppingOffset INTEGER (1.. maxNrofPhysicalResourceBlocks-1) OPTIONAL, -- Need R + pathlossReferenceIndex INTEGER (0..maxNrofPUSCH-PathlossReferenceRSs-1), + ..., + [[ + pusch-RepTypeIndicator-r16 ENUMERATED {pusch-RepTypeA,pusch-RepTypeB} OPTIONAL, -- Need M + frequencyHoppingPUSCH-RepTypeB-r16 ENUMERATED {interRepetition, interSlot} OPTIONAL, -- Cond RepTypeB + timeReferenceSFN-r16 ENUMERATED {sfn512} OPTIONAL -- Need S + ]] + } OPTIONAL, -- Need R + ..., + [[ + cg-RetransmissionTimer-r16 INTEGER (1..64) OPTIONAL, -- Need R + cg-minDFI-Delay-r16 ENUMERATED + {sym7, sym1x14, sym2x14, sym3x14, sym4x14, sym5x14, sym6x14, sym7x14, sym8x14, + sym9x14, sym10x14, sym11x14, sym12x14, sym13x14, sym14x14,sym15x14, sym16x14 + } OPTIONAL, -- Need R + cg-nrofPUSCH-InSlot-r16 INTEGER (1..7) OPTIONAL, -- Need R + cg-nrofSlots-r16 INTEGER (1..40) OPTIONAL, -- Need R + cg-StartingOffsets-r16 CG-StartingOffsets-r16 OPTIONAL, -- Need R + cg-UCI-Multiplexing-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + cg-COT-SharingOffset-r16 INTEGER (1..39) OPTIONAL, -- Need R + betaOffsetCG-UCI-r16 INTEGER (0..31) OPTIONAL, -- Need R + cg-COT-SharingList-r16 SEQUENCE (SIZE (1..1709)) OF CG-COT-Sharing-r16 OPTIONAL, -- Need R + harq-ProcID-Offset-r16 INTEGER (0..15) OPTIONAL, -- Need M + harq-ProcID-Offset2-r16 INTEGER (0..15) OPTIONAL, -- Need M + configuredGrantConfigIndex-r16 ConfiguredGrantConfigIndex-r16 OPTIONAL, -- Cond CG-List + configuredGrantConfigIndexMAC-r16 ConfiguredGrantConfigIndexMAC-r16 OPTIONAL, -- Cond CG-IndexMAC + periodicityExt-r16 INTEGER (1..5120) OPTIONAL, -- Need R + startingFromRV0-r16 ENUMERATED {on, off} OPTIONAL, -- Need R + phy-PriorityIndex-r16 ENUMERATED {p0, p1} OPTIONAL, -- Need R + autonomousTx-r16 ENUMERATED {enabled} OPTIONAL -- Cond LCH-BasedPrioritization + ]] + +} + +CG-UCI-OnPUSCH ::= CHOICE { + dynamic SEQUENCE (SIZE (1..4)) OF BetaOffsets, + semiStatic BetaOffsets +} + +CG-COT-Sharing-r16 ::= CHOICE { + noCOT-Sharing-r16 NULL, + cot-Sharing-r16 SEQUENCE { + duration-r16 INTEGER (1..39), + offset-r16 INTEGER (1..39), + channelAccessPriority-r16 INTEGER (1..4) + } +} + +CG-StartingOffsets-r16 ::= SEQUENCE { + cg-StartingFullBW-InsideCOT-r16 SEQUENCE (SIZE (1..7)) OF INTEGER (0..6) OPTIONAL, -- Need R + cg-StartingFullBW-OutsideCOT-r16 SEQUENCE (SIZE (1..7)) OF INTEGER (0..6) OPTIONAL, -- Need R + cg-StartingPartialBW-InsideCOT-r16 INTEGER (0..6) OPTIONAL, -- Need R + cg-StartingPartialBW-OutsideCOT-r16 INTEGER (0..6) OPTIONAL -- Need R +} + +-- TAG-CONFIGUREDGRANTCONFIG-STOP +-- TAG-CONFIGUREDGRANTCONFIGINDEX-START + +ConfiguredGrantConfigIndex-r16 ::= INTEGER (0.. maxNrofConfiguredGrantConfig-r16-1) + +-- TAG-CONFIGUREDGRANTCONFIGINDEX-STOP +-- TAG-CONFIGUREDGRANTCONFIGINDEXMAC-START + +ConfiguredGrantConfigIndexMAC-r16 ::= INTEGER (0.. maxNrofConfiguredGrantConfigMAC-r16-1) + +-- TAG-CONFIGUREDGRANTCONFIGINDEXMAC-STOP +-- TAG-CONNESTFAILURECONTROL-START + +ConnEstFailureControl ::= SEQUENCE { + connEstFailCount ENUMERATED {n1, n2, n3, n4}, + connEstFailOffsetValidity ENUMERATED {s30, s60, s120, s240, s300, s420, s600, s900}, + connEstFailOffset INTEGER (0..15) OPTIONAL -- Need S +} + +-- TAG-CONNESTFAILURECONTROL-STOP +-- TAG-CONTROLRESOURCESET-START + +ControlResourceSet ::= SEQUENCE { + controlResourceSetId ControlResourceSetId, + + frequencyDomainResources BIT STRING (SIZE (45)), + duration INTEGER (1..maxCoReSetDuration), + cce-REG-MappingType CHOICE { + interleaved SEQUENCE { + reg-BundleSize ENUMERATED {n2, n3, n6}, + interleaverSize ENUMERATED {n2, n3, n6}, + shiftIndex INTEGER(0..maxNrofPhysicalResourceBlocks-1) OPTIONAL -- Need S + }, + nonInterleaved NULL + }, + precoderGranularity ENUMERATED {sameAsREG-bundle, allContiguousRBs}, + tci-StatesPDCCH-ToAddList SEQUENCE(SIZE (1..maxNrofTCI-StatesPDCCH)) OF TCI-StateId OPTIONAL, -- Cond NotSIB1-initialBWP + tci-StatesPDCCH-ToReleaseList SEQUENCE(SIZE (1..maxNrofTCI-StatesPDCCH)) OF TCI-StateId OPTIONAL, -- Cond NotSIB1-initialBWP + tci-PresentInDCI ENUMERATED {enabled} OPTIONAL, -- Need S + pdcch-DMRS-ScramblingID INTEGER (0..65535) OPTIONAL, -- Need S + ..., + [[ + rb-Offset-r16 INTEGER (0..5) OPTIONAL, -- Need S + tci-PresentDCI-1-2-r16 INTEGER (1..3) OPTIONAL, -- Need S + coresetPoolIndex-r16 INTEGER (0..1) OPTIONAL, -- Need S + controlResourceSetId-v1610 ControlResourceSetId-v1610 OPTIONAL -- Need S + ]] +} + +-- TAG-CONTROLRESOURCESET-STOP +-- TAG-CONTROLRESOURCESETID-START + +ControlResourceSetId ::= INTEGER (0..maxNrofControlResourceSets-1) + +ControlResourceSetId-r16 ::= INTEGER (0..maxNrofControlResourceSets-1-r16) + +ControlResourceSetId-v1610 ::= INTEGER (maxNrofControlResourceSets..maxNrofControlResourceSets-1-r16) + +-- TAG-CONTROLRESOURCESETID-STOP +-- TAG-CONTROLRESOURCESETZERO-START + +ControlResourceSetZero ::= INTEGER (0..15) + +-- TAG-CONTROLRESOURCESETZERO-STOP +-- TAG-CROSSCARRIERSCHEDULINGCONFIG-START + +CrossCarrierSchedulingConfig ::= SEQUENCE { + schedulingCellInfo CHOICE { + own SEQUENCE { -- Cross carrier scheduling: scheduling cell + cif-Presence BOOLEAN + }, + other SEQUENCE { -- Cross carrier scheduling: scheduled cell + schedulingCellId ServCellIndex, + cif-InSchedulingCell INTEGER (1..7) + } + }, + ..., + [[ + carrierIndicatorSize-r16 SEQUENCE { + carrierIndicatorSizeDCI-1-2-r16 INTEGER (0..3), + carrierIndicatorSizeDCI-0-2-r16 INTEGER (0..3) + } OPTIONAL, -- Cond CIF-PRESENCE + enableDefaultBeamForCCS-r16 ENUMERATED {enabled} OPTIONAL -- Need S + ]] +} + +-- TAG-CROSSCARRIERSCHEDULINGCONFIG-STOP +-- TAG-CSI-APERIODICTRIGGERSTATELIST-START + +CSI-AperiodicTriggerStateList ::= SEQUENCE (SIZE (1..maxNrOfCSI-AperiodicTriggers)) OF CSI-AperiodicTriggerState + +CSI-AperiodicTriggerState ::= SEQUENCE { + associatedReportConfigInfoList SEQUENCE (SIZE(1..maxNrofReportConfigPerAperiodicTrigger)) OF CSI-AssociatedReportConfigInfo, + ... +} + +CSI-AssociatedReportConfigInfo ::= SEQUENCE { + reportConfigId CSI-ReportConfigId, + resourcesForChannel CHOICE { + nzp-CSI-RS SEQUENCE { + resourceSet INTEGER (1..maxNrofNZP-CSI-RS-ResourceSetsPerConfig), + qcl-info SEQUENCE (SIZE(1..maxNrofAP-CSI-RS-ResourcesPerSet)) OF TCI-StateId + OPTIONAL -- Cond Aperiodic + }, + csi-SSB-ResourceSet INTEGER (1..maxNrofCSI-SSB-ResourceSetsPerConfig) + }, + csi-IM-ResourcesForInterference INTEGER(1..maxNrofCSI-IM-ResourceSetsPerConfig) OPTIONAL, -- Cond CSI-IM-ForInterference + nzp-CSI-RS-ResourcesForInterference INTEGER (1..maxNrofNZP-CSI-RS-ResourceSetsPerConfig) OPTIONAL, -- Cond NZP-CSI-RS-ForInterference + ... +} + +-- TAG-CSI-APERIODICTRIGGERSTATELIST-STOP +-- TAG-CSI-FREQUENCYOCCUPATION-START + +CSI-FrequencyOccupation ::= SEQUENCE { + startingRB INTEGER (0..maxNrofPhysicalResourceBlocks-1), + nrofRBs INTEGER (24..maxNrofPhysicalResourceBlocksPlus1), + ... +} + +-- TAG-CSI-FREQUENCYOCCUPATION-STOP +-- TAG-CSI-IM-RESOURCE-START + +CSI-IM-Resource ::= SEQUENCE { + csi-IM-ResourceId CSI-IM-ResourceId, + csi-IM-ResourceElementPattern CHOICE { + pattern0 SEQUENCE { + subcarrierLocation-p0 ENUMERATED { s0, s2, s4, s6, s8, s10 }, + symbolLocation-p0 INTEGER (0..12) + }, + pattern1 SEQUENCE { + subcarrierLocation-p1 ENUMERATED { s0, s4, s8 }, + symbolLocation-p1 INTEGER (0..13) + } + } OPTIONAL, -- Need M + freqBand CSI-FrequencyOccupation OPTIONAL, -- Need M + periodicityAndOffset CSI-ResourcePeriodicityAndOffset OPTIONAL, -- Cond PeriodicOrSemiPersistent + ... +} + +-- TAG-CSI-IM-RESOURCE-STOP +-- TAG-CSI-IM-RESOURCEID-START + +CSI-IM-ResourceId ::= INTEGER (0..maxNrofCSI-IM-Resources-1) + +-- TAG-CSI-IM-RESOURCEID-STOP +-- TAG-CSI-IM-RESOURCESET-START + +CSI-IM-ResourceSet ::= SEQUENCE { + csi-IM-ResourceSetId CSI-IM-ResourceSetId, + csi-IM-Resources SEQUENCE (SIZE(1..maxNrofCSI-IM-ResourcesPerSet)) OF CSI-IM-ResourceId, + ... +} +-- TAG-CSI-IM-RESOURCESET-STOP +-- TAG-CSI-IM-RESOURCESETID-START + +CSI-IM-ResourceSetId ::= INTEGER (0..maxNrofCSI-IM-ResourceSets-1) + +-- TAG-CSI-IM-RESOURCESETID-STOP +-- TAG-CSI-MEASCONFIG-START + +CSI-MeasConfig ::= SEQUENCE { + nzp-CSI-RS-ResourceToAddModList SEQUENCE (SIZE (1..maxNrofNZP-CSI-RS-Resources)) OF NZP-CSI-RS-Resource OPTIONAL, -- Need N + nzp-CSI-RS-ResourceToReleaseList SEQUENCE (SIZE (1..maxNrofNZP-CSI-RS-Resources)) OF NZP-CSI-RS-ResourceId OPTIONAL, -- Need N + nzp-CSI-RS-ResourceSetToAddModList SEQUENCE (SIZE (1..maxNrofNZP-CSI-RS-ResourceSets)) OF NZP-CSI-RS-ResourceSet + OPTIONAL, -- Need N + nzp-CSI-RS-ResourceSetToReleaseList SEQUENCE (SIZE (1..maxNrofNZP-CSI-RS-ResourceSets)) OF NZP-CSI-RS-ResourceSetId + OPTIONAL, -- Need N + csi-IM-ResourceToAddModList SEQUENCE (SIZE (1..maxNrofCSI-IM-Resources)) OF CSI-IM-Resource OPTIONAL, -- Need N + csi-IM-ResourceToReleaseList SEQUENCE (SIZE (1..maxNrofCSI-IM-Resources)) OF CSI-IM-ResourceId OPTIONAL, -- Need N + csi-IM-ResourceSetToAddModList SEQUENCE (SIZE (1..maxNrofCSI-IM-ResourceSets)) OF CSI-IM-ResourceSet OPTIONAL, -- Need N + csi-IM-ResourceSetToReleaseList SEQUENCE (SIZE (1..maxNrofCSI-IM-ResourceSets)) OF CSI-IM-ResourceSetId OPTIONAL, -- Need N + csi-SSB-ResourceSetToAddModList SEQUENCE (SIZE (1..maxNrofCSI-SSB-ResourceSets)) OF CSI-SSB-ResourceSet OPTIONAL, -- Need N + csi-SSB-ResourceSetToReleaseList SEQUENCE (SIZE (1..maxNrofCSI-SSB-ResourceSets)) OF CSI-SSB-ResourceSetId OPTIONAL, -- Need N + csi-ResourceConfigToAddModList SEQUENCE (SIZE (1..maxNrofCSI-ResourceConfigurations)) OF CSI-ResourceConfig + OPTIONAL, -- Need N + csi-ResourceConfigToReleaseList SEQUENCE (SIZE (1..maxNrofCSI-ResourceConfigurations)) OF CSI-ResourceConfigId + OPTIONAL, -- Need N + csi-ReportConfigToAddModList SEQUENCE (SIZE (1..maxNrofCSI-ReportConfigurations)) OF CSI-ReportConfig OPTIONAL, -- Need N + csi-ReportConfigToReleaseList SEQUENCE (SIZE (1..maxNrofCSI-ReportConfigurations)) OF CSI-ReportConfigId + OPTIONAL, -- Need N + reportTriggerSize INTEGER (0..6) OPTIONAL, -- Need M + aperiodicTriggerStateList SetupRelease { CSI-AperiodicTriggerStateList } OPTIONAL, -- Need M + semiPersistentOnPUSCH-TriggerStateList SetupRelease { CSI-SemiPersistentOnPUSCH-TriggerStateList } OPTIONAL, -- Need M + ..., + [[ + reportTriggerSizeDCI-0-2-r16 INTEGER (0..6) OPTIONAL -- Need R + ]] +} + +-- TAG-CSI-MEASCONFIG-STOP +-- TAG-CSI-REPORTCONFIG-START + +CSI-ReportConfig ::= SEQUENCE { + reportConfigId CSI-ReportConfigId, + carrier ServCellIndex OPTIONAL, -- Need S + resourcesForChannelMeasurement CSI-ResourceConfigId, + csi-IM-ResourcesForInterference CSI-ResourceConfigId OPTIONAL, -- Need R + nzp-CSI-RS-ResourcesForInterference CSI-ResourceConfigId OPTIONAL, -- Need R + reportConfigType CHOICE { + periodic SEQUENCE { + reportSlotConfig CSI-ReportPeriodicityAndOffset, + pucch-CSI-ResourceList SEQUENCE (SIZE (1..maxNrofBWPs)) OF PUCCH-CSI-Resource + }, + semiPersistentOnPUCCH SEQUENCE { + reportSlotConfig CSI-ReportPeriodicityAndOffset, + pucch-CSI-ResourceList SEQUENCE (SIZE (1..maxNrofBWPs)) OF PUCCH-CSI-Resource + }, + semiPersistentOnPUSCH SEQUENCE { + reportSlotConfig ENUMERATED {sl5, sl10, sl20, sl40, sl80, sl160, sl320}, + reportSlotOffsetList SEQUENCE (SIZE (1.. maxNrofUL-Allocations)) OF INTEGER(0..32), + p0alpha P0-PUSCH-AlphaSetId + }, + aperiodic SEQUENCE { + reportSlotOffsetList SEQUENCE (SIZE (1..maxNrofUL-Allocations)) OF INTEGER(0..32) + } + }, + reportQuantity CHOICE { + none NULL, + cri-RI-PMI-CQI NULL, + cri-RI-i1 NULL, + cri-RI-i1-CQI SEQUENCE { + pdsch-BundleSizeForCSI ENUMERATED {n2, n4} OPTIONAL -- Need S + }, + cri-RI-CQI NULL, + cri-RSRP NULL, + ssb-Index-RSRP NULL, + cri-RI-LI-PMI-CQI NULL + }, + reportFreqConfiguration SEQUENCE { + cqi-FormatIndicator ENUMERATED { widebandCQI, subbandCQI } OPTIONAL, -- Need R + pmi-FormatIndicator ENUMERATED { widebandPMI, subbandPMI } OPTIONAL, -- Need R + csi-ReportingBand CHOICE { + subbands3 BIT STRING(SIZE(3)), + subbands4 BIT STRING(SIZE(4)), + subbands5 BIT STRING(SIZE(5)), + subbands6 BIT STRING(SIZE(6)), + subbands7 BIT STRING(SIZE(7)), + subbands8 BIT STRING(SIZE(8)), + subbands9 BIT STRING(SIZE(9)), + subbands10 BIT STRING(SIZE(10)), + subbands11 BIT STRING(SIZE(11)), + subbands12 BIT STRING(SIZE(12)), + subbands13 BIT STRING(SIZE(13)), + subbands14 BIT STRING(SIZE(14)), + subbands15 BIT STRING(SIZE(15)), + subbands16 BIT STRING(SIZE(16)), + subbands17 BIT STRING(SIZE(17)), + subbands18 BIT STRING(SIZE(18)), + ..., + subbands19-v1530 BIT STRING(SIZE(19)) + } OPTIONAL -- Need S + + } OPTIONAL, -- Need R + timeRestrictionForChannelMeasurements ENUMERATED {configured, notConfigured}, + timeRestrictionForInterferenceMeasurements ENUMERATED {configured, notConfigured}, + codebookConfig CodebookConfig OPTIONAL, -- Need R + dummy ENUMERATED {n1, n2} OPTIONAL, -- Need R + groupBasedBeamReporting CHOICE { + enabled NULL, + disabled SEQUENCE { + nrofReportedRS ENUMERATED {n1, n2, n3, n4} OPTIONAL -- Need S + } + }, + cqi-Table ENUMERATED {table1, table2, table3, spare1} OPTIONAL, -- Need R + subbandSize ENUMERATED {value1, value2}, + non-PMI-PortIndication SEQUENCE (SIZE (1..maxNrofNZP-CSI-RS-ResourcesPerConfig)) OF PortIndexFor8Ranks OPTIONAL, -- Need R + ..., + [[ + semiPersistentOnPUSCH-v1530 SEQUENCE { + reportSlotConfig-v1530 ENUMERATED {sl4, sl8, sl16} + } OPTIONAL -- Need R + ]], + [[ + semiPersistentOnPUSCH-v1610 SEQUENCE { + reportSlotOffsetListDCI-0-2-r16 SEQUENCE (SIZE (1.. maxNrofUL-Allocations-r16)) OF INTEGER(0..32) OPTIONAL, -- Need R + reportSlotOffsetListDCI-0-1-r16 SEQUENCE (SIZE (1.. maxNrofUL-Allocations-r16)) OF INTEGER(0..32) OPTIONAL -- Need R + } OPTIONAL, -- Need R + aperiodic-v1610 SEQUENCE { + reportSlotOffsetListDCI-0-2-r16 SEQUENCE (SIZE (1.. maxNrofUL-Allocations-r16)) OF INTEGER(0..32) OPTIONAL, -- Need R + reportSlotOffsetListDCI-0-1-r16 SEQUENCE (SIZE (1.. maxNrofUL-Allocations-r16)) OF INTEGER(0..32) OPTIONAL -- Need R + } OPTIONAL, -- Need R + reportQuantity-r16 CHOICE { + cri-SINR-r16 NULL, + ssb-Index-SINR-r16 NULL + } OPTIONAL, -- Need R + codebookConfig-r16 CodebookConfig-r16 OPTIONAL -- Need R + ]] +} + +CSI-ReportPeriodicityAndOffset ::= CHOICE { + slots4 INTEGER(0..3), + slots5 INTEGER(0..4), + slots8 INTEGER(0..7), + slots10 INTEGER(0..9), + slots16 INTEGER(0..15), + slots20 INTEGER(0..19), + slots40 INTEGER(0..39), + slots80 INTEGER(0..79), + slots160 INTEGER(0..159), + slots320 INTEGER(0..319) +} + +PUCCH-CSI-Resource ::= SEQUENCE { + uplinkBandwidthPartId BWP-Id, + pucch-Resource PUCCH-ResourceId +} + +PortIndexFor8Ranks ::= CHOICE { + portIndex8 SEQUENCE{ + rank1-8 PortIndex8 OPTIONAL, -- Need R + rank2-8 SEQUENCE(SIZE(2)) OF PortIndex8 OPTIONAL, -- Need R + rank3-8 SEQUENCE(SIZE(3)) OF PortIndex8 OPTIONAL, -- Need R + rank4-8 SEQUENCE(SIZE(4)) OF PortIndex8 OPTIONAL, -- Need R + rank5-8 SEQUENCE(SIZE(5)) OF PortIndex8 OPTIONAL, -- Need R + rank6-8 SEQUENCE(SIZE(6)) OF PortIndex8 OPTIONAL, -- Need R + rank7-8 SEQUENCE(SIZE(7)) OF PortIndex8 OPTIONAL, -- Need R + rank8-8 SEQUENCE(SIZE(8)) OF PortIndex8 OPTIONAL -- Need R + }, + portIndex4 SEQUENCE{ + rank1-4 PortIndex4 OPTIONAL, -- Need R + rank2-4 SEQUENCE(SIZE(2)) OF PortIndex4 OPTIONAL, -- Need R + rank3-4 SEQUENCE(SIZE(3)) OF PortIndex4 OPTIONAL, -- Need R + rank4-4 SEQUENCE(SIZE(4)) OF PortIndex4 OPTIONAL -- Need R + }, + portIndex2 SEQUENCE{ + rank1-2 PortIndex2 OPTIONAL, -- Need R + rank2-2 SEQUENCE(SIZE(2)) OF PortIndex2 OPTIONAL -- Need R + }, + portIndex1 NULL +} + +PortIndex8::= INTEGER (0..7) +PortIndex4::= INTEGER (0..3) +PortIndex2::= INTEGER (0..1) + +-- TAG-CSI-REPORTCONFIG-STOP +-- TAG-CSI-REPORTCONFIGID-START + +CSI-ReportConfigId ::= INTEGER (0..maxNrofCSI-ReportConfigurations-1) + +-- TAG-CSI-REPORTCONFIGID-STOP +-- TAG-CSI-RESOURCECONFIG-START + +CSI-ResourceConfig ::= SEQUENCE { + csi-ResourceConfigId CSI-ResourceConfigId, + csi-RS-ResourceSetList CHOICE { + nzp-CSI-RS-SSB SEQUENCE { + nzp-CSI-RS-ResourceSetList SEQUENCE (SIZE (1..maxNrofNZP-CSI-RS-ResourceSetsPerConfig)) OF NZP-CSI-RS-ResourceSetId + OPTIONAL, -- Need R + csi-SSB-ResourceSetList SEQUENCE (SIZE (1..maxNrofCSI-SSB-ResourceSetsPerConfig)) OF CSI-SSB-ResourceSetId OPTIONAL -- Need R + }, + csi-IM-ResourceSetList SEQUENCE (SIZE (1..maxNrofCSI-IM-ResourceSetsPerConfig)) OF CSI-IM-ResourceSetId + }, + + bwp-Id BWP-Id, + resourceType ENUMERATED { aperiodic, semiPersistent, periodic }, + ... +} + +-- TAG-CSI-RESOURCECONFIG-STOP +-- TAG-CSI-RESOURCECONFIGID-START + +CSI-ResourceConfigId ::= INTEGER (0..maxNrofCSI-ResourceConfigurations-1) + +-- TAG-CSI-RESOURCECONFIGID-STOP +-- TAG-CSI-RESOURCEPERIODICITYANDOFFSET-START + +CSI-ResourcePeriodicityAndOffset ::= CHOICE { + slots4 INTEGER (0..3), + slots5 INTEGER (0..4), + slots8 INTEGER (0..7), + slots10 INTEGER (0..9), + slots16 INTEGER (0..15), + slots20 INTEGER (0..19), + slots32 INTEGER (0..31), + slots40 INTEGER (0..39), + slots64 INTEGER (0..63), + slots80 INTEGER (0..79), + slots160 INTEGER (0..159), + slots320 INTEGER (0..319), + slots640 INTEGER (0..639) +} + +-- TAG-CSI-RESOURCEPERIODICITYANDOFFSET-STOP +-- TAG-CSI-RS-RESOURCECONFIGMOBILITY-START + +CSI-RS-ResourceConfigMobility ::= SEQUENCE { + subcarrierSpacing SubcarrierSpacing, + csi-RS-CellList-Mobility SEQUENCE (SIZE (1..maxNrofCSI-RS-CellsRRM)) OF CSI-RS-CellMobility, + ..., + [[ + refServCellIndex ServCellIndex OPTIONAL -- Need S + ]] + + +} + +CSI-RS-CellMobility ::= SEQUENCE { + cellId PhysCellId, + csi-rs-MeasurementBW SEQUENCE { + nrofPRBs ENUMERATED { size24, size48, size96, size192, size264}, + startPRB INTEGER(0..2169) + }, + density ENUMERATED {d1,d3} OPTIONAL, -- Need R + csi-rs-ResourceList-Mobility SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesRRM)) OF CSI-RS-Resource-Mobility +} + +CSI-RS-Resource-Mobility ::= SEQUENCE { + csi-RS-Index CSI-RS-Index, + slotConfig CHOICE { + ms4 INTEGER (0..31), + ms5 INTEGER (0..39), + ms10 INTEGER (0..79), + ms20 INTEGER (0..159), + ms40 INTEGER (0..319) + }, + associatedSSB SEQUENCE { + ssb-Index SSB-Index, + isQuasiColocated BOOLEAN + } OPTIONAL, -- Need R + frequencyDomainAllocation CHOICE { + row1 BIT STRING (SIZE (4)), + row2 BIT STRING (SIZE (12)) + }, + firstOFDMSymbolInTimeDomain INTEGER (0..13), + sequenceGenerationConfig INTEGER (0..1023), + ... +} + +CSI-RS-Index ::= INTEGER (0..maxNrofCSI-RS-ResourcesRRM-1) + +-- TAG-CSI-RS-RESOURCECONFIGMOBILITY-STOP +-- TAG-CSI-RS-RESOURCEMAPPING-START + +CSI-RS-ResourceMapping ::= SEQUENCE { + frequencyDomainAllocation CHOICE { + row1 BIT STRING (SIZE (4)), + row2 BIT STRING (SIZE (12)), + row4 BIT STRING (SIZE (3)), + other BIT STRING (SIZE (6)) + }, + nrofPorts ENUMERATED {p1,p2,p4,p8,p12,p16,p24,p32}, + firstOFDMSymbolInTimeDomain INTEGER (0..13), + firstOFDMSymbolInTimeDomain2 INTEGER (2..12) OPTIONAL, -- Need R + cdm-Type ENUMERATED {noCDM, fd-CDM2, cdm4-FD2-TD2, cdm8-FD2-TD4}, + density CHOICE { + dot5 ENUMERATED {evenPRBs, oddPRBs}, + one NULL, + three NULL, + spare NULL + }, + freqBand CSI-FrequencyOccupation, + ... +} + +-- TAG-CSI-RS-RESOURCEMAPPING-STOP +-- TAG-CSI-SEMIPERSISTENTONPUSCHTRIGGERSTATELIST-START + +CSI-SemiPersistentOnPUSCH-TriggerStateList ::= SEQUENCE(SIZE (1..maxNrOfSemiPersistentPUSCH-Triggers)) OF CSI-SemiPersistentOnPUSCH-TriggerState + +CSI-SemiPersistentOnPUSCH-TriggerState ::= SEQUENCE { + associatedReportConfigInfo CSI-ReportConfigId, + ... +} + +-- TAG-CSI-SEMIPERSISTENTONPUSCHTRIGGERSTATELIST-STOP +-- TAG-CSI-SSB-RESOURCESET-START + +CSI-SSB-ResourceSet ::= SEQUENCE { + csi-SSB-ResourceSetId CSI-SSB-ResourceSetId, + csi-SSB-ResourceList SEQUENCE (SIZE(1..maxNrofCSI-SSB-ResourcePerSet)) OF SSB-Index, + ... +} + +-- TAG-CSI-SSB-RESOURCESET-STOP +-- TAG-CSI-SSB-RESOURCESETID-START + +CSI-SSB-ResourceSetId ::= INTEGER (0..maxNrofCSI-SSB-ResourceSets-1) + +-- TAG-CSI-SSB-RESOURCESETID-STOP +-- TAG-DEDICATED-NAS-MESSAGE-START + +DedicatedNAS-Message ::= OCTET STRING + +-- TAG-DEDICATED-NAS-MESSAGE-STOP +-- TAG-DMRS-DOWNLINKCONFIG-START + +DMRS-DownlinkConfig ::= SEQUENCE { + dmrs-Type ENUMERATED {type2} OPTIONAL, -- Need S + dmrs-AdditionalPosition ENUMERATED {pos0, pos1, pos3} OPTIONAL, -- Need S + maxLength ENUMERATED {len2} OPTIONAL, -- Need S + scramblingID0 INTEGER (0..65535) OPTIONAL, -- Need S + scramblingID1 INTEGER (0..65535) OPTIONAL, -- Need S + phaseTrackingRS SetupRelease { PTRS-DownlinkConfig } OPTIONAL, -- Need M + ..., + [[ + dmrs-Downlink-r16 ENUMERATED {enabled} OPTIONAL -- Need R + ]] + +} + +-- TAG-DMRS-DOWNLINKCONFIG-STOP +-- TAG-DMRS-UPLINKCONFIG-START + +DMRS-UplinkConfig ::= SEQUENCE { + dmrs-Type ENUMERATED {type2} OPTIONAL, -- Need S + dmrs-AdditionalPosition ENUMERATED {pos0, pos1, pos3} OPTIONAL, -- Need S + phaseTrackingRS SetupRelease { PTRS-UplinkConfig } OPTIONAL, -- Need M + maxLength ENUMERATED {len2} OPTIONAL, -- Need S + transformPrecodingDisabled SEQUENCE { + scramblingID0 INTEGER (0..65535) OPTIONAL, -- Need S + scramblingID1 INTEGER (0..65535) OPTIONAL, -- Need S + ..., + [[ + dmrs-Uplink-r16 ENUMERATED {enabled} OPTIONAL -- Need R + ]] + } OPTIONAL, -- Need R + transformPrecodingEnabled SEQUENCE { + nPUSCH-Identity INTEGER(0..1007) OPTIONAL, -- Need S + sequenceGroupHopping ENUMERATED {disabled} OPTIONAL, -- Need S + sequenceHopping ENUMERATED {enabled} OPTIONAL, -- Need S + ..., + [[ + dmrs-UplinkTransformPrecoding-r16 SetupRelease {DMRS-UplinkTransformPrecoding-r16} OPTIONAL -- Need M + ]] + } OPTIONAL, -- Need R + ... +} + +DMRS-UplinkTransformPrecoding-r16 ::= SEQUENCE { + pi2BPSK-ScramblingID0 INTEGER(0..65535) OPTIONAL, -- Need S + pi2BPSK-ScramblingID1 INTEGER(0..65535) OPTIONAL -- Need S +} + +-- TAG-DMRS-UPLINKCONFIG-STOP +-- TAG-DOWNLINKCONFIGCOMMON-START + +DownlinkConfigCommon ::= SEQUENCE { + frequencyInfoDL FrequencyInfoDL OPTIONAL, -- Cond InterFreqHOAndServCellAdd + initialDownlinkBWP BWP-DownlinkCommon OPTIONAL, -- Cond ServCellAdd + ... +} + +-- TAG-DOWNLINKCONFIGCOMMON-STOP +-- TAG-DOWNLINKCONFIGCOMMONSIB-START + +DownlinkConfigCommonSIB ::= SEQUENCE { + frequencyInfoDL FrequencyInfoDL-SIB, + initialDownlinkBWP BWP-DownlinkCommon, + bcch-Config BCCH-Config, + pcch-Config PCCH-Config, + ... +} + + +BCCH-Config ::= SEQUENCE { + modificationPeriodCoeff ENUMERATED {n2, n4, n8, n16}, + ... +} + + +PCCH-Config ::= SEQUENCE { + defaultPagingCycle PagingCycle, + nAndPagingFrameOffset CHOICE { + oneT NULL, + halfT INTEGER (0..1), + quarterT INTEGER (0..3), + oneEighthT INTEGER (0..7), + oneSixteenthT INTEGER (0..15) + }, + ns ENUMERATED {four, two, one}, + firstPDCCH-MonitoringOccasionOfPO CHOICE { + sCS15KHZoneT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..139), + sCS30KHZoneT-SCS15KHZhalfT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..279), + sCS60KHZoneT-SCS30KHZhalfT-SCS15KHZquarterT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..559), + sCS120KHZoneT-SCS60KHZhalfT-SCS30KHZquarterT-SCS15KHZoneEighthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..1119), + sCS120KHZhalfT-SCS60KHZquarterT-SCS30KHZoneEighthT-SCS15KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..2239), + sCS120KHZquarterT-SCS60KHZoneEighthT-SCS30KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..4479), + sCS120KHZoneEighthT-SCS60KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..8959), + sCS120KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..17919) + } OPTIONAL, -- Need R + ..., + [[ + nrofPDCCH-MonitoringOccasionPerSSB-InPO-r16 INTEGER (2..4) OPTIONAL -- Cond SharedSpectrum2 + ]] +} + +-- TAG-DOWNLINKCONFIGCOMMONSIB-STOP +-- TAG-DOWNLINKPREEMPTION-START + +DownlinkPreemption ::= SEQUENCE { + int-RNTI RNTI-Value, + timeFrequencySet ENUMERATED {set0, set1}, + dci-PayloadSize INTEGER (0..maxINT-DCI-PayloadSize), + int-ConfigurationPerServingCell SEQUENCE (SIZE (1..maxNrofServingCells)) OF INT-ConfigurationPerServingCell, + ... +} + +INT-ConfigurationPerServingCell ::= SEQUENCE { + servingCellId ServCellIndex, + positionInDCI INTEGER (0..maxINT-DCI-PayloadSize-1) +} + +-- TAG-DOWNLINKPREEMPTION-STOP +-- TAG-DRB-IDENTITY-START + +DRB-Identity ::= INTEGER (1..32) + +-- TAG-DRB-IDENTITY-STOP +-- TAG-DRX-CONFIG-START + +DRX-Config ::= SEQUENCE { + drx-onDurationTimer CHOICE { + subMilliSeconds INTEGER (1..31), + milliSeconds ENUMERATED { + ms1, ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, ms40, ms50, ms60, + ms80, ms100, ms200, ms300, ms400, ms500, ms600, ms800, ms1000, ms1200, + ms1600, spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + }, + drx-InactivityTimer ENUMERATED { + ms0, ms1, ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, ms40, ms50, ms60, ms80, + ms100, ms200, ms300, ms500, ms750, ms1280, ms1920, ms2560, spare9, spare8, + spare7, spare6, spare5, spare4, spare3, spare2, spare1}, + drx-HARQ-RTT-TimerDL INTEGER (0..56), + drx-HARQ-RTT-TimerUL INTEGER (0..56), + drx-RetransmissionTimerDL ENUMERATED { + sl0, sl1, sl2, sl4, sl6, sl8, sl16, sl24, sl33, sl40, sl64, sl80, sl96, sl112, sl128, + sl160, sl320, spare15, spare14, spare13, spare12, spare11, spare10, spare9, + spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1}, + drx-RetransmissionTimerUL ENUMERATED { + sl0, sl1, sl2, sl4, sl6, sl8, sl16, sl24, sl33, sl40, sl64, sl80, sl96, sl112, sl128, + sl160, sl320, spare15, spare14, spare13, spare12, spare11, spare10, spare9, + spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 }, + drx-LongCycleStartOffset CHOICE { + ms10 INTEGER(0..9), + ms20 INTEGER(0..19), + ms32 INTEGER(0..31), + ms40 INTEGER(0..39), + ms60 INTEGER(0..59), + ms64 INTEGER(0..63), + ms70 INTEGER(0..69), + ms80 INTEGER(0..79), + ms128 INTEGER(0..127), + ms160 INTEGER(0..159), + ms256 INTEGER(0..255), + ms320 INTEGER(0..319), + ms512 INTEGER(0..511), + ms640 INTEGER(0..639), + ms1024 INTEGER(0..1023), + ms1280 INTEGER(0..1279), + ms2048 INTEGER(0..2047), + ms2560 INTEGER(0..2559), + ms5120 INTEGER(0..5119), + ms10240 INTEGER(0..10239) + }, + shortDRX SEQUENCE { + drx-ShortCycle ENUMERATED { + ms2, ms3, ms4, ms5, ms6, ms7, ms8, ms10, ms14, ms16, ms20, ms30, ms32, + ms35, ms40, ms64, ms80, ms128, ms160, ms256, ms320, ms512, ms640, spare9, + spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 }, + drx-ShortCycleTimer INTEGER (1..16) + } OPTIONAL, -- Need R + drx-SlotOffset INTEGER (0..31) +} + +-- TAG-DRX-CONFIG-STOP +-- TAG-DRX-CONFIGSECONDARYGROUP-START + +DRX-ConfigSecondaryGroup ::= SEQUENCE { + drx-onDurationTimer CHOICE { + subMilliSeconds INTEGER (1..31), + milliSeconds ENUMERATED { + ms1, ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, ms40, ms50, ms60, + ms80, ms100, ms200, ms300, ms400, ms500, ms600, ms800, ms1000, ms1200, + ms1600, spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + }, + drx-InactivityTimer ENUMERATED { + ms0, ms1, ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, ms40, ms50, ms60, ms80, + ms100, ms200, ms300, ms500, ms750, ms1280, ms1920, ms2560, spare9, spare8, + spare7, spare6, spare5, spare4, spare3, spare2, spare1} +} + +-- TAG-DRX-CONFIGSECONDARYGROUP-STOP +-- TAG-FILTERCOEFFICIENT-START + +FilterCoefficient ::= ENUMERATED { fc0, fc1, fc2, fc3, fc4, fc5, fc6, fc7, fc8, fc9, fc11, fc13, fc15, fc17, fc19, spare1, ...} + +-- TAG-FILTERCOEFFICIENT-STOP +-- TAG-FREQBANDINDICATORNR-START + +FreqBandIndicatorNR ::= INTEGER (1..1024) + +-- TAG-FREQBANDINDICATORNR-STOP +-- TAG-FREQUENCYINFODL-START + +FrequencyInfoDL ::= SEQUENCE { + absoluteFrequencySSB ARFCN-ValueNR OPTIONAL, -- Cond SpCellAdd + frequencyBandList MultiFrequencyBandListNR, + absoluteFrequencyPointA ARFCN-ValueNR, + scs-SpecificCarrierList SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier, + ... +} + +-- TAG-FREQUENCYINFODL-STOP +-- TAG-FREQUENCYINFODL-SIB-START + +FrequencyInfoDL-SIB ::= SEQUENCE { + frequencyBandList MultiFrequencyBandListNR-SIB, + offsetToPointA INTEGER (0..2199), + scs-SpecificCarrierList SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier +} + +-- TAG-FREQUENCYINFODL-SIB-STOP +-- TAG-FREQUENCYINFOUL-START + +FrequencyInfoUL ::= SEQUENCE { + frequencyBandList MultiFrequencyBandListNR OPTIONAL, -- Cond FDD-OrSUL + absoluteFrequencyPointA ARFCN-ValueNR OPTIONAL, -- Cond FDD-OrSUL + scs-SpecificCarrierList SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier, + additionalSpectrumEmission AdditionalSpectrumEmission OPTIONAL, -- Need S + p-Max P-Max OPTIONAL, -- Need S + frequencyShift7p5khz ENUMERATED {true} OPTIONAL, -- Cond FDD-TDD-OrSUL-Optional + ... +} + +-- TAG-FREQUENCYINFOUL-STOP +-- TAG-FREQUENCYINFOUL-SIB-START + +FrequencyInfoUL-SIB ::= SEQUENCE { + frequencyBandList MultiFrequencyBandListNR-SIB OPTIONAL, -- Cond FDD-OrSUL + absoluteFrequencyPointA ARFCN-ValueNR OPTIONAL, -- Cond FDD-OrSUL + scs-SpecificCarrierList SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier, + p-Max P-Max OPTIONAL, -- Need S + frequencyShift7p5khz ENUMERATED {true} OPTIONAL, -- Cond FDD-TDD-OrSUL-Optional + ... +} + +-- TAG-FREQUENCYINFOUL-SIB-STOP +-- TAG-HIGHSPEEDCONFIG-START + +HighSpeedConfig-r16 ::= SEQUENCE { + highSpeedMeasFlag-r16 ENUMERATED {true} OPTIONAL, -- Need R + highSpeedDemodFlag-r16 ENUMERATED {true} OPTIONAL, -- Need R + ... +} + +-- TAG-HIGHSPEEDCONFIG-STOP +-- TAG-HYSTERESIS-START + +Hysteresis ::= INTEGER (0..30) + +-- TAG-HYSTERESIS-STOP +-- TAG-INVALIDSYMBOLPATTERN-START + +InvalidSymbolPattern-r16 ::= SEQUENCE { + symbols-r16 CHOICE { + oneSlot BIT STRING (SIZE (14)), + twoSlots BIT STRING (SIZE (28)) + }, + periodicityAndPattern-r16 CHOICE { + n2 BIT STRING (SIZE (2)), + n4 BIT STRING (SIZE (4)), + n5 BIT STRING (SIZE (5)), + n8 BIT STRING (SIZE (8)), + n10 BIT STRING (SIZE (10)), + n20 BIT STRING (SIZE (20)), + n40 BIT STRING (SIZE (40)) + } OPTIONAL, -- Need M + ... +} + +-- TAG-INVALIDSYMBOLPATTERN-STOP +-- TAG-I-RNTI-VALUE-START + +I-RNTI-Value ::= BIT STRING (SIZE(40)) + +-- TAG-I-RNTI-VALUE-STOP +-- TAG-LBT-FAILURERECOVERYCONFIG-START + +LBT-FailureRecoveryConfig-r16 ::= SEQUENCE { + lbt-FailureInstanceMaxCount-r16 ENUMERATED {n4, n8, n16, n32, n64, n128}, + lbt-FailureDetectionTimer-r16 ENUMERATED {ms10, ms20, ms40, ms80, ms160, ms320}, + ... +} + +-- TAG-LBT-FAILURERECOVERYCONFIG-STOP +-- TAG-LOCATIONINFO-START + +LocationInfo-r16 ::= SEQUENCE { + commonLocationInfo-r16 CommonLocationInfo-r16 OPTIONAL, + bt-LocationInfo-r16 LogMeasResultListBT-r16 OPTIONAL, + wlan-LocationInfo-r16 LogMeasResultListWLAN-r16 OPTIONAL, + sensor-LocationInfo-r16 Sensor-LocationInfo-r16 OPTIONAL, + ... +} + +-- TAG-LOCATIONINFO-STOP +-- TAG-LOCATIONMEASUREMENTINFO-START + +LocationMeasurementInfo ::= CHOICE { + eutra-RSTD EUTRA-RSTD-InfoList, + ..., + eutra-FineTimingDetection NULL, + nr-PRS-Measurement-r16 NR-PRS-MeasurementInfoList-r16 +} + +EUTRA-RSTD-InfoList ::= SEQUENCE (SIZE (1..maxInterRAT-RSTD-Freq)) OF EUTRA-RSTD-Info + +EUTRA-RSTD-Info ::= SEQUENCE { + carrierFreq ARFCN-ValueEUTRA, + measPRS-Offset INTEGER (0..39), + ... +} + +NR-PRS-MeasurementInfoList-r16 ::= SEQUENCE (SIZE (1..maxFreqLayers)) OF NR-PRS-MeasurementInfo-r16 + +NR-PRS-MeasurementInfo-r16 ::= SEQUENCE { + dl-PRS-PointA-r16 ARFCN-ValueNR, + nr-MeasPRS-RepetitionAndOffset-r16 CHOICE { + ms20-r16 INTEGER (0..19), + ms40-r16 INTEGER (0..39), + ms80-r16 INTEGER (0..79), + ms160-r16 INTEGER (0..159), + ... + }, + nr-MeasPRS-length-r16 ENUMERATED {ms1dot5, ms3, ms3dot5, ms4, ms5dot5, ms6, ms10, ms20}, + ... +} + +-- TAG-LOCATIONMEASUREMENTINFO-STOP +-- TAG-LOGICALCHANNELCONFIG-START + +LogicalChannelConfig ::= SEQUENCE { + ul-SpecificParameters SEQUENCE { + priority INTEGER (1..16), + prioritisedBitRate ENUMERATED {kBps0, kBps8, kBps16, kBps32, kBps64, kBps128, kBps256, kBps512, + kBps1024, kBps2048, kBps4096, kBps8192, kBps16384, kBps32768, kBps65536, infinity}, + bucketSizeDuration ENUMERATED {ms5, ms10, ms20, ms50, ms100, ms150, ms300, ms500, ms1000, + spare7, spare6, spare5, spare4, spare3,spare2, spare1}, + allowedServingCells SEQUENCE (SIZE (1..maxNrofServingCells-1)) OF ServCellIndex + OPTIONAL, -- Cond PDCP-CADuplication + allowedSCS-List SEQUENCE (SIZE (1..maxSCSs)) OF SubcarrierSpacing OPTIONAL, -- Need R + maxPUSCH-Duration ENUMERATED {ms0p02, ms0p04, ms0p0625, ms0p125, ms0p25, ms0p5, spare2, spare1} + OPTIONAL, -- Need R + configuredGrantType1Allowed ENUMERATED {true} OPTIONAL, -- Need R + logicalChannelGroup INTEGER (0..maxLCG-ID) OPTIONAL, -- Need R + schedulingRequestID SchedulingRequestId OPTIONAL, -- Need R + logicalChannelSR-Mask BOOLEAN, + logicalChannelSR-DelayTimerApplied BOOLEAN, + ..., + bitRateQueryProhibitTimer ENUMERATED {s0, s0dot4, s0dot8, s1dot6, s3, s6, s12, s30} OPTIONAL, -- Need R + [[ + allowedCG-List-r16 SEQUENCE (SIZE (0.. maxNrofConfiguredGrantConfigMAC-r16-1)) OF ConfiguredGrantConfigIndexMAC-r16 + OPTIONAL, -- Need S + allowedPHY-PriorityIndex-r16 ENUMERATED {p0, p1} OPTIONAL -- Need S + ]] + } OPTIONAL, -- Cond UL + ..., + [[ + channelAccessPriority-r16 INTEGER (1..4) OPTIONAL, -- Need R + bitRateMultiplier-r16 ENUMERATED {x40, x70, x100, x200} OPTIONAL -- Need R + ]] +} + +-- TAG-LOGICALCHANNELCONFIG-STOP +-- TAG-LOGICALCHANNELIDENTITY-START + +LogicalChannelIdentity ::= INTEGER (1..maxLC-ID) + +-- TAG-LOGICALCHANNELIDENTITY-STOP +-- TAG-MAC-CELLGROUPCONFIG-START + +MAC-CellGroupConfig ::= SEQUENCE { + drx-Config SetupRelease { DRX-Config } OPTIONAL, -- Need M + schedulingRequestConfig SchedulingRequestConfig OPTIONAL, -- Need M + bsr-Config BSR-Config OPTIONAL, -- Need M + tag-Config TAG-Config OPTIONAL, -- Need M + phr-Config SetupRelease { PHR-Config } OPTIONAL, -- Need M + skipUplinkTxDynamic BOOLEAN, + ..., + [[ + csi-Mask BOOLEAN OPTIONAL, -- Need M + dataInactivityTimer SetupRelease { DataInactivityTimer } OPTIONAL -- Cond MCG-Only + ]], + [[ + usePreBSR-r16 ENUMERATED {true} OPTIONAL, -- Need R + schedulingRequestID-LBT-SCell-r16 SchedulingRequestId OPTIONAL, -- Need R + lch-BasedPrioritization-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + schedulingRequestID-BFR-SCell-r16 SchedulingRequestId OPTIONAL, -- Need R + drx-ConfigSecondaryGroup-r16 SetupRelease { DRX-ConfigSecondaryGroup } OPTIONAL -- Need M + ]], + [[ + enhancedSkipUplinkTxDynamic-r16 ENUMERATED {true} OPTIONAL, -- Need R + enhancedSkipUplinkTxConfigured-r16 ENUMERATED {true} OPTIONAL -- Need R + ]] +} + +DataInactivityTimer ::= ENUMERATED {s1, s2, s3, s5, s7, s10, s15, s20, s40, s50, s60, s80, s100, s120, s150, s180} + +-- TAG-MAC-CELLGROUPCONFIG-STOP +-- TAG-MEASCONFIG-START + +MeasConfig ::= SEQUENCE { + measObjectToRemoveList MeasObjectToRemoveList OPTIONAL, -- Need N + measObjectToAddModList MeasObjectToAddModList OPTIONAL, -- Need N + reportConfigToRemoveList ReportConfigToRemoveList OPTIONAL, -- Need N + reportConfigToAddModList ReportConfigToAddModList OPTIONAL, -- Need N + measIdToRemoveList MeasIdToRemoveList OPTIONAL, -- Need N + measIdToAddModList MeasIdToAddModList OPTIONAL, -- Need N + s-MeasureConfig CHOICE { + ssb-RSRP RSRP-Range, + csi-RSRP RSRP-Range + } OPTIONAL, -- Need M + quantityConfig QuantityConfig OPTIONAL, -- Need M + measGapConfig MeasGapConfig OPTIONAL, -- Need M + measGapSharingConfig MeasGapSharingConfig OPTIONAL, -- Need M + ..., + [[ + interFrequencyConfig-NoGap-r16 ENUMERATED {true} OPTIONAL -- Need R + ]] +} + +MeasObjectToRemoveList ::= SEQUENCE (SIZE (1..maxNrofObjectId)) OF MeasObjectId + +MeasIdToRemoveList ::= SEQUENCE (SIZE (1..maxNrofMeasId)) OF MeasId + +ReportConfigToRemoveList ::= SEQUENCE (SIZE (1..maxReportConfigId)) OF ReportConfigId + +-- TAG-MEASCONFIG-STOP +-- TAG-MEASGAPCONFIG-START + +MeasGapConfig ::= SEQUENCE { + gapFR2 SetupRelease { GapConfig } OPTIONAL, -- Need M + ..., + [[ + gapFR1 SetupRelease { GapConfig } OPTIONAL, -- Need M + gapUE SetupRelease { GapConfig } OPTIONAL -- Need M + ]] + +} + +GapConfig ::= SEQUENCE { + gapOffset INTEGER (0..159), + mgl ENUMERATED {ms1dot5, ms3, ms3dot5, ms4, ms5dot5, ms6}, + mgrp ENUMERATED {ms20, ms40, ms80, ms160}, + mgta ENUMERATED {ms0, ms0dot25, ms0dot5}, + ..., + [[ + refServCellIndicator ENUMERATED {pCell, pSCell, mcg-FR2} OPTIONAL -- Cond NEDCorNRDC + ]], + [[ + refFR2ServCellAsyncCA-r16 ServCellIndex OPTIONAL, -- Cond AsyncCA + mgl-r16 ENUMERATED {ms10, ms20} OPTIONAL -- Cond PRS + ]] +} + +-- TAG-MEASGAPCONFIG-STOP +-- TAG-MEASGAPSHARINGCONFIG-START + +MeasGapSharingConfig ::= SEQUENCE { + gapSharingFR2 SetupRelease { MeasGapSharingScheme } OPTIONAL, -- Need M + ..., + [[ + gapSharingFR1 SetupRelease { MeasGapSharingScheme } OPTIONAL, --Need M + gapSharingUE SetupRelease { MeasGapSharingScheme } OPTIONAL --Need M + ]] +} + +MeasGapSharingScheme::= ENUMERATED {scheme00, scheme01, scheme10, scheme11} + +-- TAG-MEASGAPSHARINGCONFIG-STOP +-- TAG-MEASID-START + +MeasId ::= INTEGER (1..maxNrofMeasId) + +-- TAG-MEASID-STOP +-- TAG-MEASIDLECONFIG-START + +MeasIdleConfigSIB-r16 ::= SEQUENCE { + measIdleCarrierListNR-r16 SEQUENCE (SIZE (1..maxFreqIdle-r16)) OF MeasIdleCarrierNR-r16 OPTIONAL, -- Need S + measIdleCarrierListEUTRA-r16 SEQUENCE (SIZE (1..maxFreqIdle-r16)) OF MeasIdleCarrierEUTRA-r16 OPTIONAL, -- Need S + ... +} + +MeasIdleConfigDedicated-r16 ::= SEQUENCE { + measIdleCarrierListNR-r16 SEQUENCE (SIZE (1..maxFreqIdle-r16)) OF MeasIdleCarrierNR-r16 OPTIONAL, -- Need N + measIdleCarrierListEUTRA-r16 SEQUENCE (SIZE (1..maxFreqIdle-r16)) OF MeasIdleCarrierEUTRA-r16 OPTIONAL, -- Need N + measIdleDuration-r16 ENUMERATED{sec10, sec30, sec60, sec120, sec180, sec240, sec300, spare}, + validityAreaList-r16 ValidityAreaList-r16 OPTIONAL, -- Need N + ... +} + +ValidityAreaList-r16 ::= SEQUENCE (SIZE (1..maxFreqIdle-r16)) OF ValidityArea-r16 + +ValidityArea-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueNR, + validityCellList-r16 ValidityCellList OPTIONAL -- Need N +} + +ValidityCellList ::= SEQUENCE (SIZE (1.. maxCellMeasIdle-r16)) OF PCI-Range + +MeasIdleCarrierNR-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueNR, + ssbSubcarrierSpacing-r16 SubcarrierSpacing, + frequencyBandList MultiFrequencyBandListNR OPTIONAL, -- Need R + measCellListNR-r16 CellListNR-r16 OPTIONAL, -- Need R + reportQuantities-r16 ENUMERATED {rsrp, rsrq, both}, + qualityThreshold-r16 SEQUENCE { + idleRSRP-Threshold-NR-r16 RSRP-Range OPTIONAL, -- Need R + idleRSRQ-Threshold-NR-r16 RSRQ-Range OPTIONAL -- Need R + } OPTIONAL, -- Need R + ssb-MeasConfig-r16 SEQUENCE { + nrofSS-BlocksToAverage-r16 INTEGER (2..maxNrofSS-BlocksToAverage) OPTIONAL, -- Need S + absThreshSS-BlocksConsolidation-r16 ThresholdNR OPTIONAL, -- Need S + smtc-r16 SSB-MTC OPTIONAL, -- Need S + ssb-ToMeasure-r16 SSB-ToMeasure OPTIONAL, -- Need S + deriveSSB-IndexFromCell-r16 BOOLEAN, + ss-RSSI-Measurement-r16 SS-RSSI-Measurement OPTIONAL -- Need S + } OPTIONAL, -- Need S + beamMeasConfigIdle-r16 BeamMeasConfigIdle-NR-r16 OPTIONAL, -- Need R + ... +} + +MeasIdleCarrierEUTRA-r16 ::= SEQUENCE { + carrierFreqEUTRA-r16 ARFCN-ValueEUTRA, + allowedMeasBandwidth-r16 EUTRA-AllowedMeasBandwidth, + measCellListEUTRA-r16 CellListEUTRA-r16 OPTIONAL, -- Need R + reportQuantitiesEUTRA-r16 ENUMERATED {rsrp, rsrq, both}, + qualityThresholdEUTRA-r16 SEQUENCE { + idleRSRP-Threshold-EUTRA-r16 RSRP-RangeEUTRA OPTIONAL, -- Need R + idleRSRQ-Threshold-EUTRA-r16 RSRQ-RangeEUTRA-r16 OPTIONAL -- Need R + } OPTIONAL, -- Need S + ... +} + +CellListNR-r16 ::= SEQUENCE (SIZE (1..maxCellMeasIdle-r16)) OF PCI-Range + +CellListEUTRA-r16 ::= SEQUENCE (SIZE (1..maxCellMeasIdle-r16)) OF EUTRA-PhysCellIdRange + +BeamMeasConfigIdle-NR-r16 ::= SEQUENCE { + reportQuantityRS-Indexes-r16 ENUMERATED {rsrp, rsrq, both}, + maxNrofRS-IndexesToReport-r16 INTEGER (1.. maxNrofIndexesToReport), + includeBeamMeasurements-r16 BOOLEAN +} + +RSRQ-RangeEUTRA-r16 ::= INTEGER (-30..46) + +-- TAG-MEASIDLECONFIG-STOP +-- TAG-MEASIDTOADDMODLIST-START + +MeasIdToAddModList ::= SEQUENCE (SIZE (1..maxNrofMeasId)) OF MeasIdToAddMod + +MeasIdToAddMod ::= SEQUENCE { + measId MeasId, + measObjectId MeasObjectId, + reportConfigId ReportConfigId +} + +-- TAG-MEASIDTOADDMODLIST-STOP +-- TAG-MEASOBJECTCLI-START + +MeasObjectCLI-r16 ::= SEQUENCE { + cli-ResourceConfig-r16 CLI-ResourceConfig-r16, + ... +} + +CLI-ResourceConfig-r16 ::= SEQUENCE { + srs-ResourceConfig-r16 SetupRelease { SRS-ResourceListConfigCLI-r16 } OPTIONAL, -- Need M + rssi-ResourceConfig-r16 SetupRelease { RSSI-ResourceListConfigCLI-r16 } OPTIONAL -- Need M +} + +SRS-ResourceListConfigCLI-r16 ::= SEQUENCE (SIZE (1.. maxNrofCLI-SRS-Resources-r16)) OF SRS-ResourceConfigCLI-r16 + +RSSI-ResourceListConfigCLI-r16 ::= SEQUENCE (SIZE (1.. maxNrofCLI-RSSI-Resources-r16)) OF RSSI-ResourceConfigCLI-r16 + +SRS-ResourceConfigCLI-r16 ::= SEQUENCE { + srs-Resource-r16 SRS-Resource, + srs-SCS-r16 SubcarrierSpacing, + refServCellIndex-r16 ServCellIndex OPTIONAL, -- Need S + refBWP-r16 BWP-Id, + ... +} + +RSSI-ResourceConfigCLI-r16 ::= SEQUENCE { + rssi-ResourceId-r16 RSSI-ResourceId-r16, + rssi-SCS-r16 SubcarrierSpacing, + startPRB-r16 INTEGER (0..2169), + nrofPRBs-r16 INTEGER (4..maxNrofPhysicalResourceBlocksPlus1), + startPosition-r16 INTEGER (0..13), + nrofSymbols-r16 INTEGER (1..14), + rssi-PeriodicityAndOffset-r16 RSSI-PeriodicityAndOffset-r16, + refServCellIndex-r16 ServCellIndex OPTIONAL, -- Need S + ... +} + +RSSI-ResourceId-r16 ::= INTEGER (0.. maxNrofCLI-RSSI-Resources-r16-1) + +RSSI-PeriodicityAndOffset-r16 ::= CHOICE { + sl10 INTEGER(0..9), + sl20 INTEGER(0..19), + sl40 INTEGER(0..39), + sl80 INTEGER(0..79), + sl160 INTEGER(0..159), + sl320 INTEGER(0..319), + s1640 INTEGER(0..639), + ... +} + +-- TAG-MEASOBJECTCLI-STOP +-- TAG-MEASOBJECTEUTRA-START + +MeasObjectEUTRA::= SEQUENCE { + carrierFreq ARFCN-ValueEUTRA, + allowedMeasBandwidth EUTRA-AllowedMeasBandwidth, + cellsToRemoveListEUTRAN EUTRA-CellIndexList OPTIONAL, -- Need N + cellsToAddModListEUTRAN SEQUENCE (SIZE (1..maxCellMeasEUTRA)) OF EUTRA-Cell OPTIONAL, -- Need N + blackCellsToRemoveListEUTRAN EUTRA-CellIndexList OPTIONAL, -- Need N + blackCellsToAddModListEUTRAN SEQUENCE (SIZE (1..maxCellMeasEUTRA)) OF EUTRA-BlackCell OPTIONAL, -- Need N + eutra-PresenceAntennaPort1 EUTRA-PresenceAntennaPort1, + eutra-Q-OffsetRange EUTRA-Q-OffsetRange OPTIONAL, -- Need R + widebandRSRQ-Meas BOOLEAN, + ... +} + +EUTRA-CellIndexList ::= SEQUENCE (SIZE (1..maxCellMeasEUTRA)) OF EUTRA-CellIndex + +EUTRA-CellIndex ::= INTEGER (1..maxCellMeasEUTRA) + + +EUTRA-Cell ::= SEQUENCE { + cellIndexEUTRA EUTRA-CellIndex, + physCellId EUTRA-PhysCellId, + cellIndividualOffset EUTRA-Q-OffsetRange +} + + +EUTRA-BlackCell ::= SEQUENCE { + cellIndexEUTRA EUTRA-CellIndex, + physCellIdRange EUTRA-PhysCellIdRange +} + +-- TAG-MEASOBJECTEUTRA-STOP +-- TAG-MEASOBJECTID-START + +MeasObjectId ::= INTEGER (1..maxNrofObjectId) + +-- TAG-MEASOBJECTID-STOP +-- TAG-MEASOBJECTNR-START + +MeasObjectNR ::= SEQUENCE { + ssbFrequency ARFCN-ValueNR OPTIONAL, -- Cond SSBorAssociatedSSB + ssbSubcarrierSpacing SubcarrierSpacing OPTIONAL, -- Cond SSBorAssociatedSSB + smtc1 SSB-MTC OPTIONAL, -- Cond SSBorAssociatedSSB + smtc2 SSB-MTC2 OPTIONAL, -- Cond IntraFreqConnected + refFreqCSI-RS ARFCN-ValueNR OPTIONAL, -- Cond CSI-RS + referenceSignalConfig ReferenceSignalConfig, + absThreshSS-BlocksConsolidation ThresholdNR OPTIONAL, -- Need R + absThreshCSI-RS-Consolidation ThresholdNR OPTIONAL, -- Need R + nrofSS-BlocksToAverage INTEGER (2..maxNrofSS-BlocksToAverage) OPTIONAL, -- Need R + nrofCSI-RS-ResourcesToAverage INTEGER (2..maxNrofCSI-RS-ResourcesToAverage) OPTIONAL, -- Need R + quantityConfigIndex INTEGER (1..maxNrofQuantityConfig), + offsetMO Q-OffsetRangeList, + cellsToRemoveList PCI-List OPTIONAL, -- Need N + cellsToAddModList CellsToAddModList OPTIONAL, -- Need N + blackCellsToRemoveList PCI-RangeIndexList OPTIONAL, -- Need N + blackCellsToAddModList SEQUENCE (SIZE (1..maxNrofPCI-Ranges)) OF PCI-RangeElement OPTIONAL, -- Need N + whiteCellsToRemoveList PCI-RangeIndexList OPTIONAL, -- Need N + whiteCellsToAddModList SEQUENCE (SIZE (1..maxNrofPCI-Ranges)) OF PCI-RangeElement OPTIONAL, -- Need N + ..., + [[ + freqBandIndicatorNR FreqBandIndicatorNR OPTIONAL, -- Need R + measCycleSCell ENUMERATED {sf160, sf256, sf320, sf512, sf640, sf1024, sf1280} OPTIONAL -- Need R + ]], + [[ + smtc3list-r16 SSB-MTC3List-r16 OPTIONAL, -- Need R + rmtc-Config-r16 SetupRelease {RMTC-Config-r16} OPTIONAL, -- Need M + t312-r16 SetupRelease { T312-r16 } OPTIONAL -- Need M + ]] +} + +SSB-MTC3List-r16::= SEQUENCE (SIZE(1..4)) OF SSB-MTC3-r16 + +T312-r16 ::= ENUMERATED { ms0, ms50, ms100, ms200, ms300, ms400, ms500, ms1000} + +ReferenceSignalConfig::= SEQUENCE { + ssb-ConfigMobility SSB-ConfigMobility OPTIONAL, -- Need M + csi-rs-ResourceConfigMobility SetupRelease { CSI-RS-ResourceConfigMobility } OPTIONAL -- Need M +} + +SSB-ConfigMobility::= SEQUENCE { + ssb-ToMeasure SetupRelease { SSB-ToMeasure } OPTIONAL, -- Need M + deriveSSB-IndexFromCell BOOLEAN, + ss-RSSI-Measurement SS-RSSI-Measurement OPTIONAL, -- Need M + ..., + [[ + ssb-PositionQCL-Common-r16 SSB-PositionQCL-Relation-r16 OPTIONAL, -- Cond SharedSpectrum + ssb-PositionQCL-CellsToAddModList-r16 SSB-PositionQCL-CellsToAddModList-r16 OPTIONAL, -- Need N + ssb-PositionQCL-CellsToRemoveList-r16 PCI-List OPTIONAL -- Need N + ]] +} + +Q-OffsetRangeList ::= SEQUENCE { + rsrpOffsetSSB Q-OffsetRange DEFAULT dB0, + rsrqOffsetSSB Q-OffsetRange DEFAULT dB0, + sinrOffsetSSB Q-OffsetRange DEFAULT dB0, + rsrpOffsetCSI-RS Q-OffsetRange DEFAULT dB0, + rsrqOffsetCSI-RS Q-OffsetRange DEFAULT dB0, + sinrOffsetCSI-RS Q-OffsetRange DEFAULT dB0 +} + + +ThresholdNR ::= SEQUENCE{ + thresholdRSRP RSRP-Range OPTIONAL, -- Need R + thresholdRSRQ RSRQ-Range OPTIONAL, -- Need R + thresholdSINR SINR-Range OPTIONAL -- Need R +} + +CellsToAddModList ::= SEQUENCE (SIZE (1..maxNrofCellMeas)) OF CellsToAddMod + +CellsToAddMod ::= SEQUENCE { + physCellId PhysCellId, + cellIndividualOffset Q-OffsetRangeList +} + +RMTC-Config-r16 ::= SEQUENCE { + rmtc-Periodicity-r16 ENUMERATED {ms40, ms80, ms160, ms320, ms640}, + rmtc-SubframeOffset-r16 INTEGER(0..639) OPTIONAL, -- Need M + measDurationSymbols-r16 ENUMERATED {sym1, sym14or12, sym28or24, sym42or36, sym70or60}, + rmtc-Frequency-r16 ARFCN-ValueNR, + ref-SCS-CP-r16 ENUMERATED {kHz15, kHz30, kHz60-NCP, kHz60-ECP}, + ... +} + +SSB-PositionQCL-CellsToAddModList-r16 ::= SEQUENCE (SIZE (1..maxNrofCellMeas)) OF SSB-PositionQCL-CellsToAddMod-r16 + +SSB-PositionQCL-CellsToAddMod-r16 ::= SEQUENCE { + physCellId-r16 PhysCellId, + ssb-PositionQCL-r16 SSB-PositionQCL-Relation-r16 +} + +-- TAG-MEASOBJECTNR-STOP +-- TAG-MEASOBJECTNR-SL-START + +MeasObjectNR-SL-r16 ::= SEQUENCE { + tx-PoolMeasToRemoveList-r16 Tx-PoolMeasList-r16 OPTIONAL, -- Need N + tx-PoolMeasToAddModList-r16 Tx-PoolMeasList-r16 OPTIONAL -- Need N +} + +Tx-PoolMeasList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-PoolToMeasureNR-r16)) OF SL-ResourcePoolID-r16 + +-- TAG-MEASOBJECTNR-SL-STOP +-- TAG-MEASOBJECTTOADDMODLIST-START + +MeasObjectToAddModList ::= SEQUENCE (SIZE (1..maxNrofObjectId)) OF MeasObjectToAddMod + +MeasObjectToAddMod ::= SEQUENCE { + measObjectId MeasObjectId, + measObject CHOICE { + measObjectNR MeasObjectNR, + ..., + measObjectEUTRA MeasObjectEUTRA, + measObjectUTRA-FDD-r16 MeasObjectUTRA-FDD-r16, + measObjectNR-SL-r16 MeasObjectNR-SL-r16, + measObjectCLI-r16 MeasObjectCLI-r16 + } +} + +-- TAG-MEASOBJECTTOADDMODLIST-STOP +-- TAG-MEASOBJECTUTRA-FDD-START + +MeasObjectUTRA-FDD-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueUTRA-FDD-r16, + utra-FDD-Q-OffsetRange-r16 UTRA-FDD-Q-OffsetRange-r16 OPTIONAL, -- Need R + cellsToRemoveList-r16 UTRA-FDD-CellIndexList-r16 OPTIONAL, -- Need N + cellsToAddModList-r16 CellsToAddModListUTRA-FDD-r16 OPTIONAL, -- Need N + ... +} + +CellsToAddModListUTRA-FDD-r16 ::= SEQUENCE (SIZE (1..maxCellMeasUTRA-FDD-r16)) OF CellsToAddModUTRA-FDD-r16 + +CellsToAddModUTRA-FDD-r16 ::= SEQUENCE { + cellIndexUTRA-FDD-r16 UTRA-FDD-CellIndex-r16, + physCellId-r16 PhysCellIdUTRA-FDD-r16 +} + +UTRA-FDD-CellIndexList-r16 ::= SEQUENCE (SIZE (1..maxCellMeasUTRA-FDD-r16)) OF UTRA-FDD-CellIndex-r16 + +UTRA-FDD-CellIndex-r16 ::= INTEGER (1..maxCellMeasUTRA-FDD-r16) + +-- TAG-MEASOBJECTUTRA-FDD-STOP +-- TAG-MEASRESULTCELLLISTSFTD-NR-START + +MeasResultCellListSFTD-NR ::= SEQUENCE (SIZE (1..maxCellSFTD)) OF MeasResultCellSFTD-NR + +MeasResultCellSFTD-NR ::= SEQUENCE { + physCellId PhysCellId, + sfn-OffsetResult INTEGER (0..1023), + frameBoundaryOffsetResult INTEGER (-30720..30719), + rsrp-Result RSRP-Range OPTIONAL +} + +-- TAG-MEASRESULTCELLLISTSFTD-NR-STOP +-- TAG-MEASRESULTCELLLISTSFTD-EUTRA-START + +MeasResultCellListSFTD-EUTRA ::= SEQUENCE (SIZE (1..maxCellSFTD)) OF MeasResultSFTD-EUTRA + +MeasResultSFTD-EUTRA ::= SEQUENCE { + eutra-PhysCellId EUTRA-PhysCellId, + sfn-OffsetResult INTEGER (0..1023), + frameBoundaryOffsetResult INTEGER (-30720..30719), + rsrp-Result RSRP-Range OPTIONAL +} + +-- TAG-MEASRESULTCELLLISTSFTD-EUTRA-STOP +-- TAG-MEASRESULTS-START + +MeasResults ::= SEQUENCE { + measId MeasId, + measResultServingMOList MeasResultServMOList, + measResultNeighCells CHOICE { + measResultListNR MeasResultListNR, + ..., + measResultListEUTRA MeasResultListEUTRA, + measResultListUTRA-FDD-r16 MeasResultListUTRA-FDD-r16 + } OPTIONAL, + ..., + [[ + measResultServFreqListEUTRA-SCG MeasResultServFreqListEUTRA-SCG OPTIONAL, + measResultServFreqListNR-SCG MeasResultServFreqListNR-SCG OPTIONAL, + measResultSFTD-EUTRA MeasResultSFTD-EUTRA OPTIONAL, + measResultSFTD-NR MeasResultCellSFTD-NR OPTIONAL + ]], + [[ + measResultCellListSFTD-NR MeasResultCellListSFTD-NR OPTIONAL + ]], + [[ + measResultForRSSI-r16 MeasResultForRSSI-r16 OPTIONAL, + locationInfo-r16 LocationInfo-r16 OPTIONAL, + ul-PDCP-DelayValueResultList-r16 UL-PDCP-DelayValueResultList-r16 OPTIONAL, + measResultsSL-r16 MeasResultsSL-r16 OPTIONAL, + measResultCLI-r16 MeasResultCLI-r16 OPTIONAL + ]] + + +} + +MeasResultServMOList ::= SEQUENCE (SIZE (1..maxNrofServingCells)) OF MeasResultServMO + +MeasResultServMO ::= SEQUENCE { + servCellId ServCellIndex, + measResultServingCell MeasResultNR, + measResultBestNeighCell MeasResultNR OPTIONAL, + ... +} + +MeasResultListNR ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultNR + +MeasResultNR ::= SEQUENCE { + physCellId PhysCellId OPTIONAL, + measResult SEQUENCE { + cellResults SEQUENCE{ + resultsSSB-Cell MeasQuantityResults OPTIONAL, + resultsCSI-RS-Cell MeasQuantityResults OPTIONAL + }, + rsIndexResults SEQUENCE{ + resultsSSB-Indexes ResultsPerSSB-IndexList OPTIONAL, + resultsCSI-RS-Indexes ResultsPerCSI-RS-IndexList OPTIONAL + } OPTIONAL + }, + ..., + [[ + cgi-Info CGI-InfoNR OPTIONAL + ]] +} + +MeasResultListEUTRA ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultEUTRA + +MeasResultEUTRA ::= SEQUENCE { + eutra-PhysCellId PhysCellId, + measResult MeasQuantityResultsEUTRA, + + cgi-Info CGI-InfoEUTRA OPTIONAL, + ... +} + +MultiBandInfoListEUTRA ::= SEQUENCE (SIZE (1..maxMultiBands)) OF FreqBandIndicatorEUTRA + +MeasQuantityResults ::= SEQUENCE { + rsrp RSRP-Range OPTIONAL, + rsrq RSRQ-Range OPTIONAL, + sinr SINR-Range OPTIONAL +} + +MeasQuantityResultsEUTRA ::= SEQUENCE { + rsrp RSRP-RangeEUTRA OPTIONAL, + rsrq RSRQ-RangeEUTRA OPTIONAL, + sinr SINR-RangeEUTRA OPTIONAL +} + +ResultsPerSSB-IndexList::= SEQUENCE (SIZE (1..maxNrofIndexesToReport2)) OF ResultsPerSSB-Index + +ResultsPerSSB-Index ::= SEQUENCE { + ssb-Index SSB-Index, + ssb-Results MeasQuantityResults OPTIONAL +} + +ResultsPerCSI-RS-IndexList::= SEQUENCE (SIZE (1..maxNrofIndexesToReport2)) OF ResultsPerCSI-RS-Index + +ResultsPerCSI-RS-Index ::= SEQUENCE { + csi-RS-Index CSI-RS-Index, + csi-RS-Results MeasQuantityResults OPTIONAL +} +MeasResultServFreqListEUTRA-SCG ::= SEQUENCE (SIZE (1..maxNrofServingCellsEUTRA)) OF MeasResult2EUTRA + +MeasResultServFreqListNR-SCG ::= SEQUENCE (SIZE (1..maxNrofServingCells)) OF MeasResult2NR + +MeasResultListUTRA-FDD-r16 ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultUTRA-FDD-r16 + +MeasResultUTRA-FDD-r16 ::= SEQUENCE { + physCellId-r16 PhysCellIdUTRA-FDD-r16, + measResult-r16 SEQUENCE { + utra-FDD-RSCP-r16 INTEGER (-5..91) OPTIONAL, + utra-FDD-EcN0-r16 INTEGER (0..49) OPTIONAL + } +} + +MeasResultForRSSI-r16 ::= SEQUENCE { + rssi-Result-r16 RSSI-Range-r16, + channelOccupancy-r16 INTEGER (0..100) +} + +MeasResultCLI-r16 ::= SEQUENCE { + measResultListSRS-RSRP-r16 MeasResultListSRS-RSRP-r16 OPTIONAL, + measResultListCLI-RSSI-r16 MeasResultListCLI-RSSI-r16 OPTIONAL +} + +MeasResultListSRS-RSRP-r16 ::= SEQUENCE (SIZE (1.. maxCLI-Report-r16)) OF MeasResultSRS-RSRP-r16 + +MeasResultSRS-RSRP-r16 ::= SEQUENCE { + srs-ResourceId-r16 SRS-ResourceId, + srs-RSRP-Result-r16 SRS-RSRP-Range-r16 +} + +MeasResultListCLI-RSSI-r16 ::= SEQUENCE (SIZE (1.. maxCLI-Report-r16)) OF MeasResultCLI-RSSI-r16 + +MeasResultCLI-RSSI-r16 ::= SEQUENCE { + rssi-ResourceId-r16 RSSI-ResourceId-r16, + cli-RSSI-Result-r16 CLI-RSSI-Range-r16 +} + +UL-PDCP-DelayValueResultList-r16 ::= SEQUENCE (SIZE (1..maxDRB)) OF UL-PDCP-DelayValueResult-r16 + +UL-PDCP-DelayValueResult-r16 ::= SEQUENCE { + drb-Id-r16 DRB-Identity, + averageDelay-r16 INTEGER (0..10000), + ... +} + +-- TAG-MEASRESULTS-STOP +-- TAG-MEASRESULT2EUTRA-START + +MeasResult2EUTRA ::= SEQUENCE { + carrierFreq ARFCN-ValueEUTRA, + measResultServingCell MeasResultEUTRA OPTIONAL, + measResultBestNeighCell MeasResultEUTRA OPTIONAL, + ... +} + +-- TAG-MEASRESULT2EUTRA-STOP +-- TAG-MEASRESULT2NR-START + +MeasResult2NR ::= SEQUENCE { + ssbFrequency ARFCN-ValueNR OPTIONAL, + refFreqCSI-RS ARFCN-ValueNR OPTIONAL, + measResultServingCell MeasResultNR OPTIONAL, + measResultNeighCellListNR MeasResultListNR OPTIONAL, + ... +} + +-- TAG-MEASRESULT2NR-STOP +-- TAG-MEASRESULTIDLEEUTRA-START + +MeasResultIdleEUTRA-r16 ::= SEQUENCE { + measResultsPerCarrierListIdleEUTRA-r16 SEQUENCE (SIZE (1.. maxFreqIdle-r16)) OF MeasResultsPerCarrierIdleEUTRA-r16, + ... +} + +MeasResultsPerCarrierIdleEUTRA-r16 ::= SEQUENCE { + carrierFreqEUTRA-r16 ARFCN-ValueEUTRA, + measResultsPerCellListIdleEUTRA-r16 SEQUENCE (SIZE (1..maxCellMeasIdle-r16)) OF MeasResultsPerCellIdleEUTRA-r16, + ... +} + +MeasResultsPerCellIdleEUTRA-r16 ::= SEQUENCE { + eutra-PhysCellId-r16 EUTRA-PhysCellId, + measIdleResultEUTRA-r16 SEQUENCE { + rsrp-ResultEUTRA-r16 RSRP-RangeEUTRA OPTIONAL, + rsrq-ResultEUTRA-r16 RSRQ-RangeEUTRA-r16 OPTIONAL + }, + ... +} + +-- TAG-MEASRESULTIDLEEUTRA-STOP +-- TAG-MEASRESULTIDLENR-START + +MeasResultIdleNR-r16 ::= SEQUENCE { + measResultServingCell-r16 SEQUENCE { + rsrp-Result-r16 RSRP-Range OPTIONAL, + rsrq-Result-r16 RSRQ-Range OPTIONAL, + resultsSSB-Indexes-r16 ResultsPerSSB-IndexList-r16 OPTIONAL + }, + measResultsPerCarrierListIdleNR-r16 SEQUENCE (SIZE (1.. maxFreqIdle-r16)) OF MeasResultsPerCarrierIdleNR-r16 OPTIONAL, + ... +} + +MeasResultsPerCarrierIdleNR-r16 ::= SEQUENCE { + carrierFreq-r16 ARFCN-ValueNR, + measResultsPerCellListIdleNR-r16 SEQUENCE (SIZE (1..maxCellMeasIdle-r16)) OF MeasResultsPerCellIdleNR-r16, + ... +} + +MeasResultsPerCellIdleNR-r16 ::= SEQUENCE { + physCellId-r16 PhysCellId, + measIdleResultNR-r16 SEQUENCE { + rsrp-Result-r16 RSRP-Range OPTIONAL, + rsrq-Result-r16 RSRQ-Range OPTIONAL, + resultsSSB-Indexes-r16 ResultsPerSSB-IndexList-r16 OPTIONAL + }, + ... +} + +ResultsPerSSB-IndexList-r16 ::= SEQUENCE (SIZE (1.. maxNrofIndexesToReport)) OF ResultsPerSSB-IndexIdle-r16 + +ResultsPerSSB-IndexIdle-r16 ::= SEQUENCE { + ssb-Index-r16 SSB-Index, + ssb-Results-r16 SEQUENCE { + ssb-RSRP-Result-r16 RSRP-Range OPTIONAL, + ssb-RSRQ-Result-r16 RSRQ-Range OPTIONAL + } OPTIONAL +} + +-- TAG-MEASRESULTIDLENR-STOP +-- TAG-MEASRESULTSCG-FAILURE-START + +MeasResultSCG-Failure ::= SEQUENCE { + measResultPerMOList MeasResultList2NR, + ..., + [[ + locationInfo-r16 LocationInfo-r16 OPTIONAL + ]] +} + +MeasResultList2NR ::= SEQUENCE (SIZE (1..maxFreq)) OF MeasResult2NR + +-- TAG-MEASRESULTSCG-FAILURE-STOP +-- TAG-MEASRESULTSSL-START + +MeasResultsSL-r16 ::= SEQUENCE { + measResultsListSL-r16 CHOICE { + measResultNR-SL-r16 MeasResultNR-SL-r16, + ... + }, + ... +} + +MeasResultNR-SL-r16 ::= SEQUENCE { + measResultListCBR-NR-r16 SEQUENCE (SIZE (1.. maxNrofSL-PoolToMeasureNR-r16)) OF MeasResultCBR-NR-r16, + ... +} + +MeasResultCBR-NR-r16 ::= SEQUENCE { + sl-poolReportIdentity-r16 SL-ResourcePoolID-r16, + sl-CBR-ResultsNR-r16 SL-CBR-r16, + ... +} + +-- TAG-MEASRESULTSSL-STOP +-- TAG-MEASTRIGGERQUANTITYEUTRA-START + +MeasTriggerQuantityEUTRA::= CHOICE { + rsrp RSRP-RangeEUTRA, + rsrq RSRQ-RangeEUTRA, + sinr SINR-RangeEUTRA +} + +RSRP-RangeEUTRA ::= INTEGER (0..97) + +RSRQ-RangeEUTRA ::= INTEGER (0..34) + +SINR-RangeEUTRA ::= INTEGER (0..127) + +-- TAG-MEASTRIGGERQUANTITYEUTRA-STOP +-- TAG-MOBILITYSTATEPARAMETERS-START + +MobilityStateParameters ::= SEQUENCE{ + t-Evaluation ENUMERATED { + s30, s60, s120, s180, s240, spare3, spare2, spare1}, + t-HystNormal ENUMERATED { + s30, s60, s120, s180, s240, spare3, spare2, spare1}, + n-CellChangeMedium INTEGER (1..16), + n-CellChangeHigh INTEGER (1..16) +} + +-- TAG-MOBILITYSTATEPARAMETERS-STOP +-- TAG-MSGACONFIGCOMMON-START + +MsgA-ConfigCommon-r16 ::= SEQUENCE { + rach-ConfigCommonTwoStepRA-r16 RACH-ConfigCommonTwoStepRA-r16, + msgA-PUSCH-Config-r16 MsgA-PUSCH-Config-r16 OPTIONAL --Cond InitialBWPConfig +} +-- TAG-MSGACONFIGCOMMON-STOP +-- TAG-MSGA-PUSCH-CONFIG-START + +MsgA-PUSCH-Config-r16 ::= SEQUENCE { + msgA-PUSCH-ResourceGroupA-r16 MsgA-PUSCH-Resource-r16 OPTIONAL, -- Cond InitialBWPConfig + msgA-PUSCH-ResourceGroupB-r16 MsgA-PUSCH-Resource-r16 OPTIONAL, -- Cond GroupBConfigured + msgA-TransformPrecoder-r16 ENUMERATED {enabled, disabled} OPTIONAL, -- Need R + msgA-DataScramblingIndex-r16 INTEGER (0..1023) OPTIONAL, -- Need S + msgA-DeltaPreamble-r16 INTEGER (-1..6) OPTIONAL -- Need R +} + +MsgA-PUSCH-Resource-r16 ::= SEQUENCE { + msgA-MCS-r16 INTEGER (0..15), + nrofSlotsMsgA-PUSCH-r16 INTEGER (1..4), + nrofMsgA-PO-PerSlot-r16 ENUMERATED {one, two, three, six}, + msgA-PUSCH-TimeDomainOffset-r16 INTEGER (1..32), + msgA-PUSCH-TimeDomainAllocation-r16 INTEGER (1..maxNrofUL-Allocations) OPTIONAL, -- Need S + startSymbolAndLengthMsgA-PO-r16 INTEGER (0..127) OPTIONAL, -- Need S + mappingTypeMsgA-PUSCH-r16 ENUMERATED {typeA, typeB} OPTIONAL, -- Need S + guardPeriodMsgA-PUSCH-r16 INTEGER (0..3) OPTIONAL, -- Need R + guardBandMsgA-PUSCH-r16 INTEGER (0..1), + frequencyStartMsgA-PUSCH-r16 INTEGER (0..maxNrofPhysicalResourceBlocks-1), + nrofPRBs-PerMsgA-PO-r16 INTEGER (1..32), + nrofMsgA-PO-FDM-r16 ENUMERATED {one, two, four, eight}, + msgA-IntraSlotFrequencyHopping-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + msgA-HoppingBits-r16 BIT STRING (SIZE(2)) OPTIONAL, -- Cond FreqHopConfigured + msgA-DMRS-Config-r16 MsgA-DMRS-Config-r16, + nrofDMRS-Sequences-r16 INTEGER (1..2), + msgA-Alpha-r16 ENUMERATED {alpha0, alpha04, alpha05, alpha06, + alpha07, alpha08, alpha09, alpha1} OPTIONAL, -- Need S + interlaceIndexFirstPO-MsgA-PUSCH-r16 INTEGER (1..10) OPTIONAL, -- Need R + nrofInterlacesPerMsgA-PO-r16 INTEGER (1..10) OPTIONAL, -- Need R + ... +} + +MsgA-DMRS-Config-r16 ::= SEQUENCE { + msgA-DMRS-AdditionalPosition-r16 ENUMERATED {pos0, pos1, pos3} OPTIONAL, -- Need S + msgA-MaxLength-r16 ENUMERATED {len2} OPTIONAL, -- Need S + msgA-PUSCH-DMRS-CDM-Group-r16 INTEGER (0..1) OPTIONAL, -- Need S + msgA-PUSCH-NrofPorts-r16 INTEGER (0..1) OPTIONAL, -- Need S + msgA-ScramblingID0-r16 INTEGER (0..65535) OPTIONAL, -- Need S + msgA-ScramblingID1-r16 INTEGER (0..65535) OPTIONAL -- Need S +} + +-- TAG-MSGA-PUSCH-CONFIG-STOP +-- TAG-MULTIFREQUENCYBANDLISTNR-START + +MultiFrequencyBandListNR ::= SEQUENCE (SIZE (1..maxNrofMultiBands)) OF FreqBandIndicatorNR + +-- TAG-MULTIFREQUENCYBANDLISTNR-STOP +-- TAG-MULTIFREQUENCYBANDLISTNR-SIB-START + +MultiFrequencyBandListNR-SIB ::= SEQUENCE (SIZE (1.. maxNrofMultiBands)) OF NR-MultiBandInfo + +NR-MultiBandInfo ::= SEQUENCE { + freqBandIndicatorNR FreqBandIndicatorNR OPTIONAL, -- Cond OptULNotSIB2 + nr-NS-PmaxList NR-NS-PmaxList OPTIONAL -- Need S +} + +-- TAG-MULTIFREQUENCYBANDLISTNR-SIB-STOP +-- TAG-NeedForGapsConfigNR-START + +NeedForGapsConfigNR-r16 ::= SEQUENCE { + requestedTargetBandFilterNR-r16 SEQUENCE (SIZE (1..maxBands)) OF FreqBandIndicatorNR OPTIONAL -- Need R +} + +-- TAG-NeedForGapsConfigNR-STOP +-- TAG-NeedForGapsInfoNR-START + +NeedForGapsInfoNR-r16 ::= SEQUENCE { + intraFreq-needForGap-r16 NeedForGapsIntraFreqlist-r16, + interFreq-needForGap-r16 NeedForGapsBandlistNR-r16 +} + +NeedForGapsIntraFreqlist-r16 ::= SEQUENCE (SIZE (1.. maxNrofServingCells)) OF NeedForGapsIntraFreq-r16 + +NeedForGapsBandlistNR-r16 ::= SEQUENCE (SIZE (1..maxBands)) OF NeedForGapsNR-r16 + +NeedForGapsIntraFreq-r16 ::= SEQUENCE { + servCellId-r16 ServCellIndex, + gapIndicationIntra-r16 ENUMERATED {gap, no-gap} +} + +NeedForGapsNR-r16 ::= SEQUENCE { + bandNR-r16 FreqBandIndicatorNR, + gapIndication-r16 ENUMERATED {gap, no-gap} +} + +-- TAG-NeedForGapsInfoNR-STOP +-- TAG-NEXTHOPCHAININGCOUNT-START + +NextHopChainingCount ::= INTEGER (0..7) + +-- TAG-NEXTHOPCHAININGCOUNT-STOP +-- TAG-NG-5G-S-TMSI-START + +NG-5G-S-TMSI ::= BIT STRING (SIZE (48)) + +-- TAG-NG-5G-S-TMSI-STOP +-- TAG-NPN-IDENTITY-START + +NPN-Identity-r16 ::= CHOICE { + pni-npn-r16 SEQUENCE { + plmn-Identity-r16 PLMN-Identity, + cag-IdentityList-r16 SEQUENCE (SIZE (1..maxNPN-r16)) OF CAG-IdentityInfo-r16 + }, + snpn-r16 SEQUENCE { + plmn-Identity-r16 PLMN-Identity, + nid-List-r16 SEQUENCE (SIZE (1..maxNPN-r16)) OF NID-r16 + } +} + +CAG-IdentityInfo-r16 ::= SEQUENCE { + cag-Identity-r16 BIT STRING (SIZE (32)), + manualCAGselectionAllowed-r16 ENUMERATED {true} OPTIONAL -- Need R +} + +NID-r16 ::= BIT STRING (SIZE (44)) + +-- TAG-NPN-IDENTITY-STOP +-- TAG-NPN-IDENTITYINFOLIST-START + +NPN-IdentityInfoList-r16 ::= SEQUENCE (SIZE (1..maxNPN-r16)) OF NPN-IdentityInfo-r16 + + +NPN-IdentityInfo-r16 ::= SEQUENCE { + npn-IdentityList-r16 SEQUENCE (SIZE (1..maxNPN-r16)) OF NPN-Identity-r16, + trackingAreaCode-r16 TrackingAreaCode, + ranac-r16 RAN-AreaCode OPTIONAL, -- Need R + cellIdentity-r16 CellIdentity, + cellReservedForOperatorUse-r16 ENUMERATED {reserved, notReserved}, + iab-Support-r16 ENUMERATED {true} OPTIONAL, -- Need S + ... +} + +-- TAG-NPN-IDENTITYINFOLIST-STOP +-- TAG-NR-NS-PMAXLIST-START + +NR-NS-PmaxList ::= SEQUENCE (SIZE (1..maxNR-NS-Pmax)) OF NR-NS-PmaxValue + +NR-NS-PmaxValue ::= SEQUENCE { + additionalPmax P-Max OPTIONAL, -- Need N + additionalSpectrumEmission AdditionalSpectrumEmission +} + +-- TAG-NR-NS-PMAXLIST-STOP +-- TAG-NZP-CSI-RS-RESOURCE-START + +NZP-CSI-RS-Resource ::= SEQUENCE { + nzp-CSI-RS-ResourceId NZP-CSI-RS-ResourceId, + resourceMapping CSI-RS-ResourceMapping, + powerControlOffset INTEGER (-8..15), + powerControlOffsetSS ENUMERATED{db-3, db0, db3, db6} OPTIONAL, -- Need R + scramblingID ScramblingId, + periodicityAndOffset CSI-ResourcePeriodicityAndOffset OPTIONAL, -- Cond PeriodicOrSemiPersistent + qcl-InfoPeriodicCSI-RS TCI-StateId OPTIONAL, -- Cond Periodic + ... +} + +-- TAG-NZP-CSI-RS-RESOURCE-STOP +-- TAG-NZP-CSI-RS-RESOURCEID-START + +NZP-CSI-RS-ResourceId ::= INTEGER (0..maxNrofNZP-CSI-RS-Resources-1) + +-- TAG-NZP-CSI-RS-RESOURCEID-STOP +-- TAG-NZP-CSI-RS-RESOURCESET-START +NZP-CSI-RS-ResourceSet ::= SEQUENCE { + nzp-CSI-ResourceSetId NZP-CSI-RS-ResourceSetId, + nzp-CSI-RS-Resources SEQUENCE (SIZE (1..maxNrofNZP-CSI-RS-ResourcesPerSet)) OF NZP-CSI-RS-ResourceId, + repetition ENUMERATED { on, off } OPTIONAL, -- Need S + aperiodicTriggeringOffset INTEGER(0..6) OPTIONAL, -- Need S + trs-Info ENUMERATED {true} OPTIONAL, -- Need R + ..., + [[ + aperiodicTriggeringOffset-r16 INTEGER(0..31) OPTIONAL -- Need S + ]] +} + +-- TAG-NZP-CSI-RS-RESOURCESET-STOP +-- TAG-NZP-CSI-RS-RESOURCESETID-START + +NZP-CSI-RS-ResourceSetId ::= INTEGER (0..maxNrofNZP-CSI-RS-ResourceSets-1) + +-- TAG-NZP-CSI-RS-RESOURCESETID-STOP +-- TAG-P-MAX-START + +P-Max ::= INTEGER (-30..33) + +-- TAG-P-MAX-STOP +-- TAG-PCI-LIST-START + +PCI-List ::= SEQUENCE (SIZE (1..maxNrofCellMeas)) OF PhysCellId + +-- TAG-PCI-LIST-STOP +-- TAG-PCI-RANGE-START + +PCI-Range ::= SEQUENCE { + start PhysCellId, + range ENUMERATED {n4, n8, n12, n16, n24, n32, n48, n64, n84, + n96, n128, n168, n252, n504, n1008,spare1} OPTIONAL -- Need S +} + +-- TAG-PCI-RANGE-STOP +-- TAG-PCI-RANGEELEMENT-START + +PCI-RangeElement ::= SEQUENCE { + pci-RangeIndex PCI-RangeIndex, + pci-Range PCI-Range +} + +-- TAG-PCI-RANGEELEMENT-STOP +-- TAG-PCI-RANGEINDEX-START + +PCI-RangeIndex ::= INTEGER (1..maxNrofPCI-Ranges) + +-- TAG-PCI-RANGEINDEX-STOP +-- TAG-PCI-RANGEINDEXLIST-START + +PCI-RangeIndexList ::= SEQUENCE (SIZE (1..maxNrofPCI-Ranges)) OF PCI-RangeIndex + +-- TAG-PCI-RANGEINDEXLIST-STOP +-- TAG-PDCCH-CONFIG-START + +PDCCH-Config ::= SEQUENCE { + controlResourceSetToAddModList SEQUENCE(SIZE (1..3)) OF ControlResourceSet OPTIONAL, -- Need N + controlResourceSetToReleaseList SEQUENCE(SIZE (1..3)) OF ControlResourceSetId OPTIONAL, -- Need N + searchSpacesToAddModList SEQUENCE(SIZE (1..10)) OF SearchSpace OPTIONAL, -- Need N + searchSpacesToReleaseList SEQUENCE(SIZE (1..10)) OF SearchSpaceId OPTIONAL, -- Need N + downlinkPreemption SetupRelease { DownlinkPreemption } OPTIONAL, -- Need M + tpc-PUSCH SetupRelease { PUSCH-TPC-CommandConfig } OPTIONAL, -- Need M + tpc-PUCCH SetupRelease { PUCCH-TPC-CommandConfig } OPTIONAL, -- Need M + tpc-SRS SetupRelease { SRS-TPC-CommandConfig} OPTIONAL, -- Need M + ..., + [[ + controlResourceSetToAddModListSizeExt-v1610 SEQUENCE (SIZE (1..2)) OF ControlResourceSet OPTIONAL, -- Need N + controlResourceSetToReleaseListSizeExt-r16 SEQUENCE (SIZE (1..5)) OF ControlResourceSetId-r16 OPTIONAL, -- Need N + searchSpacesToAddModListExt-r16 SEQUENCE(SIZE (1..10)) OF SearchSpaceExt-r16 OPTIONAL, -- Need N + uplinkCancellation-r16 SetupRelease { UplinkCancellation-r16 } OPTIONAL, -- Need M + monitoringCapabilityConfig-r16 ENUMERATED { r15monitoringcapability,r16monitoringcapability } OPTIONAL, -- Need M + searchSpaceSwitchConfig-r16 SearchSpaceSwitchConfig-r16 OPTIONAL -- Need R + ]] +} + +SearchSpaceSwitchConfig-r16 ::= SEQUENCE { + cellGroupsForSwitchList-r16 SEQUENCE(SIZE (1..4)) OF CellGroupForSwitch-r16 OPTIONAL, -- Need R + searchSpaceSwitchDelay-r16 INTEGER (10..52) OPTIONAL -- Need R +} + +CellGroupForSwitch-r16 ::= SEQUENCE(SIZE (1..16)) OF ServCellIndex + +-- TAG-PDCCH-CONFIG-STOP +-- TAG-PDCCH-CONFIGCOMMON-START + +PDCCH-ConfigCommon ::= SEQUENCE { + controlResourceSetZero ControlResourceSetZero OPTIONAL, -- Cond InitialBWP-Only + commonControlResourceSet ControlResourceSet OPTIONAL, -- Need R + searchSpaceZero SearchSpaceZero OPTIONAL, -- Cond InitialBWP-Only + commonSearchSpaceList SEQUENCE (SIZE(1..4)) OF SearchSpace OPTIONAL, -- Need R + searchSpaceSIB1 SearchSpaceId OPTIONAL, -- Need S + searchSpaceOtherSystemInformation SearchSpaceId OPTIONAL, -- Need S + pagingSearchSpace SearchSpaceId OPTIONAL, -- Need S + ra-SearchSpace SearchSpaceId OPTIONAL, -- Need S + ..., + [[ + firstPDCCH-MonitoringOccasionOfPO CHOICE { + sCS15KHZoneT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..139), + sCS30KHZoneT-SCS15KHZhalfT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..279), + sCS60KHZoneT-SCS30KHZhalfT-SCS15KHZquarterT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..559), + sCS120KHZoneT-SCS60KHZhalfT-SCS30KHZquarterT-SCS15KHZoneEighthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..1119), + sCS120KHZhalfT-SCS60KHZquarterT-SCS30KHZoneEighthT-SCS15KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..2239), + sCS120KHZquarterT-SCS60KHZoneEighthT-SCS30KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..4479), + sCS120KHZoneEighthT-SCS60KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..8959), + sCS120KHZoneSixteenthT SEQUENCE (SIZE (1..maxPO-perPF)) OF INTEGER (0..17919) + } OPTIONAL -- Cond OtherBWP + ]], + [[ + commonSearchSpaceListExt-r16 SEQUENCE (SIZE(1..4)) OF SearchSpaceExt-r16 OPTIONAL -- Need R + ]] +} + +-- TAG-PDCCH-CONFIGCOMMON-STOP +-- TAG-PDCCH-CONFIGSIB1-START + +PDCCH-ConfigSIB1 ::= SEQUENCE { + controlResourceSetZero ControlResourceSetZero, + searchSpaceZero SearchSpaceZero +} + +-- TAG-PDCCH-CONFIGSIB1-STOP +-- TAG-PDCCH-SERVINGCELLCONFIG-START + +PDCCH-ServingCellConfig ::= SEQUENCE { + slotFormatIndicator SetupRelease { SlotFormatIndicator } OPTIONAL, -- Need M + ..., + [[ + availabilityIndicator-r16 SetupRelease {AvailabilityIndicator-r16} OPTIONAL, -- Need M + searchSpaceSwitchTimer-r16 INTEGER (1..80) OPTIONAL -- Need R + ]] +} + +-- TAG-PDCCH-SERVINGCELLCONFIG-STOP +-- TAG-PDCP-CONFIG-START + +PDCP-Config ::= SEQUENCE { + drb SEQUENCE { + discardTimer ENUMERATED {ms10, ms20, ms30, ms40, ms50, ms60, ms75, ms100, ms150, ms200, + ms250, ms300, ms500, ms750, ms1500, infinity} OPTIONAL, -- Cond Setup + pdcp-SN-SizeUL ENUMERATED {len12bits, len18bits} OPTIONAL, -- Cond Setup2 + pdcp-SN-SizeDL ENUMERATED {len12bits, len18bits} OPTIONAL, -- Cond Setup2 + headerCompression CHOICE { + notUsed NULL, + rohc SEQUENCE { + maxCID INTEGER (1..16383) DEFAULT 15, + profiles SEQUENCE { + profile0x0001 BOOLEAN, + profile0x0002 BOOLEAN, + profile0x0003 BOOLEAN, + profile0x0004 BOOLEAN, + profile0x0006 BOOLEAN, + profile0x0101 BOOLEAN, + profile0x0102 BOOLEAN, + profile0x0103 BOOLEAN, + profile0x0104 BOOLEAN + }, + drb-ContinueROHC ENUMERATED { true } OPTIONAL -- Need N + }, + uplinkOnlyROHC SEQUENCE { + maxCID INTEGER (1..16383) DEFAULT 15, + profiles SEQUENCE { + profile0x0006 BOOLEAN + }, + drb-ContinueROHC ENUMERATED { true } OPTIONAL -- Need N + }, + ... + }, + integrityProtection ENUMERATED { enabled } OPTIONAL, -- Cond ConnectedTo5GC1 + statusReportRequired ENUMERATED { true } OPTIONAL, -- Cond Rlc-AM-UM + outOfOrderDelivery ENUMERATED { true } OPTIONAL -- Need R + } OPTIONAL, -- Cond DRB + moreThanOneRLC SEQUENCE { + primaryPath SEQUENCE { + cellGroup CellGroupId OPTIONAL, -- Need R + logicalChannel LogicalChannelIdentity OPTIONAL -- Need R + }, + ul-DataSplitThreshold UL-DataSplitThreshold OPTIONAL, -- Cond SplitBearer + pdcp-Duplication BOOLEAN OPTIONAL -- Need R + } OPTIONAL, -- Cond MoreThanOneRLC + + t-Reordering ENUMERATED { + ms0, ms1, ms2, ms4, ms5, ms8, ms10, ms15, ms20, ms30, ms40, + ms50, ms60, ms80, ms100, ms120, ms140, ms160, ms180, ms200, ms220, + ms240, ms260, ms280, ms300, ms500, ms750, ms1000, ms1250, + ms1500, ms1750, ms2000, ms2250, ms2500, ms2750, + ms3000, spare28, spare27, spare26, spare25, spare24, + spare23, spare22, spare21, spare20, + spare19, spare18, spare17, spare16, spare15, spare14, + spare13, spare12, spare11, spare10, spare09, + spare08, spare07, spare06, spare05, spare04, spare03, + spare02, spare01 } OPTIONAL, -- Need S + ..., + [[ + cipheringDisabled ENUMERATED {true} OPTIONAL -- Cond ConnectedTo5GC + ]], + [[ + discardTimerExt-r16 SetupRelease { DiscardTimerExt-r16 } OPTIONAL, -- Cond DRB2 + moreThanTwoRLC-DRB-r16 SEQUENCE { + splitSecondaryPath-r16 LogicalChannelIdentity OPTIONAL, -- Cond SplitBearer2 + duplicationState-r16 SEQUENCE (SIZE (3)) OF BOOLEAN OPTIONAL -- Need S + } OPTIONAL, -- Cond MoreThanTwoRLC-DRB + ethernetHeaderCompression-r16 SetupRelease { EthernetHeaderCompression-r16 } OPTIONAL -- Need M + ]] +} + +EthernetHeaderCompression-r16 ::= SEQUENCE { + ehc-Common-r16 SEQUENCE { + ehc-CID-Length-r16 ENUMERATED { bits7, bits15 }, + ... + }, + ehc-Downlink-r16 SEQUENCE { + drb-ContinueEHC-DL-r16 ENUMERATED { true } OPTIONAL, -- Need R + ... + } OPTIONAL, -- Need M + ehc-Uplink-r16 SEQUENCE { + maxCID-EHC-UL-r16 INTEGER (1..32767), + drb-ContinueEHC-UL-r16 ENUMERATED { true } OPTIONAL, -- Need R + ... + } OPTIONAL -- Need M +} + +UL-DataSplitThreshold ::= ENUMERATED { + b0, b100, b200, b400, b800, b1600, b3200, b6400, b12800, b25600, b51200, b102400, b204800, + b409600, b819200, b1228800, b1638400, b2457600, b3276800, b4096000, b4915200, b5734400, + b6553600, infinity, spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1} + +DiscardTimerExt-r16 ::= ENUMERATED {ms0dot5, ms1, ms2, ms4, ms6, ms8, spare2, spare1} + +-- TAG-PDCP-CONFIG-STOP +-- TAG-PDSCH-CONFIG-START + +PDSCH-Config ::= SEQUENCE { + dataScramblingIdentityPDSCH INTEGER (0..1023) OPTIONAL, -- Need S + dmrs-DownlinkForPDSCH-MappingTypeA SetupRelease { DMRS-DownlinkConfig } OPTIONAL, -- Need M + dmrs-DownlinkForPDSCH-MappingTypeB SetupRelease { DMRS-DownlinkConfig } OPTIONAL, -- Need M + + tci-StatesToAddModList SEQUENCE (SIZE(1..maxNrofTCI-States)) OF TCI-State OPTIONAL, -- Need N + tci-StatesToReleaseList SEQUENCE (SIZE(1..maxNrofTCI-States)) OF TCI-StateId OPTIONAL, -- Need N + vrb-ToPRB-Interleaver ENUMERATED {n2, n4} OPTIONAL, -- Need S + resourceAllocation ENUMERATED { resourceAllocationType0, resourceAllocationType1, dynamicSwitch}, + pdsch-TimeDomainAllocationList SetupRelease { PDSCH-TimeDomainResourceAllocationList } OPTIONAL, -- Need M + pdsch-AggregationFactor ENUMERATED { n2, n4, n8 } OPTIONAL, -- Need S + rateMatchPatternToAddModList SEQUENCE (SIZE (1..maxNrofRateMatchPatterns)) OF RateMatchPattern OPTIONAL, -- Need N + rateMatchPatternToReleaseList SEQUENCE (SIZE (1..maxNrofRateMatchPatterns)) OF RateMatchPatternId OPTIONAL, -- Need N + rateMatchPatternGroup1 RateMatchPatternGroup OPTIONAL, -- Need R + rateMatchPatternGroup2 RateMatchPatternGroup OPTIONAL, -- Need R + + rbg-Size ENUMERATED {config1, config2}, + mcs-Table ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + maxNrofCodeWordsScheduledByDCI ENUMERATED {n1, n2} OPTIONAL, -- Need R + + prb-BundlingType CHOICE { + staticBundling SEQUENCE { + bundleSize ENUMERATED { n4, wideband } OPTIONAL -- Need S + }, + dynamicBundling SEQUENCE { + bundleSizeSet1 ENUMERATED { n4, wideband, n2-wideband, n4-wideband } OPTIONAL, -- Need S + bundleSizeSet2 ENUMERATED { n4, wideband } OPTIONAL -- Need S + } + }, + zp-CSI-RS-ResourceToAddModList SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-Resources)) OF ZP-CSI-RS-Resource + OPTIONAL, -- Need N + zp-CSI-RS-ResourceToReleaseList SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-Resources)) OF ZP-CSI-RS-ResourceId + OPTIONAL, -- Need N + aperiodic-ZP-CSI-RS-ResourceSetsToAddModList SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-ResourceSets)) OF ZP-CSI-RS-ResourceSet + OPTIONAL, -- Need N + aperiodic-ZP-CSI-RS-ResourceSetsToReleaseList SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-ResourceSets)) OF ZP-CSI-RS-ResourceSetId + OPTIONAL, -- Need N + sp-ZP-CSI-RS-ResourceSetsToAddModList SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-ResourceSets)) OF ZP-CSI-RS-ResourceSet + OPTIONAL, -- Need N + sp-ZP-CSI-RS-ResourceSetsToReleaseList SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-ResourceSets)) OF ZP-CSI-RS-ResourceSetId + OPTIONAL, -- Need N + p-ZP-CSI-RS-ResourceSet SetupRelease { ZP-CSI-RS-ResourceSet } + OPTIONAL, -- Need M + ..., + [[ + maxMIMO-Layers-r16 SetupRelease { MaxMIMO-LayersDL-r16 } OPTIONAL, -- Need M + minimumSchedulingOffsetK0-r16 SetupRelease { MinSchedulingOffsetK0-Values-r16 } OPTIONAL, -- Need M + + -- Start of the parameters for DCI format 1_2 introduced in V16.1.0 + antennaPortsFieldPresenceDCI-1-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + aperiodicZP-CSI-RS-ResourceSetsToAddModListDCI-1-2-r16 SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-ResourceSets)) OF ZP-CSI-RS-ResourceSet + OPTIONAL, -- Need N + aperiodicZP-CSI-RS-ResourceSetsToReleaseListDCI-1-2-r16 SEQUENCE (SIZE (1..maxNrofZP-CSI-RS-ResourceSets)) OF ZP-CSI-RS-ResourceSetId + OPTIONAL, -- Need N + dmrs-DownlinkForPDSCH-MappingTypeA-DCI-1-2-r16 SetupRelease { DMRS-DownlinkConfig } OPTIONAL, -- Need M + dmrs-DownlinkForPDSCH-MappingTypeB-DCI-1-2-r16 SetupRelease { DMRS-DownlinkConfig } OPTIONAL, -- Need M + dmrs-SequenceInitializationDCI-1-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + harq-ProcessNumberSizeDCI-1-2-r16 INTEGER (0..4) OPTIONAL, -- Need R + mcs-TableDCI-1-2-r16 ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + numberOfBitsForRV-DCI-1-2-r16 INTEGER (0..2) OPTIONAL, -- Need R + pdsch-TimeDomainAllocationListDCI-1-2-r16 SetupRelease { PDSCH-TimeDomainResourceAllocationList-r16 } + OPTIONAL, -- Need M + prb-BundlingTypeDCI-1-2-r16 CHOICE { + staticBundling-r16 SEQUENCE { + bundleSize-r16 ENUMERATED { n4, wideband } OPTIONAL -- Need S + }, + dynamicBundling-r16 SEQUENCE { + bundleSizeSet1-r16 ENUMERATED { n4, wideband, n2-wideband, n4-wideband } OPTIONAL, -- Need S + bundleSizeSet2-r16 ENUMERATED { n4, wideband } OPTIONAL -- Need S + } + } OPTIONAL, -- Need R + priorityIndicatorDCI-1-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + rateMatchPatternGroup1DCI-1-2-r16 RateMatchPatternGroup OPTIONAL, -- Need R + rateMatchPatternGroup2DCI-1-2-r16 RateMatchPatternGroup OPTIONAL, -- Need R + resourceAllocationType1GranularityDCI-1-2-r16 ENUMERATED {n2,n4,n8,n16} OPTIONAL, -- Need S + vrb-ToPRB-InterleaverDCI-1-2-r16 ENUMERATED {n2, n4} OPTIONAL, -- Need S + referenceOfSLIVDCI-1-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + resourceAllocationDCI-1-2-r16 ENUMERATED { resourceAllocationType0, resourceAllocationType1, dynamicSwitch} + OPTIONAL, -- Need M + -- End of the parameters for DCI format 1_2 introduced in V16.1.0 + + priorityIndicatorDCI-1-1-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + dataScramblingIdentityPDSCH2-r16 INTEGER (0..1023) OPTIONAL, -- Need R + pdsch-TimeDomainAllocationList-r16 SetupRelease { PDSCH-TimeDomainResourceAllocationList-r16 } OPTIONAL, -- Need M + repetitionSchemeConfig-r16 SetupRelease { RepetitionSchemeConfig-r16} OPTIONAL -- Need M + ]], + [[ + repetitionSchemeConfig-v1630 SetupRelease { RepetitionSchemeConfig-v1630} OPTIONAL -- Need M + ]] +} + +RateMatchPatternGroup ::= SEQUENCE (SIZE (1..maxNrofRateMatchPatternsPerGroup)) OF CHOICE { + cellLevel RateMatchPatternId, + bwpLevel RateMatchPatternId +} + +MinSchedulingOffsetK0-Values-r16 ::= SEQUENCE (SIZE (1..maxNrOfMinSchedulingOffsetValues-r16)) OF INTEGER (0..maxK0-SchedulingOffset-r16) + +MaxMIMO-LayersDL-r16 ::= INTEGER (1..8) + +-- TAG-PDSCH-CONFIG-STOP +-- TAG-PDSCH-CONFIGCOMMON-START + +PDSCH-ConfigCommon ::= SEQUENCE { + pdsch-TimeDomainAllocationList PDSCH-TimeDomainResourceAllocationList OPTIONAL, -- Need R + ... +} + +-- TAG-PDSCH-CONFIGCOMMON-STOP +-- TAG-PDSCH-SERVINGCELLCONFIG-START + +PDSCH-ServingCellConfig ::= SEQUENCE { + codeBlockGroupTransmission SetupRelease { PDSCH-CodeBlockGroupTransmission } OPTIONAL, -- Need M + xOverhead ENUMERATED { xOh6, xOh12, xOh18 } OPTIONAL, -- Need S + nrofHARQ-ProcessesForPDSCH ENUMERATED {n2, n4, n6, n10, n12, n16} OPTIONAL, -- Need S + pucch-Cell ServCellIndex OPTIONAL, -- Cond SCellAddOnly + ..., + [[ + maxMIMO-Layers INTEGER (1..8) OPTIONAL, -- Need M + processingType2Enabled BOOLEAN OPTIONAL -- Need M + ]], + [[ + pdsch-CodeBlockGroupTransmissionList-r16 SetupRelease { PDSCH-CodeBlockGroupTransmissionList-r16 } OPTIONAL -- Need M + ]] +} + +PDSCH-CodeBlockGroupTransmission ::= SEQUENCE { + maxCodeBlockGroupsPerTransportBlock ENUMERATED {n2, n4, n6, n8}, + codeBlockGroupFlushIndicator BOOLEAN, + ... +} + +PDSCH-CodeBlockGroupTransmissionList-r16 ::= SEQUENCE (SIZE (1..2)) OF PDSCH-CodeBlockGroupTransmission + +-- TAG-PDSCH-SERVINGCELLCONFIG-STOP +-- TAG-PDSCH-TIMEDOMAINRESOURCEALLOCATIONLIST-START + + +PDSCH-TimeDomainResourceAllocationList ::= SEQUENCE (SIZE(1..maxNrofDL-Allocations)) OF PDSCH-TimeDomainResourceAllocation + +PDSCH-TimeDomainResourceAllocation ::= SEQUENCE { + k0 INTEGER(0..32) OPTIONAL, -- Need S + mappingType ENUMERATED {typeA, typeB}, + startSymbolAndLength INTEGER (0..127) +} + +PDSCH-TimeDomainResourceAllocationList-r16 ::= SEQUENCE (SIZE(1..maxNrofDL-Allocations)) OF PDSCH-TimeDomainResourceAllocation-r16 + +PDSCH-TimeDomainResourceAllocation-r16 ::= SEQUENCE { + k0-r16 INTEGER(0..32) OPTIONAL, -- Need S + mappingType-r16 ENUMERATED {typeA, typeB}, + startSymbolAndLength-r16 INTEGER (0..127), + repetitionNumber-r16 ENUMERATED {n2, n3, n4, n5, n6, n7, n8, n16} OPTIONAL, -- Cond Formats1-0and1-1 + ... +} + +-- TAG-PDSCH-TIMEDOMAINRESOURCEALLOCATIONLIST-STOP +-- TAG-PHR-CONFIG-START + +PHR-Config ::= SEQUENCE { + phr-PeriodicTimer ENUMERATED {sf10, sf20, sf50, sf100, sf200,sf500, sf1000, infinity}, + phr-ProhibitTimer ENUMERATED {sf0, sf10, sf20, sf50, sf100,sf200, sf500, sf1000}, + phr-Tx-PowerFactorChange ENUMERATED {dB1, dB3, dB6, infinity}, + multiplePHR BOOLEAN, + dummy BOOLEAN, + phr-Type2OtherCell BOOLEAN, + phr-ModeOtherCG ENUMERATED {real, virtual}, + ..., + [[ + mpe-Reporting-FR2-r16 SetupRelease { MPE-Config-FR2-r16 } OPTIONAL -- Need M + ]] +} + +MPE-Config-FR2-r16 ::= SEQUENCE { + mpe-ProhibitTimer-r16 ENUMERATED {sf0, sf10, sf20, sf50, sf100, sf200, sf500, sf1000}, + mpe-Threshold-r16 ENUMERATED {dB3, dB6, dB9, dB12} +} + +-- TAG-PHR-CONFIG-STOP +-- TAG-PHYSCELLID-START + +PhysCellId ::= INTEGER (0..1007) + +-- TAG-PHYSCELLID-STOP +-- TAG-PHYSICALCELLGROUPCONFIG-START + +PhysicalCellGroupConfig ::= SEQUENCE { + harq-ACK-SpatialBundlingPUCCH ENUMERATED {true} OPTIONAL, -- Need S + harq-ACK-SpatialBundlingPUSCH ENUMERATED {true} OPTIONAL, -- Need S + p-NR-FR1 P-Max OPTIONAL, -- Need R + pdsch-HARQ-ACK-Codebook ENUMERATED {semiStatic, dynamic}, + tpc-SRS-RNTI RNTI-Value OPTIONAL, -- Need R + tpc-PUCCH-RNTI RNTI-Value OPTIONAL, -- Need R + tpc-PUSCH-RNTI RNTI-Value OPTIONAL, -- Need R + sp-CSI-RNTI RNTI-Value OPTIONAL, -- Need R + cs-RNTI SetupRelease { RNTI-Value } OPTIONAL, -- Need M + ..., + [[ + mcs-C-RNTI RNTI-Value OPTIONAL, -- Need R + p-UE-FR1 P-Max OPTIONAL -- Cond MCG-Only + ]], + [[ + xScale ENUMERATED {dB0, dB6, spare2, spare1} OPTIONAL -- Cond SCG-Only + ]], + [[ + pdcch-BlindDetection SetupRelease { PDCCH-BlindDetection } OPTIONAL -- Need M + ]], + [[ + dcp-Config-r16 SetupRelease { DCP-Config-r16 } OPTIONAL, -- Need M + harq-ACK-SpatialBundlingPUCCH-secondaryPUCCHgroup-r16 ENUMERATED {enabled, disabled} OPTIONAL, -- Cond twoPUCCHgroup + harq-ACK-SpatialBundlingPUSCH-secondaryPUCCHgroup-r16 ENUMERATED {enabled, disabled} OPTIONAL, -- Cond twoPUCCHgroup + pdsch-HARQ-ACK-Codebook-secondaryPUCCHgroup-r16 ENUMERATED {semiStatic, dynamic} OPTIONAL, -- Cond twoPUCCHgroup + p-NR-FR2-r16 P-Max OPTIONAL, -- Need R + p-UE-FR2-r16 P-Max OPTIONAL, -- Cond MCG-Only + nrdc-PCmode-FR1-r16 ENUMERATED {semi-static-mode1, semi-static-mode2, dynamic} OPTIONAL, -- Cond MCG-Only + nrdc-PCmode-FR2-r16 ENUMERATED {semi-static-mode1, semi-static-mode2, dynamic} OPTIONAL, -- Cond MCG-Only + pdsch-HARQ-ACK-Codebook-r16 ENUMERATED {enhancedDynamic} OPTIONAL, -- Need R + nfi-TotalDAI-Included-r16 ENUMERATED {true} OPTIONAL, -- Need R + ul-TotalDAI-Included-r16 ENUMERATED {true} OPTIONAL, -- Need R + pdsch-HARQ-ACK-OneShotFeedback-r16 ENUMERATED {true} OPTIONAL, -- Need R + pdsch-HARQ-ACK-OneShotFeedbackNDI-r16 ENUMERATED {true} OPTIONAL, -- Need R + pdsch-HARQ-ACK-OneShotFeedbackCBG-r16 ENUMERATED {true} OPTIONAL, -- Need R + downlinkAssignmentIndexDCI-0-2-r16 ENUMERATED { enabled } OPTIONAL, -- Need S + downlinkAssignmentIndexDCI-1-2-r16 ENUMERATED {n1, n2, n4} OPTIONAL, -- Need S + pdsch-HARQ-ACK-CodebookList-r16 SetupRelease {PDSCH-HARQ-ACK-CodebookList-r16} OPTIONAL, -- Need M + ackNackFeedbackMode-r16 ENUMERATED {joint, separate} OPTIONAL, -- Need R + pdcch-BlindDetectionCA-CombIndicator-r16 SetupRelease { PDCCH-BlindDetectionCA-CombIndicator-r16 } OPTIONAL, -- Need M + pdcch-BlindDetection2-r16 SetupRelease { PDCCH-BlindDetection2-r16 } OPTIONAL, -- Need M + pdcch-BlindDetection3-r16 SetupRelease { PDCCH-BlindDetection3-r16 } OPTIONAL, -- Need M + bdFactorR-r16 ENUMERATED {n1} OPTIONAL -- Need R + ]] +} + +PDCCH-BlindDetection ::= INTEGER (1..15) + +DCP-Config-r16 ::= SEQUENCE { + ps-RNTI-r16 RNTI-Value, + ps-Offset-r16 INTEGER (1..120), + sizeDCI-2-6-r16 INTEGER (1..maxDCI-2-6-Size-r16), + ps-PositionDCI-2-6-r16 INTEGER (0..maxDCI-2-6-Size-1-r16), + ps-WakeUp-r16 ENUMERATED {true} OPTIONAL, -- Need S + ps-TransmitPeriodicL1-RSRP-r16 ENUMERATED {true} OPTIONAL, -- Need S + ps-TransmitOtherPeriodicCSI-r16 ENUMERATED {true} OPTIONAL -- Need S +} + +PDSCH-HARQ-ACK-CodebookList-r16 ::= SEQUENCE (SIZE (1..2)) OF ENUMERATED {semiStatic, dynamic} + +PDCCH-BlindDetectionCA-CombIndicator-r16 ::= SEQUENCE { + pdcch-BlindDetectionCA1-r16 INTEGER (1..15), + pdcch-BlindDetectionCA2-r16 INTEGER (1..15) +} + +PDCCH-BlindDetection2-r16 ::= INTEGER (1..15) + +PDCCH-BlindDetection3-r16 ::= INTEGER (1..15) + +-- TAG-PHYSICALCELLGROUPCONFIG-STOP +-- TAG-PLMN-IDENTITY-START + +PLMN-Identity ::= SEQUENCE { + mcc MCC OPTIONAL, -- Cond MCC + mnc MNC +} + +MCC ::= SEQUENCE (SIZE (3)) OF MCC-MNC-Digit + +MNC ::= SEQUENCE (SIZE (2..3)) OF MCC-MNC-Digit + +MCC-MNC-Digit ::= INTEGER (0..9) + +-- TAG-PLMN-IDENTITY-STOP +-- TAG-PLMN-IDENTITYINFOLIST-START + +PLMN-IdentityInfoList ::= SEQUENCE (SIZE (1..maxPLMN)) OF PLMN-IdentityInfo + +PLMN-IdentityInfo ::= SEQUENCE { + plmn-IdentityList SEQUENCE (SIZE (1..maxPLMN)) OF PLMN-Identity, + trackingAreaCode TrackingAreaCode OPTIONAL, -- Need R + ranac RAN-AreaCode OPTIONAL, -- Need R + cellIdentity CellIdentity, + cellReservedForOperatorUse ENUMERATED {reserved, notReserved}, + ..., + [[ + iab-Support-r16 ENUMERATED {true} OPTIONAL -- Need S + ]] +} +-- TAG-PLMN-IDENTITYINFOLIST-STOP +-- TAG-PLMNIDENTITYLIST2-START + +PLMN-IdentityList2-r16 ::= SEQUENCE (SIZE (1..16)) OF PLMN-Identity + +-- TAG-PLMNIDENTITYLIST2-STOP +-- TAG-PRB-ID-START + +PRB-Id ::= INTEGER (0..maxNrofPhysicalResourceBlocks-1) + +-- TAG-PRB-ID-STOP +-- TAG-PTRS-DOWNLINKCONFIG-START + +PTRS-DownlinkConfig ::= SEQUENCE { + frequencyDensity SEQUENCE (SIZE (2)) OF INTEGER (1..276) OPTIONAL, -- Need S + timeDensity SEQUENCE (SIZE (3)) OF INTEGER (0..29) OPTIONAL, -- Need S + epre-Ratio INTEGER (0..3) OPTIONAL, -- Need S + resourceElementOffset ENUMERATED { offset01, offset10, offset11 } OPTIONAL, -- Need S + ..., + [[ + maxNrofPorts-r16 ENUMERATED {n1, n2} OPTIONAL -- Need R + ]] + +} + +-- TAG-PTRS-DOWNLINKCONFIG-STOP +-- TAG-PTRS-UPLINKCONFIG-START + +PTRS-UplinkConfig ::= SEQUENCE { + transformPrecoderDisabled SEQUENCE { + frequencyDensity SEQUENCE (SIZE (2)) OF INTEGER (1..276) OPTIONAL, -- Need S + timeDensity SEQUENCE (SIZE (3)) OF INTEGER (0..29) OPTIONAL, -- Need S + maxNrofPorts ENUMERATED {n1, n2}, + resourceElementOffset ENUMERATED {offset01, offset10, offset11 } OPTIONAL, -- Need S + ptrs-Power ENUMERATED {p00, p01, p10, p11} + } OPTIONAL, -- Need R + transformPrecoderEnabled SEQUENCE { + sampleDensity SEQUENCE (SIZE (5)) OF INTEGER (1..276), + timeDensityTransformPrecoding ENUMERATED {d2} OPTIONAL -- Need S + } OPTIONAL, -- Need R + ... +} + +-- TAG-PTRS-UPLINKCONFIG-STOP +-- TAG-PUCCH-CONFIG-START + +PUCCH-Config ::= SEQUENCE { + resourceSetToAddModList SEQUENCE (SIZE (1..maxNrofPUCCH-ResourceSets)) OF PUCCH-ResourceSet OPTIONAL, -- Need N + resourceSetToReleaseList SEQUENCE (SIZE (1..maxNrofPUCCH-ResourceSets)) OF PUCCH-ResourceSetId OPTIONAL, -- Need N + resourceToAddModList SEQUENCE (SIZE (1..maxNrofPUCCH-Resources)) OF PUCCH-Resource OPTIONAL, -- Need N + resourceToReleaseList SEQUENCE (SIZE (1..maxNrofPUCCH-Resources)) OF PUCCH-ResourceId OPTIONAL, -- Need N + format1 SetupRelease { PUCCH-FormatConfig } OPTIONAL, -- Need M + format2 SetupRelease { PUCCH-FormatConfig } OPTIONAL, -- Need M + format3 SetupRelease { PUCCH-FormatConfig } OPTIONAL, -- Need M + format4 SetupRelease { PUCCH-FormatConfig } OPTIONAL, -- Need M + schedulingRequestResourceToAddModList SEQUENCE (SIZE (1..maxNrofSR-Resources)) OF SchedulingRequestResourceConfig + OPTIONAL, -- Need N + schedulingRequestResourceToReleaseList SEQUENCE (SIZE (1..maxNrofSR-Resources)) OF SchedulingRequestResourceId + OPTIONAL, -- Need N + multi-CSI-PUCCH-ResourceList SEQUENCE (SIZE (1..2)) OF PUCCH-ResourceId OPTIONAL, -- Need M + dl-DataToUL-ACK SEQUENCE (SIZE (1..8)) OF INTEGER (0..15) OPTIONAL, -- Need M + spatialRelationInfoToAddModList SEQUENCE (SIZE (1..maxNrofSpatialRelationInfos)) OF PUCCH-SpatialRelationInfo + OPTIONAL, -- Need N + spatialRelationInfoToReleaseList SEQUENCE (SIZE (1..maxNrofSpatialRelationInfos)) OF PUCCH-SpatialRelationInfoId + OPTIONAL, -- Need N + pucch-PowerControl PUCCH-PowerControl OPTIONAL, -- Need M + ..., + [[ + resourceToAddModListExt-r16 SEQUENCE (SIZE (1..maxNrofPUCCH-Resources)) OF PUCCH-ResourceExt-r16 OPTIONAL, -- Need N + dl-DataToUL-ACK-r16 SetupRelease { DL-DataToUL-ACK-r16 } OPTIONAL, -- Need M + ul-AccessConfigListDCI-1-1-r16 SetupRelease { UL-AccessConfigListDCI-1-1-r16 } OPTIONAL, -- Need M + subslotLengthForPUCCH-r16 CHOICE { + normalCP-r16 ENUMERATED {n2,n7}, + extendedCP-r16 ENUMERATED {n2,n6} + } OPTIONAL, -- Need R + dl-DataToUL-ACK-DCI-1-2-r16 SetupRelease { DL-DataToUL-ACK-DCI-1-2-r16} OPTIONAL, -- Need M + numberOfBitsForPUCCH-ResourceIndicatorDCI-1-2-r16 INTEGER (0..3) OPTIONAL, -- Need R + dmrs-UplinkTransformPrecodingPUCCH-r16 ENUMERATED {enabled} OPTIONAL, -- Cond PI2-BPSK + spatialRelationInfoToAddModListSizeExt-v1610 SEQUENCE (SIZE (1..maxNrofSpatialRelationInfosDiff-r16)) OF PUCCH-SpatialRelationInfo + OPTIONAL, -- Need N + spatialRelationInfoToReleaseListSizeExt-v1610 SEQUENCE (SIZE (1..maxNrofSpatialRelationInfosDiff-r16)) OF PUCCH-SpatialRelationInfoId + OPTIONAL, -- Need N + spatialRelationInfoToAddModListExt-v1610 SEQUENCE (SIZE (1..maxNrofSpatialRelationInfos-r16)) OF PUCCH-SpatialRelationInfoExt-r16 + OPTIONAL, -- Need N + spatialRelationInfoToReleaseListExt-v1610 SEQUENCE (SIZE (1..maxNrofSpatialRelationInfos-r16)) OF + PUCCH-SpatialRelationInfoId-r16 OPTIONAL, -- Need N + resourceGroupToAddModList-r16 SEQUENCE (SIZE (1..maxNrofPUCCH-ResourceGroups-r16)) OF PUCCH-ResourceGroup-r16 + OPTIONAL, -- Need N + resourceGroupToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofPUCCH-ResourceGroups-r16)) OF PUCCH-ResourceGroupId-r16 + OPTIONAL, -- Need N + sps-PUCCH-AN-List-r16 SetupRelease { SPS-PUCCH-AN-List-r16 } OPTIONAL, -- Need M + schedulingRequestResourceToAddModListExt-v1610 SEQUENCE (SIZE (1..maxNrofSR-Resources)) OF SchedulingRequestResourceConfigExt-v1610 + OPTIONAL -- Need N + ]] +} + +PUCCH-FormatConfig ::= SEQUENCE { + interslotFrequencyHopping ENUMERATED {enabled} OPTIONAL, -- Need R + additionalDMRS ENUMERATED {true} OPTIONAL, -- Need R + maxCodeRate PUCCH-MaxCodeRate OPTIONAL, -- Need R + nrofSlots ENUMERATED {n2,n4,n8} OPTIONAL, -- Need S + pi2BPSK ENUMERATED {enabled} OPTIONAL, -- Need R + simultaneousHARQ-ACK-CSI ENUMERATED {true} OPTIONAL -- Need R +} + +PUCCH-MaxCodeRate ::= ENUMERATED {zeroDot08, zeroDot15, zeroDot25, zeroDot35, zeroDot45, zeroDot60, zeroDot80} + +-- A set with one or more PUCCH resources +PUCCH-ResourceSet ::= SEQUENCE { + pucch-ResourceSetId PUCCH-ResourceSetId, + resourceList SEQUENCE (SIZE (1..maxNrofPUCCH-ResourcesPerSet)) OF PUCCH-ResourceId, + maxPayloadSize INTEGER (4..256) OPTIONAL -- Need R +} + +PUCCH-ResourceSetId ::= INTEGER (0..maxNrofPUCCH-ResourceSets-1) + +PUCCH-Resource ::= SEQUENCE { + pucch-ResourceId PUCCH-ResourceId, + startingPRB PRB-Id, + intraSlotFrequencyHopping ENUMERATED { enabled } OPTIONAL, -- Need R + secondHopPRB PRB-Id OPTIONAL, -- Need R + format CHOICE { + format0 PUCCH-format0, + format1 PUCCH-format1, + format2 PUCCH-format2, + format3 PUCCH-format3, + format4 PUCCH-format4 + } +} + +PUCCH-ResourceExt-r16 ::= SEQUENCE { + interlaceAllocation-r16 SEQUENCE { + rb-SetIndex INTEGER (0..4), + interlace0 CHOICE { + scs15 INTEGER (0..9), + scs30 INTEGER (0..4) + } + } OPTIONAL, --Need R + formatExt-v1610 CHOICE { + interlace1-v1610 INTEGER (0..9), + occ-v1610 SEQUENCE { + occ-Length-v1610 ENUMERATED {n2,n4} OPTIONAL, -- Need M + occ-Index-v1610 ENUMERATED {n0,n1,n2,n3} OPTIONAL -- Need M + } + } OPTIONAL, -- Need R + ... +} + +PUCCH-ResourceId ::= INTEGER (0..maxNrofPUCCH-Resources-1) + + +PUCCH-format0 ::= SEQUENCE { + initialCyclicShift INTEGER(0..11), + nrofSymbols INTEGER (1..2), + startingSymbolIndex INTEGER(0..13) +} + +PUCCH-format1 ::= SEQUENCE { + initialCyclicShift INTEGER(0..11), + nrofSymbols INTEGER (4..14), + startingSymbolIndex INTEGER(0..10), + timeDomainOCC INTEGER(0..6) +} + +PUCCH-format2 ::= SEQUENCE { + nrofPRBs INTEGER (1..16), + nrofSymbols INTEGER (1..2), + startingSymbolIndex INTEGER(0..13) +} + +PUCCH-format3 ::= SEQUENCE { + nrofPRBs INTEGER (1..16), + nrofSymbols INTEGER (4..14), + startingSymbolIndex INTEGER(0..10) +} + +PUCCH-format4 ::= SEQUENCE { + nrofSymbols INTEGER (4..14), + occ-Length ENUMERATED {n2,n4}, + occ-Index ENUMERATED {n0,n1,n2,n3}, + startingSymbolIndex INTEGER(0..10) +} + +PUCCH-ResourceGroup-r16 ::= SEQUENCE { + pucch-ResourceGroupId-r16 PUCCH-ResourceGroupId-r16, + resourcePerGroupList-r16 SEQUENCE (SIZE (1..maxNrofPUCCH-ResourcesPerGroup-r16)) OF PUCCH-ResourceId +} + +PUCCH-ResourceGroupId-r16 ::= INTEGER (0..maxNrofPUCCH-ResourceGroups-1-r16) + +DL-DataToUL-ACK-r16 ::= SEQUENCE (SIZE (1..8)) OF INTEGER (-1..15) + +DL-DataToUL-ACK-DCI-1-2-r16 ::= SEQUENCE (SIZE (1..8)) OF INTEGER (0..15) + +UL-AccessConfigListDCI-1-1-r16 ::= SEQUENCE (SIZE (1..16)) OF INTEGER (0..15) + +-- TAG-PUCCH-CONFIG-STOP +-- TAG-PUCCH-CONFIGCOMMON-START + +PUCCH-ConfigCommon ::= SEQUENCE { + pucch-ResourceCommon INTEGER (0..15) OPTIONAL, -- Cond InitialBWP-Only + pucch-GroupHopping ENUMERATED { neither, enable, disable }, + hoppingId INTEGER (0..1023) OPTIONAL, -- Need R + p0-nominal INTEGER (-202..24) OPTIONAL, -- Need R + ... +} + +-- TAG-PUCCH-CONFIGCOMMON-STOP +-- TAG-PUCCH-CONFIGURATIONLIST-START + +PUCCH-ConfigurationList-r16 ::= SEQUENCE (SIZE (1..2)) OF PUCCH-Config + +-- TAG-PUCCH-CONFIGURATIONLIST-STOP +-- TAG-PUCCH-PATHLOSSREFERENCERS-ID-START + +PUCCH-PathlossReferenceRS-Id ::= INTEGER (0..maxNrofPUCCH-PathlossReferenceRSs-1) + +PUCCH-PathlossReferenceRS-Id-v1610 ::= INTEGER (maxNrofPUCCH-PathlossReferenceRSs..maxNrofPUCCH-PathlossReferenceRSs-1-r16) + +-- TAG-PUCCH-PATHLOSSREFERENCERS-ID-STOP +-- TAG-PUCCH-POWERCONTROL-START +PUCCH-PowerControl ::= SEQUENCE { + deltaF-PUCCH-f0 INTEGER (-16..15) OPTIONAL, -- Need R + deltaF-PUCCH-f1 INTEGER (-16..15) OPTIONAL, -- Need R + deltaF-PUCCH-f2 INTEGER (-16..15) OPTIONAL, -- Need R + deltaF-PUCCH-f3 INTEGER (-16..15) OPTIONAL, -- Need R + deltaF-PUCCH-f4 INTEGER (-16..15) OPTIONAL, -- Need R + p0-Set SEQUENCE (SIZE (1..maxNrofPUCCH-P0-PerSet)) OF P0-PUCCH OPTIONAL, -- Need M + pathlossReferenceRSs SEQUENCE (SIZE (1..maxNrofPUCCH-PathlossReferenceRSs)) OF PUCCH-PathlossReferenceRS + OPTIONAL, -- Need M + twoPUCCH-PC-AdjustmentStates ENUMERATED {twoStates} OPTIONAL, -- Need S + ..., + [[ + pathlossReferenceRSs-v1610 SetupRelease { PathlossReferenceRSs-v1610 } OPTIONAL -- Need M + ]] +} + +P0-PUCCH ::= SEQUENCE { + p0-PUCCH-Id P0-PUCCH-Id, + p0-PUCCH-Value INTEGER (-16..15) +} + +P0-PUCCH-Id ::= INTEGER (1..8) + +PathlossReferenceRSs-v1610 ::= SEQUENCE (SIZE (1..maxNrofPUCCH-PathlossReferenceRSsDiff-r16)) OF PUCCH-PathlossReferenceRS-r16 + +PUCCH-PathlossReferenceRS ::= SEQUENCE { + pucch-PathlossReferenceRS-Id PUCCH-PathlossReferenceRS-Id, + referenceSignal CHOICE { + ssb-Index SSB-Index, + csi-RS-Index NZP-CSI-RS-ResourceId + } +} + +PUCCH-PathlossReferenceRS-r16 ::= SEQUENCE { + pucch-PathlossReferenceRS-Id-r16 PUCCH-PathlossReferenceRS-Id-v1610, + referenceSignal-r16 CHOICE { + ssb-Index-r16 SSB-Index, + csi-RS-Index-r16 NZP-CSI-RS-ResourceId + } +} + +-- TAG-PUCCH-POWERCONTROL-STOP +-- TAG-PUCCH-SPATIALRELATIONINFO-START + +PUCCH-SpatialRelationInfo ::= SEQUENCE { + pucch-SpatialRelationInfoId PUCCH-SpatialRelationInfoId, + servingCellId ServCellIndex OPTIONAL, -- Need S + referenceSignal CHOICE { + ssb-Index SSB-Index, + csi-RS-Index NZP-CSI-RS-ResourceId, + srs PUCCH-SRS + }, + pucch-PathlossReferenceRS-Id PUCCH-PathlossReferenceRS-Id, + p0-PUCCH-Id P0-PUCCH-Id, + closedLoopIndex ENUMERATED { i0, i1 } +} + +PUCCH-SpatialRelationInfoExt-r16 ::= SEQUENCE { + pucch-SpatialRelationInfoId-v1610 PUCCH-SpatialRelationInfoId-v1610 OPTIONAL, -- Cond SetupOnly + pucch-PathlossReferenceRS-Id-v1610 PUCCH-PathlossReferenceRS-Id-v1610 OPTIONAL, --Need R + ... +} + +PUCCH-SRS ::= SEQUENCE { + resource SRS-ResourceId, + uplinkBWP BWP-Id +} +-- TAG-PUCCH-SPATIALRELATIONINFO-STOP +-- TAG-PUCCH-SPATIALRELATIONINFO-START + +PUCCH-SpatialRelationInfoId ::= INTEGER (1..maxNrofSpatialRelationInfos) + +PUCCH-SpatialRelationInfoId-r16 ::= INTEGER (1..maxNrofSpatialRelationInfos-r16) + +PUCCH-SpatialRelationInfoId-v1610::= INTEGER (maxNrofSpatialRelationInfos-plus-1..maxNrofSpatialRelationInfos-r16) + +-- TAG-PUCCH-SPATIALRELATIONINFO-STOP +-- TAG-PUCCH-TPC-COMMANDCONFIG-START + +PUCCH-TPC-CommandConfig ::= SEQUENCE { + tpc-IndexPCell INTEGER (1..15) OPTIONAL, -- Cond PDCCH-OfSpcell + tpc-IndexPUCCH-SCell INTEGER (1..15) OPTIONAL, -- Cond PDCCH-ofSpCellOrPUCCH-SCell + ... +} + +-- TAG-PUCCH-TPC-COMMANDCONFIG-STOP +-- TAG-PUSCH-CONFIG-START + +PUSCH-Config ::= SEQUENCE { + dataScramblingIdentityPUSCH INTEGER (0..1023) OPTIONAL, -- Need S + txConfig ENUMERATED {codebook, nonCodebook} OPTIONAL, -- Need S + dmrs-UplinkForPUSCH-MappingTypeA SetupRelease { DMRS-UplinkConfig } OPTIONAL, -- Need M + dmrs-UplinkForPUSCH-MappingTypeB SetupRelease { DMRS-UplinkConfig } OPTIONAL, -- Need M + pusch-PowerControl PUSCH-PowerControl OPTIONAL, -- Need M + frequencyHopping ENUMERATED {intraSlot, interSlot} OPTIONAL, -- Need S + frequencyHoppingOffsetLists SEQUENCE (SIZE (1..4)) OF INTEGER (1.. maxNrofPhysicalResourceBlocks-1) + OPTIONAL, -- Need M + resourceAllocation ENUMERATED { resourceAllocationType0, resourceAllocationType1, dynamicSwitch}, + pusch-TimeDomainAllocationList SetupRelease { PUSCH-TimeDomainResourceAllocationList } OPTIONAL, -- Need M + pusch-AggregationFactor ENUMERATED { n2, n4, n8 } OPTIONAL, -- Need S + mcs-Table ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + mcs-TableTransformPrecoder ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + transformPrecoder ENUMERATED {enabled, disabled} OPTIONAL, -- Need S + codebookSubset ENUMERATED {fullyAndPartialAndNonCoherent, partialAndNonCoherent,nonCoherent} + OPTIONAL, -- Cond codebookBased + maxRank INTEGER (1..4) OPTIONAL, -- Cond codebookBased + rbg-Size ENUMERATED { config2} OPTIONAL, -- Need S + uci-OnPUSCH SetupRelease { UCI-OnPUSCH} OPTIONAL, -- Need M + tp-pi2BPSK ENUMERATED {enabled} OPTIONAL, -- Need S + ..., + [[ + minimumSchedulingOffsetK2-r16 SetupRelease { MinSchedulingOffsetK2-Values-r16 } OPTIONAL, -- Need M + ul-AccessConfigListDCI-0-1-r16 SetupRelease { UL-AccessConfigListDCI-0-1-r16 } OPTIONAL, -- Need M + -- Start of the parameters for DCI format 0_2 introduced in V16.1.0 + harq-ProcessNumberSizeDCI-0-2-r16 INTEGER (0..4) OPTIONAL, -- Need R + dmrs-SequenceInitializationDCI-0-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + numberOfBitsForRV-DCI-0-2-r16 INTEGER (0..2) OPTIONAL, -- Need R + antennaPortsFieldPresenceDCI-0-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + dmrs-UplinkForPUSCH-MappingTypeA-DCI-0-2-r16 SetupRelease { DMRS-UplinkConfig } OPTIONAL, -- Need M + dmrs-UplinkForPUSCH-MappingTypeB-DCI-0-2-r16 SetupRelease { DMRS-UplinkConfig } OPTIONAL, -- Need M + frequencyHoppingDCI-0-2-r16 CHOICE { + pusch-RepTypeA ENUMERATED {intraSlot, interSlot}, + pusch-RepTypeB ENUMERATED {interRepetition, interSlot} + } OPTIONAL, -- Need S + frequencyHoppingOffsetListsDCI-0-2-r16 SetupRelease { FrequencyHoppingOffsetListsDCI-0-2-r16} OPTIONAL, -- Need M + codebookSubsetDCI-0-2-r16 ENUMERATED {fullyAndPartialAndNonCoherent, partialAndNonCoherent,nonCoherent} + OPTIONAL, -- Cond codebookBased + invalidSymbolPatternIndicatorDCI-0-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + maxRankDCI-0-2-r16 INTEGER (1..4) OPTIONAL, -- Cond codebookBased + mcs-TableDCI-0-2-r16 ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + mcs-TableTransformPrecoderDCI-0-2-r16 ENUMERATED {qam256, qam64LowSE} OPTIONAL, -- Need S + priorityIndicatorDCI-0-2-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + pusch-RepTypeIndicatorDCI-0-2-r16 ENUMERATED { pusch-RepTypeA, pusch-RepTypeB} OPTIONAL, -- Need R + resourceAllocationDCI-0-2-r16 ENUMERATED { resourceAllocationType0, resourceAllocationType1, dynamicSwitch} + OPTIONAL, -- Need M + resourceAllocationType1GranularityDCI-0-2-r16 ENUMERATED { n2,n4,n8,n16 } OPTIONAL, -- Need S + uci-OnPUSCH-ListDCI-0-2-r16 SetupRelease { UCI-OnPUSCH-ListDCI-0-2-r16} OPTIONAL, -- Need M + pusch-TimeDomainAllocationListDCI-0-2-r16 SetupRelease { PUSCH-TimeDomainResourceAllocationList-r16 } + OPTIONAL, -- Need M + -- End of the parameters for DCI format 0_2 introduced in V16.1.0 + -- Start of the parameters for DCI format 0_1 introduced in V16.1.0 + pusch-TimeDomainAllocationListDCI-0-1-r16 SetupRelease { PUSCH-TimeDomainResourceAllocationList-r16 } + OPTIONAL, -- Need M + invalidSymbolPatternIndicatorDCI-0-1-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + priorityIndicatorDCI-0-1-r16 ENUMERATED {enabled} OPTIONAL, -- Need S + pusch-RepTypeIndicatorDCI-0-1-r16 ENUMERATED { pusch-RepTypeA, pusch-RepTypeB} OPTIONAL, -- Need R + frequencyHoppingDCI-0-1-r16 ENUMERATED {interRepetition, interSlot} OPTIONAL, -- Cond RepTypeB + uci-OnPUSCH-ListDCI-0-1-r16 SetupRelease { UCI-OnPUSCH-ListDCI-0-1-r16 } OPTIONAL, -- Need M + -- End of the parameters for DCI format 0_1 introduced in V16.1.0 + invalidSymbolPattern-r16 InvalidSymbolPattern-r16 OPTIONAL, -- Need S + pusch-PowerControl-v1610 SetupRelease {PUSCH-PowerControl-v1610} OPTIONAL, -- Need M + ul-FullPowerTransmission-r16 ENUMERATED {fullpower, fullpowerMode1, fullpowerMode2} OPTIONAL, -- Need R + pusch-TimeDomainAllocationListForMultiPUSCH-r16 SetupRelease { PUSCH-TimeDomainResourceAllocationList-r16 } + OPTIONAL, -- Need M + numberOfInvalidSymbolsForDL-UL-Switching-r16 INTEGER (1..4) OPTIONAL -- Cond RepTypeB2 + ]] +} + +UCI-OnPUSCH ::= SEQUENCE { + betaOffsets CHOICE { + dynamic SEQUENCE (SIZE (4)) OF BetaOffsets, + semiStatic BetaOffsets + } OPTIONAL, -- Need M + scaling ENUMERATED { f0p5, f0p65, f0p8, f1 } +} + +MinSchedulingOffsetK2-Values-r16 ::= SEQUENCE (SIZE (1..maxNrOfMinSchedulingOffsetValues-r16)) OF INTEGER (0..maxK2-SchedulingOffset-r16) + +UCI-OnPUSCH-DCI-0-2-r16 ::= SEQUENCE { + betaOffsetsDCI-0-2-r16 CHOICE { + dynamicDCI-0-2-r16 CHOICE { + oneBit-r16 SEQUENCE (SIZE (2)) OF BetaOffsets, + twoBits-r16 SEQUENCE (SIZE (4)) OF BetaOffsets + }, + semiStaticDCI-0-2-r16 BetaOffsets + } OPTIONAL, -- Need M + scalingDCI-0-2-r16 ENUMERATED { f0p5, f0p65, f0p8, f1 } +} + +FrequencyHoppingOffsetListsDCI-0-2-r16 ::= SEQUENCE (SIZE (1..4)) OF INTEGER (1.. maxNrofPhysicalResourceBlocks-1) + +UCI-OnPUSCH-ListDCI-0-2-r16 ::= SEQUENCE (SIZE (1..2)) OF UCI-OnPUSCH-DCI-0-2-r16 + +UCI-OnPUSCH-ListDCI-0-1-r16 ::= SEQUENCE (SIZE (1..2)) OF UCI-OnPUSCH + +UL-AccessConfigListDCI-0-1-r16 ::= SEQUENCE (SIZE (1..64)) OF INTEGER (0..63) + +-- TAG-PUSCH-CONFIG-STOP +-- TAG-PUSCH-CONFIGCOMMON-START + +PUSCH-ConfigCommon ::= SEQUENCE { + groupHoppingEnabledTransformPrecoding ENUMERATED {enabled} OPTIONAL, -- Need R + pusch-TimeDomainAllocationList PUSCH-TimeDomainResourceAllocationList OPTIONAL, -- Need R + msg3-DeltaPreamble INTEGER (-1..6) OPTIONAL, -- Need R + p0-NominalWithGrant INTEGER (-202..24) OPTIONAL, -- Need R + ... +} + +-- TAG-PUSCH-CONFIGCOMMON-STOP +-- TAG-PUSCH-POWERCONTROL-START + +PUSCH-PowerControl ::= SEQUENCE { + tpc-Accumulation ENUMERATED { disabled } OPTIONAL, -- Need S + msg3-Alpha Alpha OPTIONAL, -- Need S + p0-NominalWithoutGrant INTEGER (-202..24) OPTIONAL, -- Need M + p0-AlphaSets SEQUENCE (SIZE (1..maxNrofP0-PUSCH-AlphaSets)) OF P0-PUSCH-AlphaSet OPTIONAL, -- Need M + pathlossReferenceRSToAddModList SEQUENCE (SIZE (1..maxNrofPUSCH-PathlossReferenceRSs)) OF PUSCH-PathlossReferenceRS + OPTIONAL, -- Need N + pathlossReferenceRSToReleaseList SEQUENCE (SIZE (1..maxNrofPUSCH-PathlossReferenceRSs)) OF PUSCH-PathlossReferenceRS-Id + OPTIONAL, -- Need N + twoPUSCH-PC-AdjustmentStates ENUMERATED {twoStates} OPTIONAL, -- Need S + deltaMCS ENUMERATED {enabled} OPTIONAL, -- Need S + sri-PUSCH-MappingToAddModList SEQUENCE (SIZE (1..maxNrofSRI-PUSCH-Mappings)) OF SRI-PUSCH-PowerControl + OPTIONAL, -- Need N + sri-PUSCH-MappingToReleaseList SEQUENCE (SIZE (1..maxNrofSRI-PUSCH-Mappings)) OF SRI-PUSCH-PowerControlId + OPTIONAL -- Need N +} + +P0-PUSCH-AlphaSet ::= SEQUENCE { + p0-PUSCH-AlphaSetId P0-PUSCH-AlphaSetId, + p0 INTEGER (-16..15) OPTIONAL, -- Need S + alpha Alpha OPTIONAL -- Need S +} + +P0-PUSCH-AlphaSetId ::= INTEGER (0..maxNrofP0-PUSCH-AlphaSets-1) + +PUSCH-PathlossReferenceRS ::= SEQUENCE { + pusch-PathlossReferenceRS-Id PUSCH-PathlossReferenceRS-Id, + referenceSignal CHOICE { + ssb-Index SSB-Index, + csi-RS-Index NZP-CSI-RS-ResourceId + } +} + +PUSCH-PathlossReferenceRS-r16 ::= SEQUENCE { + pusch-PathlossReferenceRS-Id-r16 PUSCH-PathlossReferenceRS-Id-v1610, + referenceSignal-r16 CHOICE { + ssb-Index-r16 SSB-Index, + csi-RS-Index-r16 NZP-CSI-RS-ResourceId + } +} + +PUSCH-PathlossReferenceRS-Id ::= INTEGER (0..maxNrofPUSCH-PathlossReferenceRSs-1) + +PUSCH-PathlossReferenceRS-Id-v1610 ::= INTEGER (maxNrofPUSCH-PathlossReferenceRSs..maxNrofPUSCH-PathlossReferenceRSs-1-r16) + +SRI-PUSCH-PowerControl ::= SEQUENCE { + sri-PUSCH-PowerControlId SRI-PUSCH-PowerControlId, + sri-PUSCH-PathlossReferenceRS-Id PUSCH-PathlossReferenceRS-Id, + sri-P0-PUSCH-AlphaSetId P0-PUSCH-AlphaSetId, + sri-PUSCH-ClosedLoopIndex ENUMERATED { i0, i1 } +} + +SRI-PUSCH-PowerControlId ::= INTEGER (0..maxNrofSRI-PUSCH-Mappings-1) + +PUSCH-PowerControl-v1610 ::= SEQUENCE { + pathlossReferenceRSToAddModListSizeExt-v1610 SEQUENCE (SIZE (1..maxNrofPUSCH-PathlossReferenceRSsDiff-r16)) OF PUSCH-PathlossReferenceRS-r16 + OPTIONAL, -- Need N + pathlossReferenceRSToReleaseListSizeExt-v1610 SEQUENCE (SIZE (1..maxNrofPUSCH-PathlossReferenceRSsDiff-r16)) OF PUSCH-PathlossReferenceRS-Id-v1610 + OPTIONAL, -- Need N + p0-PUSCH-SetList-r16 SEQUENCE (SIZE (1..maxNrofSRI-PUSCH-Mappings)) OF P0-PUSCH-Set-r16 OPTIONAL, -- Need R + olpc-ParameterSet SEQUENCE { + olpc-ParameterSetDCI-0-1-r16 INTEGER (1..2) OPTIONAL, -- Need R + olpc-ParameterSetDCI-0-2-r16 INTEGER (1..2) OPTIONAL -- Need R + } OPTIONAL, -- Need M + ... +} + +P0-PUSCH-Set-r16 ::= SEQUENCE { + p0-PUSCH-SetId-r16 P0-PUSCH-SetId-r16, + p0-List-r16 SEQUENCE (SIZE (1..maxNrofP0-PUSCH-Set-r16)) OF P0-PUSCH-r16 OPTIONAL, -- Need R + ... +} + +P0-PUSCH-SetId-r16 ::= INTEGER (0..maxNrofSRI-PUSCH-Mappings-1) + +P0-PUSCH-r16 ::= INTEGER (-16..15) + +-- TAG-PUSCH-POWERCONTROL-STOP +-- TAG-PUSCH-SERVINGCELLCONFIG-START + +PUSCH-ServingCellConfig ::= SEQUENCE { + codeBlockGroupTransmission SetupRelease { PUSCH-CodeBlockGroupTransmission } OPTIONAL, -- Need M + rateMatching ENUMERATED {limitedBufferRM} OPTIONAL, -- Need S + xOverhead ENUMERATED {xoh6, xoh12, xoh18} OPTIONAL, -- Need S + ..., + [[ + maxMIMO-Layers INTEGER (1..4) OPTIONAL, -- Need M + processingType2Enabled BOOLEAN OPTIONAL -- Need M + ]], + [[ + maxMIMO-LayersDCI-0-2-r16 SetupRelease { MaxMIMO-LayersDCI-0-2-r16} OPTIONAL -- Need M + ]] +} + +PUSCH-CodeBlockGroupTransmission ::= SEQUENCE { + maxCodeBlockGroupsPerTransportBlock ENUMERATED {n2, n4, n6, n8}, + ... +} + +MaxMIMO-LayersDCI-0-2-r16 ::= INTEGER (1..4) + +-- TAG-PUSCH-SERVINGCELLCONFIG-STOP +-- TAG-PUSCH-TIMEDOMAINRESOURCEALLOCATIONLIST-START + +PUSCH-TimeDomainResourceAllocationList ::= SEQUENCE (SIZE(1..maxNrofUL-Allocations)) OF PUSCH-TimeDomainResourceAllocation + +PUSCH-TimeDomainResourceAllocation ::= SEQUENCE { + k2 INTEGER(0..32) OPTIONAL, -- Need S + mappingType ENUMERATED {typeA, typeB}, + startSymbolAndLength INTEGER (0..127) +} + +PUSCH-TimeDomainResourceAllocationList-r16 ::= SEQUENCE (SIZE(1..maxNrofUL-Allocations-r16)) OF PUSCH-TimeDomainResourceAllocation-r16 + +PUSCH-TimeDomainResourceAllocation-r16 ::= SEQUENCE { + k2-r16 INTEGER(0..32) OPTIONAL, -- Need S + puschAllocationList-r16 SEQUENCE (SIZE(1..maxNrofMultiplePUSCHs-r16)) OF PUSCH-Allocation-r16, +... +} + +PUSCH-Allocation-r16 ::= SEQUENCE { + mappingType-r16 ENUMERATED {typeA, typeB} OPTIONAL, -- Cond NotFormat01-02-Or-TypeA + startSymbolAndLength-r16 INTEGER (0..127) OPTIONAL, -- Cond NotFormat01-02-Or-TypeA + startSymbol-r16 INTEGER (0..13) OPTIONAL, -- Cond RepTypeB + length-r16 INTEGER (1..14) OPTIONAL, -- Cond RepTypeB + numberOfRepetitions-r16 ENUMERATED {n1, n2, n3, n4, n7, n8, n12, n16} OPTIONAL, -- Cond Format01-02 + ... +} + +-- TAG-PUSCH-TIMEDOMAINRESOURCEALLOCATIONLIST-STOP +-- TAG-PUSCH-TPC-COMMANDCONFIG-START + +PUSCH-TPC-CommandConfig ::= SEQUENCE { + tpc-Index INTEGER (1..15) OPTIONAL, -- Cond SUL + tpc-IndexSUL INTEGER (1..15) OPTIONAL, -- Cond SUL-Only + targetCell ServCellIndex OPTIONAL, -- Need S + ... +} + +-- TAG-PUSCH-TPC-COMMANDCONFIG-STOP +-- TAG-Q-OFFSETRANGE-START + +Q-OffsetRange ::= ENUMERATED { + dB-24, dB-22, dB-20, dB-18, dB-16, dB-14, + dB-12, dB-10, dB-8, dB-6, dB-5, dB-4, dB-3, + dB-2, dB-1, dB0, dB1, dB2, dB3, dB4, dB5, + dB6, dB8, dB10, dB12, dB14, dB16, dB18, + dB20, dB22, dB24} + +-- TAG-Q-OFFSETRANGE-STOP +-- TAG-Q-QUALMIN-START + +Q-QualMin ::= INTEGER (-43..-12) + +-- TAG-Q-QUALMIN-STOP +-- TAG-Q-RXLEVMIN-START + +Q-RxLevMin ::= INTEGER (-70..-22) + +-- TAG-Q-RXLEVMIN-STOP +-- TAG-QUANTITYCONFIG-START + + +QuantityConfig ::= SEQUENCE { + quantityConfigNR-List SEQUENCE (SIZE (1..maxNrofQuantityConfig)) OF QuantityConfigNR OPTIONAL, -- Need M + ..., + [[ + quantityConfigEUTRA FilterConfig OPTIONAL -- Need M + ]], + [[ + quantityConfigUTRA-FDD-r16 QuantityConfigUTRA-FDD-r16 OPTIONAL, -- Need M + quantityConfigCLI-r16 FilterConfigCLI-r16 OPTIONAL -- Need M + ]] +} + +QuantityConfigNR::= SEQUENCE { + quantityConfigCell QuantityConfigRS, + quantityConfigRS-Index QuantityConfigRS OPTIONAL -- Need M +} + +QuantityConfigRS ::= SEQUENCE { + ssb-FilterConfig FilterConfig, + csi-RS-FilterConfig FilterConfig +} + +FilterConfig ::= SEQUENCE { + filterCoefficientRSRP FilterCoefficient DEFAULT fc4, + filterCoefficientRSRQ FilterCoefficient DEFAULT fc4, + filterCoefficientRS-SINR FilterCoefficient DEFAULT fc4 +} + +FilterConfigCLI-r16 ::= SEQUENCE { + filterCoefficientSRS-RSRP-r16 FilterCoefficient DEFAULT fc4, + filterCoefficientCLI-RSSI-r16 FilterCoefficient DEFAULT fc4 +} + +QuantityConfigUTRA-FDD-r16 ::= SEQUENCE { + filterCoefficientRSCP-r16 FilterCoefficient DEFAULT fc4, + filterCoefficientEcNO-r16 FilterCoefficient DEFAULT fc4 +} + +-- TAG-QUANTITYCONFIG-STOP +-- TAG-RACH-CONFIGCOMMON-START + +RACH-ConfigCommon ::= SEQUENCE { + rach-ConfigGeneric RACH-ConfigGeneric, + totalNumberOfRA-Preambles INTEGER (1..63) OPTIONAL, -- Need S + ssb-perRACH-OccasionAndCB-PreamblesPerSSB CHOICE { + oneEighth ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + oneFourth ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + oneHalf ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + one ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + two ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32}, + four INTEGER (1..16), + eight INTEGER (1..8), + sixteen INTEGER (1..4) + } OPTIONAL, -- Need M + + groupBconfigured SEQUENCE { + ra-Msg3SizeGroupA ENUMERATED {b56, b144, b208, b256, b282, b480, b640, + b800, b1000, b72, spare6, spare5,spare4, spare3, spare2, spare1}, + messagePowerOffsetGroupB ENUMERATED { minusinfinity, dB0, dB5, dB8, dB10, dB12, dB15, dB18}, + numberOfRA-PreamblesGroupA INTEGER (1..64) + } OPTIONAL, -- Need R + ra-ContentionResolutionTimer ENUMERATED { sf8, sf16, sf24, sf32, sf40, sf48, sf56, sf64}, + rsrp-ThresholdSSB RSRP-Range OPTIONAL, -- Need R + rsrp-ThresholdSSB-SUL RSRP-Range OPTIONAL, -- Cond SUL + prach-RootSequenceIndex CHOICE { + l839 INTEGER (0..837), + l139 INTEGER (0..137) + }, + msg1-SubcarrierSpacing SubcarrierSpacing OPTIONAL, -- Cond L139 + restrictedSetConfig ENUMERATED {unrestrictedSet, restrictedSetTypeA, restrictedSetTypeB}, + msg3-transformPrecoder ENUMERATED {enabled} OPTIONAL, -- Need R + ..., + [[ + ra-PrioritizationForAccessIdentity-r16 SEQUENCE { + ra-Prioritization-r16 RA-Prioritization, + ra-PrioritizationForAI-r16 BIT STRING (SIZE (2)) + } OPTIONAL, -- Cond InitialBWP-Only + prach-RootSequenceIndex-r16 CHOICE { + l571 INTEGER (0..569), + l1151 INTEGER (0..1149) + } OPTIONAL -- Need R + ]] +} + +-- TAG-RACH-CONFIGCOMMON-STOP +-- TAG-RACH-CONFIGCOMMONTWOSTEPRA-START + +RACH-ConfigCommonTwoStepRA-r16 ::= SEQUENCE { + rach-ConfigGenericTwoStepRA-r16 RACH-ConfigGenericTwoStepRA-r16, + msgA-TotalNumberOfRA-Preambles-r16 INTEGER (1..63) OPTIONAL, -- Need S + msgA-SSB-PerRACH-OccasionAndCB-PreamblesPerSSB-r16 CHOICE { + oneEighth ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + oneFourth ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + oneHalf ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + one ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32,n36,n40,n44,n48,n52,n56,n60,n64}, + two ENUMERATED {n4,n8,n12,n16,n20,n24,n28,n32}, + four INTEGER (1..16), + eight INTEGER (1..8), + sixteen INTEGER (1..4) + } OPTIONAL, -- Cond 2StepOnly + msgA-CB-PreamblesPerSSB-PerSharedRO-r16 INTEGER (1..60) OPTIONAL, -- Cond SharedRO + msgA-SSB-SharedRO-MaskIndex-r16 INTEGER (1..15) OPTIONAL, -- Need S + groupB-ConfiguredTwoStepRA-r16 GroupB-ConfiguredTwoStepRA-r16 OPTIONAL, -- Need S + msgA-PRACH-RootSequenceIndex-r16 CHOICE { + l839 INTEGER (0..837), + l139 INTEGER (0..137), + l571 INTEGER (0..569), + l1151 INTEGER (0..1149) + } OPTIONAL, -- Cond 2StepOnly + msgA-TransMax-r16 ENUMERATED {n1, n2, n4, n6, n8, n10, n20, n50, n100, n200} OPTIONAL, -- Need R + msgA-RSRP-Threshold-r16 RSRP-Range OPTIONAL, -- Cond 2Step4Step + msgA-RSRP-ThresholdSSB-r16 RSRP-Range OPTIONAL, -- Need R + msgA-SubcarrierSpacing-r16 SubcarrierSpacing OPTIONAL, -- Cond 2StepOnlyL139 + msgA-RestrictedSetConfig-r16 ENUMERATED {unrestrictedSet, restrictedSetTypeA, + restrictedSetTypeB} OPTIONAL, -- Cond 2StepOnly + ra-PrioritizationForAccessIdentityTwoStep-r16 SEQUENCE { + ra-Prioritization-r16 RA-Prioritization, + ra-PrioritizationForAI-r16 BIT STRING (SIZE (2)) + } OPTIONAL, -- Cond InitialBWP-Only + ra-ContentionResolutionTimer-r16 ENUMERATED {sf8, sf16, sf24, sf32, sf40, sf48, sf56, sf64} OPTIONAL, -- Cond 2StepOnly + ... +} + +GroupB-ConfiguredTwoStepRA-r16 ::= SEQUENCE { + ra-MsgA-SizeGroupA ENUMERATED {b56, b144, b208, b256, b282, b480, b640, b800, + b1000, b72, spare6, spare5, spare4, spare3, spare2, spare1}, + messagePowerOffsetGroupB ENUMERATED {minusinfinity, dB0, dB5, dB8, dB10, dB12, dB15, dB18}, + numberOfRA-PreamblesGroupA INTEGER (1..64) +} + +-- TAG-RACH-CONFIGCOMMONTWOSTEPRA-STOP +-- TAG-RACH-CONFIGDEDICATED-START + + +RACH-ConfigDedicated ::= SEQUENCE { + cfra CFRA OPTIONAL, -- Need S + ra-Prioritization RA-Prioritization OPTIONAL, -- Need N + ..., + [[ + ra-PrioritizationTwoStep-r16 RA-Prioritization OPTIONAL, -- Need N + cfra-TwoStep-r16 CFRA-TwoStep-r16 OPTIONAL -- Need S + ]] +} + +CFRA ::= SEQUENCE { + occasions SEQUENCE { + rach-ConfigGeneric RACH-ConfigGeneric, + ssb-perRACH-Occasion ENUMERATED {oneEighth, oneFourth, oneHalf, one, two, four, eight, sixteen} + OPTIONAL -- Cond Mandatory + } OPTIONAL, -- Need S + resources CHOICE { + ssb SEQUENCE { + ssb-ResourceList SEQUENCE (SIZE(1..maxRA-SSB-Resources)) OF CFRA-SSB-Resource, + ra-ssb-OccasionMaskIndex INTEGER (0..15) + }, + csirs SEQUENCE { + csirs-ResourceList SEQUENCE (SIZE(1..maxRA-CSIRS-Resources)) OF CFRA-CSIRS-Resource, + rsrp-ThresholdCSI-RS RSRP-Range + } + }, + ..., + [[ + totalNumberOfRA-Preambles INTEGER (1..63) OPTIONAL -- Cond Occasions + ]] +} + +CFRA-TwoStep-r16 ::= SEQUENCE { + occasionsTwoStepRA-r16 SEQUENCE { + rach-ConfigGenericTwoStepRA-r16 RACH-ConfigGenericTwoStepRA-r16, + ssb-PerRACH-OccasionTwoStepRA-r16 ENUMERATED {oneEighth, oneFourth, oneHalf, one, + two, four, eight, sixteen} + } OPTIONAL, -- Need S + msgA-CFRA-PUSCH-r16 MsgA-PUSCH-Resource-r16, + msgA-TransMax-r16 ENUMERATED {n1, n2, n4, n6, n8, n10, n20, n50, n100, n200} OPTIONAL, -- Need S + resourcesTwoStep-r16 SEQUENCE { + ssb-ResourceList SEQUENCE (SIZE(1..maxRA-SSB-Resources)) OF CFRA-SSB-Resource, + ra-ssb-OccasionMaskIndex INTEGER (0..15) + }, + ... +} + +CFRA-SSB-Resource ::= SEQUENCE { + ssb SSB-Index, + ra-PreambleIndex INTEGER (0..63), + ..., + [[ + msgA-PUSCH-Resource-Index-r16 INTEGER (0..3071) OPTIONAL -- Cond 2StepCFRA + ]] + +} + +CFRA-CSIRS-Resource ::= SEQUENCE { + csi-RS CSI-RS-Index, + ra-OccasionList SEQUENCE (SIZE(1..maxRA-OccasionsPerCSIRS)) OF INTEGER (0..maxRA-Occasions-1), + ra-PreambleIndex INTEGER (0..63), + ... +} + +-- TAG-RACH-CONFIGDEDICATED-STOP +-- TAG-RACH-CONFIGGENERIC-START + +RACH-ConfigGeneric ::= SEQUENCE { + prach-ConfigurationIndex INTEGER (0..255), + msg1-FDM ENUMERATED {one, two, four, eight}, + msg1-FrequencyStart INTEGER (0..maxNrofPhysicalResourceBlocks-1), + zeroCorrelationZoneConfig INTEGER(0..15), + preambleReceivedTargetPower INTEGER (-202..-60), + preambleTransMax ENUMERATED {n3, n4, n5, n6, n7, n8, n10, n20, n50, n100, n200}, + powerRampingStep ENUMERATED {dB0, dB2, dB4, dB6}, + ra-ResponseWindow ENUMERATED {sl1, sl2, sl4, sl8, sl10, sl20, sl40, sl80}, + ..., + [[ + prach-ConfigurationPeriodScaling-IAB-r16 ENUMERATED {scf1,scf2,scf4,scf8,scf16,scf32,scf64} OPTIONAL, -- Need R + prach-ConfigurationFrameOffset-IAB-r16 INTEGER (0..63) OPTIONAL, -- Need R + prach-ConfigurationSOffset-IAB-r16 INTEGER (0..39) OPTIONAL, -- Need R + ra-ResponseWindow-v1610 ENUMERATED { sl60, sl160} OPTIONAL, -- Need R + prach-ConfigurationIndex-v1610 INTEGER (256..262) OPTIONAL -- Need R + ]] +} + +-- TAG-RACH-CONFIGGENERIC-STOP +-- TAG-RACH-CONFIGGENERICTWOSTEPRA-START + +RACH-ConfigGenericTwoStepRA-r16 ::= SEQUENCE { + msgA-PRACH-ConfigurationIndex-r16 INTEGER (0..262) OPTIONAL, -- Cond 2StepOnly + msgA-RO-FDM-r16 ENUMERATED {one, two, four, eight} OPTIONAL, -- Cond 2StepOnly + msgA-RO-FrequencyStart-r16 INTEGER (0..maxNrofPhysicalResourceBlocks-1) OPTIONAL, -- Cond 2StepOnly + msgA-ZeroCorrelationZoneConfig-r16 INTEGER (0..15) OPTIONAL, -- Cond 2StepOnly + msgA-PreamblePowerRampingStep-r16 ENUMERATED {dB0, dB2, dB4, dB6} OPTIONAL, -- Cond 2StepOnlyNoCFRA + msgA-PreambleReceivedTargetPower-r16 INTEGER (-202..-60) OPTIONAL, -- Cond 2StepOnlyNoCFRA + msgB-ResponseWindow-r16 ENUMERATED {sl1, sl2, sl4, sl8, sl10, sl20, sl40, sl80, sl160, sl320} + OPTIONAL, -- Cond NoCFRA + preambleTransMax-r16 ENUMERATED {n3, n4, n5, n6, n7, n8, n10, n20, n50, n100, n200} OPTIONAL, -- Cond 2StepOnlyNoCFRA + ... +} + +-- TAG-RACH-CONFIGGENERICTWOSTEPRA-STOP +-- TAG-RA-PRIORITIZATION-START + +RA-Prioritization ::= SEQUENCE { + powerRampingStepHighPriority ENUMERATED {dB0, dB2, dB4, dB6}, + scalingFactorBI ENUMERATED {zero, dot25, dot5, dot75} OPTIONAL, -- Need R + ... +} + +-- TAG-RA-PRIORITIZATION-STOP +-- TAG-RADIOBEARERCONFIG-START + +RadioBearerConfig ::= SEQUENCE { + srb-ToAddModList SRB-ToAddModList OPTIONAL, -- Cond HO-Conn + srb3-ToRelease ENUMERATED{true} OPTIONAL, -- Need N + drb-ToAddModList DRB-ToAddModList OPTIONAL, -- Cond HO-toNR + drb-ToReleaseList DRB-ToReleaseList OPTIONAL, -- Need N + securityConfig SecurityConfig OPTIONAL, -- Need M + ... +} + +SRB-ToAddModList ::= SEQUENCE (SIZE (1..2)) OF SRB-ToAddMod +SRB-ToAddMod ::= SEQUENCE { + srb-Identity SRB-Identity, + reestablishPDCP ENUMERATED{true} OPTIONAL, -- Need N + discardOnPDCP ENUMERATED{true} OPTIONAL, -- Need N + pdcp-Config PDCP-Config OPTIONAL, -- Cond PDCP + ... +} + +DRB-ToAddModList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-ToAddMod + +DRB-ToAddMod ::= SEQUENCE { + cnAssociation CHOICE { + eps-BearerIdentity INTEGER (0..15), + sdap-Config SDAP-Config + } OPTIONAL, -- Cond DRBSetup + drb-Identity DRB-Identity, + reestablishPDCP ENUMERATED{true} OPTIONAL, -- Need N + recoverPDCP ENUMERATED{true} OPTIONAL, -- Need N + pdcp-Config PDCP-Config OPTIONAL, -- Cond PDCP + ..., + [[ + daps-Config-r16 ENUMERATED{true} OPTIONAL -- Cond DAPS + ]] +} +DRB-ToReleaseList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-Identity + +SecurityConfig ::= SEQUENCE { + securityAlgorithmConfig SecurityAlgorithmConfig OPTIONAL, -- Cond RBTermChange1 + keyToUse ENUMERATED{master, secondary} OPTIONAL, -- Cond RBTermChange + ... +} + +-- TAG-RADIOBEARERCONFIG-STOP +-- TAG-RADIOLINKMONITORINGCONFIG-START + +RadioLinkMonitoringConfig ::= SEQUENCE { + failureDetectionResourcesToAddModList SEQUENCE (SIZE(1..maxNrofFailureDetectionResources)) OF RadioLinkMonitoringRS + OPTIONAL, -- Need N + failureDetectionResourcesToReleaseList SEQUENCE (SIZE(1..maxNrofFailureDetectionResources)) OF RadioLinkMonitoringRS-Id + OPTIONAL, -- Need N + beamFailureInstanceMaxCount ENUMERATED {n1, n2, n3, n4, n5, n6, n8, n10} OPTIONAL, -- Need R + beamFailureDetectionTimer ENUMERATED {pbfd1, pbfd2, pbfd3, pbfd4, pbfd5, pbfd6, pbfd8, pbfd10} OPTIONAL, -- Need R + ... +} + +RadioLinkMonitoringRS ::= SEQUENCE { + radioLinkMonitoringRS-Id RadioLinkMonitoringRS-Id, + purpose ENUMERATED {beamFailure, rlf, both}, + detectionResource CHOICE { + ssb-Index SSB-Index, + csi-RS-Index NZP-CSI-RS-ResourceId + }, + ... +} + +-- TAG-RADIOLINKMONITORINGCONFIG-STOP +-- TAG-RADIOLINKMONITORINGRS-ID-START + +RadioLinkMonitoringRS-Id ::= INTEGER (0..maxNrofFailureDetectionResources-1) + +-- TAG-RADIOLINKMONITORINGRS-ID-STOP +-- TAG-RAN-AREACODE-START + +RAN-AreaCode ::= INTEGER (0..255) + +-- TAG-RAN-AREACODE-STOP +-- TAG-RATEMATCHPATTERN-START + +RateMatchPattern ::= SEQUENCE { + rateMatchPatternId RateMatchPatternId, + + patternType CHOICE { + bitmaps SEQUENCE { + resourceBlocks BIT STRING (SIZE (275)), + symbolsInResourceBlock CHOICE { + oneSlot BIT STRING (SIZE (14)), + twoSlots BIT STRING (SIZE (28)) + }, + periodicityAndPattern CHOICE { + n2 BIT STRING (SIZE (2)), + n4 BIT STRING (SIZE (4)), + n5 BIT STRING (SIZE (5)), + n8 BIT STRING (SIZE (8)), + n10 BIT STRING (SIZE (10)), + n20 BIT STRING (SIZE (20)), + n40 BIT STRING (SIZE (40)) + } OPTIONAL, -- Need S + ... + }, + controlResourceSet ControlResourceSetId + }, + subcarrierSpacing SubcarrierSpacing OPTIONAL, -- Cond CellLevel + dummy ENUMERATED { dynamic, semiStatic }, + ..., + [[ + controlResourceSet-r16 ControlResourceSetId-r16 OPTIONAL -- Need R + ]] + +} + +-- TAG-RATEMATCHPATTERN-STOP +-- TAG-RATEMATCHPATTERNID-START + +RateMatchPatternId ::= INTEGER (0..maxNrofRateMatchPatterns-1) + +-- TAG-RATEMATCHPATTERNID-STOP +-- TAG-RATEMATCHPATTERNLTE-CRS-START + +RateMatchPatternLTE-CRS ::= SEQUENCE { + carrierFreqDL INTEGER (0..16383), + carrierBandwidthDL ENUMERATED {n6, n15, n25, n50, n75, n100, spare2, spare1}, + mbsfn-SubframeConfigList EUTRA-MBSFN-SubframeConfigList OPTIONAL, -- Need M + nrofCRS-Ports ENUMERATED {n1, n2, n4}, + v-Shift ENUMERATED {n0, n1, n2, n3, n4, n5} +} + +LTE-CRS-PatternList-r16 ::= SEQUENCE (SIZE (1..maxLTE-CRS-Patterns-r16)) OF RateMatchPatternLTE-CRS + +-- TAG-RATEMATCHPATTERNLTE-CRS-STOP +-- TAG-REFERENCETIMEINFO-START + +ReferenceTimeInfo-r16 ::= SEQUENCE { + time-r16 ReferenceTime-r16, + uncertainty-r16 INTEGER (0..32767) OPTIONAL, -- Need S + timeInfoType-r16 ENUMERATED {localClock} OPTIONAL, -- Need S + referenceSFN-r16 INTEGER (0..1023) OPTIONAL -- Cond RefTime +} + +ReferenceTime-r16 ::= SEQUENCE { + refDays-r16 INTEGER (0..72999), + refSeconds-r16 INTEGER (0..86399), + refMilliSeconds-r16 INTEGER (0..999), + refTenNanoSeconds-r16 INTEGER (0..99999) +} + +-- TAG-REFERENCETIMEINFO-STOP +-- TAG-REJECTWAITTIME-START + +RejectWaitTime ::= INTEGER (1..16) + +-- TAG-REJECTWAITTIME-STOP +-- TAG-REPETITIONSCHEMECONFIG-START + +RepetitionSchemeConfig-r16 ::= CHOICE { + fdm-TDM-r16 SetupRelease { FDM-TDM-r16 }, + slotBased-r16 SetupRelease { SlotBased-r16 } +} + +RepetitionSchemeConfig-v1630 ::= SEQUENCE { + slotBased-v1630 SetupRelease { SlotBased-v1630 } +} + +FDM-TDM-r16 ::= SEQUENCE { + repetitionScheme-r16 ENUMERATED {fdmSchemeA, fdmSchemeB,tdmSchemeA }, + startingSymbolOffsetK-r16 INTEGER (0..7) OPTIONAL -- Need R +} + +SlotBased-r16 ::= SEQUENCE { + tciMapping-r16 ENUMERATED {cyclicMapping, sequentialMapping}, + sequenceOffsetForRV-r16 INTEGER (1..3) +} + +SlotBased-v1630 ::= SEQUENCE { + tciMapping-r16 ENUMERATED {cyclicMapping, sequentialMapping}, + sequenceOffsetForRV-r16 INTEGER (0) +} + +-- TAG-REPETITIONSCHEMECONFIG-STOP +-- TAG-REPORTCONFIGID-START + +ReportConfigId ::= INTEGER (1..maxReportConfigId) + +-- TAG-REPORTCONFIGID-STOP +-- TAG-REPORTCONFIGINTERRAT-START + +ReportConfigInterRAT ::= SEQUENCE { + reportType CHOICE { + periodical PeriodicalReportConfigInterRAT, + eventTriggered EventTriggerConfigInterRAT, + reportCGI ReportCGI-EUTRA, + ..., + reportSFTD ReportSFTD-EUTRA + } +} + +ReportCGI-EUTRA ::= SEQUENCE { + cellForWhichToReportCGI EUTRA-PhysCellId, + ..., + [[ + useAutonomousGaps-r16 ENUMERATED {setup} OPTIONAL -- Need R + ]] +} + +ReportSFTD-EUTRA ::= SEQUENCE { + reportSFTD-Meas BOOLEAN, + reportRSRP BOOLEAN, + ... +} + +EventTriggerConfigInterRAT ::= SEQUENCE { + eventId CHOICE { + eventB1 SEQUENCE { + b1-ThresholdEUTRA MeasTriggerQuantityEUTRA, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger, + ... + }, + eventB2 SEQUENCE { + b2-Threshold1 MeasTriggerQuantity, + b2-Threshold2EUTRA MeasTriggerQuantityEUTRA, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger, + ... + }, + ..., + [[ + eventB1-UTRA-FDD-r16 SEQUENCE { + b1-ThresholdUTRA-FDD-r16 MeasTriggerQuantityUTRA-FDD-r16, + reportOnLeave-r16 BOOLEAN, + hysteresis-r16 Hysteresis, + timeToTrigger-r16 TimeToTrigger, + ... + }, + eventB2-UTRA-FDD-r16 SEQUENCE { + b2-Threshold1-r16 MeasTriggerQuantity, + b2-Threshold2UTRA-FDD-r16 MeasTriggerQuantityUTRA-FDD-r16, + reportOnLeave-r16 BOOLEAN, + hysteresis-r16 Hysteresis, + timeToTrigger-r16 TimeToTrigger, + ... + } + ]] + }, + rsType NR-RS-Type, + + reportInterval ReportInterval, + reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + reportQuantity MeasReportQuantity, + maxReportCells INTEGER (1..maxCellReport), + ..., + [[ + reportQuantityUTRA-FDD-r16 MeasReportQuantityUTRA-FDD-r16 OPTIONAL -- Need R + ]] + + +} + +PeriodicalReportConfigInterRAT ::= SEQUENCE { + reportInterval ReportInterval, + reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + reportQuantity MeasReportQuantity, + maxReportCells INTEGER (1..maxCellReport), + ..., + [[ + reportQuantityUTRA-FDD-r16 MeasReportQuantityUTRA-FDD-r16 OPTIONAL -- Need R + ]] +} + +MeasTriggerQuantityUTRA-FDD-r16 ::= CHOICE{ + utra-FDD-RSCP-r16 INTEGER (-5..91), + utra-FDD-EcN0-r16 INTEGER (0..49) +} + +MeasReportQuantityUTRA-FDD-r16 ::= SEQUENCE { + cpich-RSCP BOOLEAN, + cpich-EcN0 BOOLEAN +} + +-- TAG-REPORTCONFIGINTERRAT-STOP +-- TAG-REPORTCONFIGNR-START + +ReportConfigNR ::= SEQUENCE { + reportType CHOICE { + periodical PeriodicalReportConfig, + eventTriggered EventTriggerConfig, + ..., + reportCGI ReportCGI, + reportSFTD ReportSFTD-NR, + condTriggerConfig-r16 CondTriggerConfig-r16, + cli-Periodical-r16 CLI-PeriodicalReportConfig-r16, + cli-EventTriggered-r16 CLI-EventTriggerConfig-r16 + } +} + +ReportCGI ::= SEQUENCE { + cellForWhichToReportCGI PhysCellId, + ..., + [[ + useAutonomousGaps-r16 ENUMERATED {setup} OPTIONAL -- Need R + ]] + +} + +ReportSFTD-NR ::= SEQUENCE { + reportSFTD-Meas BOOLEAN, + reportRSRP BOOLEAN, + ..., + [[ + reportSFTD-NeighMeas ENUMERATED {true} OPTIONAL, -- Need R + drx-SFTD-NeighMeas ENUMERATED {true} OPTIONAL, -- Need R + cellsForWhichToReportSFTD SEQUENCE (SIZE (1..maxCellSFTD)) OF PhysCellId OPTIONAL -- Need R + ]] +} + +CondTriggerConfig-r16 ::= SEQUENCE { + condEventId CHOICE { + condEventA3 SEQUENCE { + a3-Offset MeasTriggerQuantityOffset, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger + }, + condEventA5 SEQUENCE { + a5-Threshold1 MeasTriggerQuantity, + a5-Threshold2 MeasTriggerQuantity, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger + }, + ... + }, + rsType-r16 NR-RS-Type, + ... +} + +EventTriggerConfig::= SEQUENCE { + eventId CHOICE { + eventA1 SEQUENCE { + a1-Threshold MeasTriggerQuantity, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger + }, + eventA2 SEQUENCE { + a2-Threshold MeasTriggerQuantity, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger + }, + eventA3 SEQUENCE { + a3-Offset MeasTriggerQuantityOffset, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger, + useWhiteCellList BOOLEAN + }, + eventA4 SEQUENCE { + a4-Threshold MeasTriggerQuantity, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger, + useWhiteCellList BOOLEAN + }, + eventA5 SEQUENCE { + a5-Threshold1 MeasTriggerQuantity, + a5-Threshold2 MeasTriggerQuantity, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger, + useWhiteCellList BOOLEAN + }, + eventA6 SEQUENCE { + a6-Offset MeasTriggerQuantityOffset, + reportOnLeave BOOLEAN, + hysteresis Hysteresis, + timeToTrigger TimeToTrigger, + useWhiteCellList BOOLEAN + }, + ... + }, + rsType NR-RS-Type, + reportInterval ReportInterval, + reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + reportQuantityCell MeasReportQuantity, + maxReportCells INTEGER (1..maxCellReport), + reportQuantityRS-Indexes MeasReportQuantity OPTIONAL, -- Need R + maxNrofRS-IndexesToReport INTEGER (1..maxNrofIndexesToReport) OPTIONAL, -- Need R + includeBeamMeasurements BOOLEAN, + reportAddNeighMeas ENUMERATED {setup} OPTIONAL, -- Need R + ..., + [[ + measRSSI-ReportConfig-r16 MeasRSSI-ReportConfig-r16 OPTIONAL, -- Need R + useT312-r16 BOOLEAN OPTIONAL, -- Need M + includeCommonLocationInfo-r16 ENUMERATED {true} OPTIONAL, -- Need R + includeBT-Meas-r16 SetupRelease {BT-NameList-r16} OPTIONAL, -- Need M + includeWLAN-Meas-r16 SetupRelease {WLAN-NameList-r16} OPTIONAL, -- Need M + includeSensor-Meas-r16 SetupRelease {Sensor-NameList-r16} OPTIONAL -- Need M + ]] +} + +PeriodicalReportConfig ::= SEQUENCE { + rsType NR-RS-Type, + reportInterval ReportInterval, + reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + reportQuantityCell MeasReportQuantity, + maxReportCells INTEGER (1..maxCellReport), + reportQuantityRS-Indexes MeasReportQuantity OPTIONAL, -- Need R + maxNrofRS-IndexesToReport INTEGER (1..maxNrofIndexesToReport) OPTIONAL, -- Need R + includeBeamMeasurements BOOLEAN, + useWhiteCellList BOOLEAN, + ..., + [[ + measRSSI-ReportConfig-r16 MeasRSSI-ReportConfig-r16 OPTIONAL, -- Need R + includeCommonLocationInfo-r16 ENUMERATED {true} OPTIONAL, -- Need R + includeBT-Meas-r16 SetupRelease {BT-NameList-r16} OPTIONAL, -- Need M + includeWLAN-Meas-r16 SetupRelease {WLAN-NameList-r16} OPTIONAL, -- Need M + includeSensor-Meas-r16 SetupRelease {Sensor-NameList-r16} OPTIONAL, -- Need M + ul-DelayValueConfig-r16 SetupRelease { UL-DelayValueConfig-r16 } OPTIONAL, -- Need M + reportAddNeighMeas-r16 ENUMERATED {setup} OPTIONAL -- Need R + ]] +} + +NR-RS-Type ::= ENUMERATED {ssb, csi-rs} + +MeasTriggerQuantity ::= CHOICE { + rsrp RSRP-Range, + rsrq RSRQ-Range, + sinr SINR-Range +} + +MeasTriggerQuantityOffset ::= CHOICE { + rsrp INTEGER (-30..30), + rsrq INTEGER (-30..30), + sinr INTEGER (-30..30) +} + + +MeasReportQuantity ::= SEQUENCE { + rsrp BOOLEAN, + rsrq BOOLEAN, + sinr BOOLEAN +} + +MeasRSSI-ReportConfig-r16 ::= SEQUENCE { + channelOccupancyThreshold-r16 RSSI-Range-r16 OPTIONAL -- Need R +} + +CLI-EventTriggerConfig-r16 ::= SEQUENCE { + eventId-r16 CHOICE { + eventI1-r16 SEQUENCE { + i1-Threshold-r16 MeasTriggerQuantityCLI-r16, + reportOnLeave-r16 BOOLEAN, + hysteresis-r16 Hysteresis, + timeToTrigger-r16 TimeToTrigger + }, + ... + }, + reportInterval-r16 ReportInterval, + reportAmount-r16 ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + maxReportCLI-r16 INTEGER (1..maxCLI-Report-r16), + ... +} + +CLI-PeriodicalReportConfig-r16 ::= SEQUENCE { + reportInterval-r16 ReportInterval, + reportAmount-r16 ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + reportQuantityCLI-r16 MeasReportQuantityCLI-r16, + maxReportCLI-r16 INTEGER (1..maxCLI-Report-r16), + ... +} + +MeasTriggerQuantityCLI-r16 ::= CHOICE { + srs-RSRP-r16 SRS-RSRP-Range-r16, + cli-RSSI-r16 CLI-RSSI-Range-r16 +} + +MeasReportQuantityCLI-r16 ::= ENUMERATED {srs-rsrp, cli-rssi} + +-- TAG-REPORTCONFIGNR-STOP +-- TAG-REPORTCONFIGNR-SL-START + +ReportConfigNR-SL-r16 ::= SEQUENCE { + reportType-r16 CHOICE { + periodical-r16 PeriodicalReportConfigNR-SL-r16, + eventTriggered-r16 EventTriggerConfigNR-SL-r16 + } +} + +EventTriggerConfigNR-SL-r16::= SEQUENCE { + eventId-r16 CHOICE { + eventC1 SEQUENCE { + c1-Threshold-r16 SL-CBR-r16, + hysteresis-r16 Hysteresis, + timeToTrigger-r16 TimeToTrigger + }, + eventC2-r16 SEQUENCE { + c2-Threshold-r16 SL-CBR-r16, + hysteresis-r16 Hysteresis, + timeToTrigger-r16 TimeToTrigger + }, + ... + }, + reportInterval-r16 ReportInterval, + reportAmount-r16 ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + reportQuantity-r16 MeasReportQuantity-r16, + ... +} + +PeriodicalReportConfigNR-SL-r16 ::= SEQUENCE { + reportInterval-r16 ReportInterval, + reportAmount-r16 ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + reportQuantity-r16 MeasReportQuantity-r16, + ... +} + +MeasReportQuantity-r16 ::= SEQUENCE { + cbr-r16 BOOLEAN, + ... +} + +-- TAG-REPORTCONFIGNR-SL-STOP +-- TAG-REPORTCONFIGTOADDMODLIST-START + +ReportConfigToAddModList ::= SEQUENCE (SIZE (1..maxReportConfigId)) OF ReportConfigToAddMod + +ReportConfigToAddMod ::= SEQUENCE { + reportConfigId ReportConfigId, + reportConfig CHOICE { + reportConfigNR ReportConfigNR, + ..., + reportConfigInterRAT ReportConfigInterRAT, + reportConfigNR-SL-r16 ReportConfigNR-SL-r16 + } +} + +-- TAG-REPORTCONFIGTOADDMODLIST-STOP +-- TAG-REPORTINTERVAL-START + +ReportInterval ::= ENUMERATED {ms120, ms240, ms480, ms640, ms1024, ms2048, ms5120, ms10240, ms20480, ms40960, + min1,min6, min12, min30 } + +-- TAG-REPORTINTERVAL-STOP +-- TAG-RESELECTIONTHRESHOLD-START + +ReselectionThreshold ::= INTEGER (0..31) + +-- TAG-RESELECTIONTHRESHOLD-STOP +-- TAG-RESELECTIONTHRESHOLDQ-START + +ReselectionThresholdQ ::= INTEGER (0..31) + +-- TAG-RESELECTIONTHRESHOLDQ-STOP +-- TAG-RESUMECAUSE-START + +ResumeCause ::= ENUMERATED {emergency, highPriorityAccess, mt-Access, mo-Signalling, + mo-Data, mo-VoiceCall, mo-VideoCall, mo-SMS, rna-Update, mps-PriorityAccess, + mcs-PriorityAccess, spare1, spare2, spare3, spare4, spare5 } + +-- TAG-RESUMECAUSE-STOP +-- TAG-RLC-BEARERCONFIG-START + +RLC-BearerConfig ::= SEQUENCE { + logicalChannelIdentity LogicalChannelIdentity, + servedRadioBearer CHOICE { + srb-Identity SRB-Identity, + drb-Identity DRB-Identity + } OPTIONAL, -- Cond LCH-SetupOnly + reestablishRLC ENUMERATED {true} OPTIONAL, -- Need N + rlc-Config RLC-Config OPTIONAL, -- Cond LCH-Setup + mac-LogicalChannelConfig LogicalChannelConfig OPTIONAL, -- Cond LCH-Setup + ..., + [[ + rlc-Config-v1610 RLC-Config-v1610 OPTIONAL -- Need R + ]] +} + +-- TAG-RLC-BEARERCONFIG-STOP +-- TAG-RLC-CONFIG-START + +RLC-Config ::= CHOICE { + am SEQUENCE { + ul-AM-RLC UL-AM-RLC, + dl-AM-RLC DL-AM-RLC + }, + um-Bi-Directional SEQUENCE { + ul-UM-RLC UL-UM-RLC, + dl-UM-RLC DL-UM-RLC + }, + um-Uni-Directional-UL SEQUENCE { + ul-UM-RLC UL-UM-RLC + }, + um-Uni-Directional-DL SEQUENCE { + dl-UM-RLC DL-UM-RLC + }, + ... +} + +UL-AM-RLC ::= SEQUENCE { + sn-FieldLength SN-FieldLengthAM OPTIONAL, -- Cond Reestab + t-PollRetransmit T-PollRetransmit, + pollPDU PollPDU, + pollByte PollByte, + maxRetxThreshold ENUMERATED { t1, t2, t3, t4, t6, t8, t16, t32 } +} + +DL-AM-RLC ::= SEQUENCE { + sn-FieldLength SN-FieldLengthAM OPTIONAL, -- Cond Reestab + t-Reassembly T-Reassembly, + t-StatusProhibit T-StatusProhibit +} + +UL-UM-RLC ::= SEQUENCE { + sn-FieldLength SN-FieldLengthUM OPTIONAL -- Cond Reestab +} + +DL-UM-RLC ::= SEQUENCE { + sn-FieldLength SN-FieldLengthUM OPTIONAL, -- Cond Reestab + t-Reassembly T-Reassembly +} + +T-PollRetransmit ::= ENUMERATED { + ms5, ms10, ms15, ms20, ms25, ms30, ms35, + ms40, ms45, ms50, ms55, ms60, ms65, ms70, + ms75, ms80, ms85, ms90, ms95, ms100, ms105, + ms110, ms115, ms120, ms125, ms130, ms135, + ms140, ms145, ms150, ms155, ms160, ms165, + ms170, ms175, ms180, ms185, ms190, ms195, + ms200, ms205, ms210, ms215, ms220, ms225, + ms230, ms235, ms240, ms245, ms250, ms300, + ms350, ms400, ms450, ms500, ms800, ms1000, + ms2000, ms4000, ms1-v1610, ms2-v1610, ms3-v1610, + ms4-v1610, spare1} + + +PollPDU ::= ENUMERATED { + p4, p8, p16, p32, p64, p128, p256, p512, p1024, p2048, p4096, p6144, p8192, p12288, p16384,p20480, + p24576, p28672, p32768, p40960, p49152, p57344, p65536, infinity, spare8, spare7, spare6, spare5, spare4, + spare3, spare2, spare1} + +PollByte ::= ENUMERATED { + kB1, kB2, kB5, kB8, kB10, kB15, kB25, kB50, kB75, + kB100, kB125, kB250, kB375, kB500, kB750, kB1000, + kB1250, kB1500, kB2000, kB3000, kB4000, kB4500, + kB5000, kB5500, kB6000, kB6500, kB7000, kB7500, + mB8, mB9, mB10, mB11, mB12, mB13, mB14, mB15, + mB16, mB17, mB18, mB20, mB25, mB30, mB40, infinity, + spare20, spare19, spare18, spare17, spare16, + spare15, spare14, spare13, spare12, spare11, + spare10, spare9, spare8, spare7, spare6, spare5, + spare4, spare3, spare2, spare1} + +T-Reassembly ::= ENUMERATED { + ms0, ms5, ms10, ms15, ms20, ms25, ms30, ms35, + ms40, ms45, ms50, ms55, ms60, ms65, ms70, + ms75, ms80, ms85, ms90, ms95, ms100, ms110, + ms120, ms130, ms140, ms150, ms160, ms170, + ms180, ms190, ms200, spare1} + +T-StatusProhibit ::= ENUMERATED { + ms0, ms5, ms10, ms15, ms20, ms25, ms30, ms35, + ms40, ms45, ms50, ms55, ms60, ms65, ms70, + ms75, ms80, ms85, ms90, ms95, ms100, ms105, + ms110, ms115, ms120, ms125, ms130, ms135, + ms140, ms145, ms150, ms155, ms160, ms165, + ms170, ms175, ms180, ms185, ms190, ms195, + ms200, ms205, ms210, ms215, ms220, ms225, + ms230, ms235, ms240, ms245, ms250, ms300, + ms350, ms400, ms450, ms500, ms800, ms1000, + ms1200, ms1600, ms2000, ms2400, spare2, spare1} + +SN-FieldLengthUM ::= ENUMERATED {size6, size12} +SN-FieldLengthAM ::= ENUMERATED {size12, size18} + +RLC-Config-v1610 ::= SEQUENCE { + dl-AM-RLC-v1610 DL-AM-RLC-v1610 +} + +DL-AM-RLC-v1610 ::= SEQUENCE { + t-StatusProhibit-v1610 T-StatusProhibit-v1610 OPTIONAL, -- Need N + ... +} + +T-StatusProhibit-v1610 ::= ENUMERATED { ms1, ms2, ms3, ms4, spare4, spare3, spare2, spare1} + +-- TAG-RLC-CONFIG-STOP +-- TAG-RLF-TIMERSANDCONSTANTS-START + +RLF-TimersAndConstants ::= SEQUENCE { + t310 ENUMERATED {ms0, ms50, ms100, ms200, ms500, ms1000, ms2000, ms4000, ms6000}, + n310 ENUMERATED {n1, n2, n3, n4, n6, n8, n10, n20}, + n311 ENUMERATED {n1, n2, n3, n4, n5, n6, n8, n10}, + ..., + [[ + t311 ENUMERATED {ms1000, ms3000, ms5000, ms10000, ms15000, ms20000, ms30000} + ]] +} + +-- TAG-RLF-TIMERSANDCONSTANTS-STOP +-- TAG-RNTI-VALUE-START + +RNTI-Value ::= INTEGER (0..65535) + +-- TAG-RNTI-VALUE-STOP +-- TAG-RSRP-RANGE-START + +RSRP-Range ::= INTEGER(0..127) + +-- TAG-RSRP-RANGE-STOP +-- TAG-RSRQ-RANGE-START + +RSRQ-Range ::= INTEGER(0..127) + +-- TAG-RSRQ-RANGE-STOP +-- TAG-RSSI-RANGE-START + +RSSI-Range-r16 ::= INTEGER(0..76) + +-- TAG-RSSI-RANGE-STOP +-- TAG-SCELLINDEX-START + +SCellIndex ::= INTEGER (1..31) + +-- TAG-SCELLINDEX-STOP +-- TAG-SCHEDULINGREQUESTCONFIG-START + +SchedulingRequestConfig ::= SEQUENCE { + schedulingRequestToAddModList SEQUENCE (SIZE (1..maxNrofSR-ConfigPerCellGroup)) OF SchedulingRequestToAddMod + OPTIONAL, -- Need N + schedulingRequestToReleaseList SEQUENCE (SIZE (1..maxNrofSR-ConfigPerCellGroup)) OF SchedulingRequestId + OPTIONAL -- Need N +} + +SchedulingRequestToAddMod ::= SEQUENCE { + schedulingRequestId SchedulingRequestId, + sr-ProhibitTimer ENUMERATED {ms1, ms2, ms4, ms8, ms16, ms32, ms64, ms128} OPTIONAL, -- Need S + sr-TransMax ENUMERATED { n4, n8, n16, n32, n64, spare3, spare2, spare1} +} + + + +-- TAG-SCHEDULINGREQUESTCONFIG-STOP +-- TAG-SCHEDULINGREQUESTID-START + +SchedulingRequestId ::= INTEGER (0..7) + +-- TAG-SCHEDULINGREQUESTID-STOP +-- TAG-SCHEDULINGREQUESTRESOURCECONFIG-START + +SchedulingRequestResourceConfig ::= SEQUENCE { + schedulingRequestResourceId SchedulingRequestResourceId, + schedulingRequestID SchedulingRequestId, + periodicityAndOffset CHOICE { + sym2 NULL, + sym6or7 NULL, + sl1 NULL, -- Recurs in every slot + sl2 INTEGER (0..1), + sl4 INTEGER (0..3), + sl5 INTEGER (0..4), + sl8 INTEGER (0..7), + sl10 INTEGER (0..9), + sl16 INTEGER (0..15), + sl20 INTEGER (0..19), + sl40 INTEGER (0..39), + sl80 INTEGER (0..79), + sl160 INTEGER (0..159), + sl320 INTEGER (0..319), + sl640 INTEGER (0..639) + } OPTIONAL, -- Need M + resource PUCCH-ResourceId OPTIONAL -- Need M +} + +SchedulingRequestResourceConfigExt-v1610 ::= SEQUENCE { + phy-PriorityIndex-r16 ENUMERATED {p0, p1} OPTIONAL, -- Need M + ... +} + +-- TAG-SCHEDULINGREQUESTRESOURCECONFIG-STOP +-- TAG-SCHEDULINGREQUESTRESOURCEID-START + +SchedulingRequestResourceId ::= INTEGER (1..maxNrofSR-Resources) + +-- TAG-SCHEDULINGREQUESTRESOURCEID-STOP +-- TAG-SCRAMBLINGID-START + +ScramblingId ::= INTEGER(0..1023) + +-- TAG-SCRAMBLINGID-STOP +-- TAG-SCS-SPECIFICCARRIER-START + +SCS-SpecificCarrier ::= SEQUENCE { + offsetToCarrier INTEGER (0..2199), + subcarrierSpacing SubcarrierSpacing, + carrierBandwidth INTEGER (1..maxNrofPhysicalResourceBlocks), + ..., + [[ + txDirectCurrentLocation INTEGER (0..4095) OPTIONAL -- Need S + ]] +} + +-- TAG-SCS-SPECIFICCARRIER-STOP +-- TAG-SDAP-CONFIG-START + +SDAP-Config ::= SEQUENCE { + pdu-Session PDU-SessionID, + sdap-HeaderDL ENUMERATED {present, absent}, + sdap-HeaderUL ENUMERATED {present, absent}, + defaultDRB BOOLEAN, + mappedQoS-FlowsToAdd SEQUENCE (SIZE (1..maxNrofQFIs)) OF QFI OPTIONAL, -- Need N + mappedQoS-FlowsToRelease SEQUENCE (SIZE (1..maxNrofQFIs)) OF QFI OPTIONAL, -- Need N + ... +} + +QFI ::= INTEGER (0..maxQFI) + +PDU-SessionID ::= INTEGER (0..255) + +-- TAG-SDAP-CONFIG-STOP +-- TAG-SEARCHSPACE-START + +SearchSpace ::= SEQUENCE { + searchSpaceId SearchSpaceId, + controlResourceSetId ControlResourceSetId OPTIONAL, -- Cond SetupOnly + monitoringSlotPeriodicityAndOffset CHOICE { + sl1 NULL, + sl2 INTEGER (0..1), + sl4 INTEGER (0..3), + sl5 INTEGER (0..4), + sl8 INTEGER (0..7), + sl10 INTEGER (0..9), + sl16 INTEGER (0..15), + sl20 INTEGER (0..19), + sl40 INTEGER (0..39), + sl80 INTEGER (0..79), + sl160 INTEGER (0..159), + sl320 INTEGER (0..319), + sl640 INTEGER (0..639), + sl1280 INTEGER (0..1279), + sl2560 INTEGER (0..2559) + } OPTIONAL, -- Cond Setup + duration INTEGER (2..2559) OPTIONAL, -- Need R + monitoringSymbolsWithinSlot BIT STRING (SIZE (14)) OPTIONAL, -- Cond Setup + nrofCandidates SEQUENCE { + aggregationLevel1 ENUMERATED {n0, n1, n2, n3, n4, n5, n6, n8}, + aggregationLevel2 ENUMERATED {n0, n1, n2, n3, n4, n5, n6, n8}, + aggregationLevel4 ENUMERATED {n0, n1, n2, n3, n4, n5, n6, n8}, + aggregationLevel8 ENUMERATED {n0, n1, n2, n3, n4, n5, n6, n8}, + aggregationLevel16 ENUMERATED {n0, n1, n2, n3, n4, n5, n6, n8} + } OPTIONAL, -- Cond Setup + searchSpaceType CHOICE { + common SEQUENCE { + dci-Format0-0-AndFormat1-0 SEQUENCE { + ... + } OPTIONAL, -- Need R + dci-Format2-0 SEQUENCE { + nrofCandidates-SFI SEQUENCE { + aggregationLevel1 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel2 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel4 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel8 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel16 ENUMERATED {n1, n2} OPTIONAL -- Need R + }, + ... + } OPTIONAL, -- Need R + dci-Format2-1 SEQUENCE { + ... + } OPTIONAL, -- Need R + dci-Format2-2 SEQUENCE { + ... + } OPTIONAL, -- Need R + dci-Format2-3 SEQUENCE { + dummy1 ENUMERATED {sl1, sl2, sl4, sl5, sl8, sl10, sl16, sl20} OPTIONAL, -- Cond Setup + dummy2 ENUMERATED {n1, n2}, + ... + } OPTIONAL -- Need R + }, + ue-Specific SEQUENCE { + dci-Formats ENUMERATED {formats0-0-And-1-0, formats0-1-And-1-1}, + ..., + [[ + dci-Formats-MT-r16 ENUMERATED {formats2-5} OPTIONAL, -- Need R + dci-FormatsSL-r16 ENUMERATED {formats0-0-And-1-0, formats0-1-And-1-1, formats3-0, formats3-1, + formats3-0-And-3-1} OPTIONAL, -- Need R + dci-FormatsExt-r16 ENUMERATED {formats0-2-And-1-2, formats0-1-And-1-1And-0-2-And-1-2} + OPTIONAL -- Need R + ]] + } + } OPTIONAL -- Cond Setup2 +} + +SearchSpaceExt-r16 ::= SEQUENCE { + controlResourceSetId-r16 ControlResourceSetId-r16 OPTIONAL, -- Cond SetupOnly2 + searchSpaceType-r16 SEQUENCE { + common-r16 SEQUENCE { + dci-Format2-4-r16 SEQUENCE { + nrofCandidates-CI-r16 SEQUENCE { + aggregationLevel1-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel2-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel4-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel8-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel16-r16 ENUMERATED {n1, n2} OPTIONAL -- Need R + }, + ... + } OPTIONAL, -- Need R + dci-Format2-5-r16 SEQUENCE { + nrofCandidates-IAB-r16 SEQUENCE { + aggregationLevel1-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel2-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel4-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel8-r16 ENUMERATED {n1, n2} OPTIONAL, -- Need R + aggregationLevel16-r16 ENUMERATED {n1, n2} OPTIONAL -- Need R + }, + ... + } OPTIONAL, -- Need R + dci-Format2-6-r16 SEQUENCE { + ... + } OPTIONAL, -- Need R + ... + } + } OPTIONAL, -- Cond Setup3 + searchSpaceGroupIdList-r16 SEQUENCE (SIZE (1.. 2)) OF INTEGER (0..1) OPTIONAL, -- Need R + freqMonitorLocations-r16 BIT STRING (SIZE (5)) OPTIONAL -- Need R +} + +-- TAG-SEARCHSPACE-STOP +-- TAG-SEARCHSPACEID-START + +SearchSpaceId ::= INTEGER (0..maxNrofSearchSpaces-1) + +-- TAG-SEARCHSPACEID-STOP +-- TAG-SEARCHSPACEZERO-START + +SearchSpaceZero ::= INTEGER (0..15) + +-- TAG-SEARCHSPACEZERO-STOP +-- TAG-SECURITYALGORITHMCONFIG-START + +SecurityAlgorithmConfig ::= SEQUENCE { + cipheringAlgorithm CipheringAlgorithm, + integrityProtAlgorithm IntegrityProtAlgorithm OPTIONAL, -- Need R + ... +} + +IntegrityProtAlgorithm ::= ENUMERATED { + nia0, nia1, nia2, nia3, spare4, spare3, + spare2, spare1, ...} + +CipheringAlgorithm ::= ENUMERATED { + nea0, nea1, nea2, nea3, spare4, spare3, + spare2, spare1, ...} + +-- TAG-SECURITYALGORITHMCONFIG-STOP +-- TAG-SEMISTATICCHANNELACCESSCONFIG-START + +SemiStaticChannelAccessConfig-r16 ::= SEQUENCE { + period ENUMERATED {ms1, ms2, ms2dot5, ms4, ms5, ms10} +} + +-- TAG-SEMISTATICCHANNELACCESSCONFIG-STOP +-- TAG-SENSORLOCATIONINFO-START + +Sensor-LocationInfo-r16 ::= SEQUENCE { + sensor-MeasurementInformation-r16 OCTET STRING OPTIONAL, + sensor-MotionInformation-r16 OCTET STRING OPTIONAL, + ... +} + +-- TAG-SENSORLOCATIONINFO-STOP +-- TAG-SERVCELLINDEX-START + +ServCellIndex ::= INTEGER (0..maxNrofServingCells-1) + +-- TAG-SERVCELLINDEX-STOP +-- TAG-SERVINGCELLCONFIG-START + +ServingCellConfig ::= SEQUENCE { + tdd-UL-DL-ConfigurationDedicated TDD-UL-DL-ConfigDedicated OPTIONAL, -- Cond TDD + initialDownlinkBWP BWP-DownlinkDedicated OPTIONAL, -- Need M + downlinkBWP-ToReleaseList SEQUENCE (SIZE (1..maxNrofBWPs)) OF BWP-Id OPTIONAL, -- Need N + downlinkBWP-ToAddModList SEQUENCE (SIZE (1..maxNrofBWPs)) OF BWP-Downlink OPTIONAL, -- Need N + firstActiveDownlinkBWP-Id BWP-Id OPTIONAL, -- Cond SyncAndCellAdd + bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + spare7, spare6, spare5, spare4, spare3, spare2, spare1 } OPTIONAL, --Need R + defaultDownlinkBWP-Id BWP-Id OPTIONAL, -- Need S + uplinkConfig UplinkConfig OPTIONAL, -- Need M + supplementaryUplink UplinkConfig OPTIONAL, -- Need M + pdcch-ServingCellConfig SetupRelease { PDCCH-ServingCellConfig } OPTIONAL, -- Need M + pdsch-ServingCellConfig SetupRelease { PDSCH-ServingCellConfig } OPTIONAL, -- Need M + csi-MeasConfig SetupRelease { CSI-MeasConfig } OPTIONAL, -- Need M + sCellDeactivationTimer ENUMERATED {ms20, ms40, ms80, ms160, ms200, ms240, + ms320, ms400, ms480, ms520, ms640, ms720, + ms840, ms1280, spare2,spare1} OPTIONAL, -- Cond ServingCellWithoutPUCCH + crossCarrierSchedulingConfig CrossCarrierSchedulingConfig OPTIONAL, -- Need M + tag-Id TAG-Id, + dummy1 ENUMERATED {enabled} OPTIONAL, -- Need R + pathlossReferenceLinking ENUMERATED {spCell, sCell} OPTIONAL, -- Cond SCellOnly + servingCellMO MeasObjectId OPTIONAL, -- Cond MeasObject + ..., + [[ + lte-CRS-ToMatchAround SetupRelease { RateMatchPatternLTE-CRS } OPTIONAL, -- Need M + rateMatchPatternToAddModList SEQUENCE (SIZE (1..maxNrofRateMatchPatterns)) OF RateMatchPattern OPTIONAL, -- Need N + rateMatchPatternToReleaseList SEQUENCE (SIZE (1..maxNrofRateMatchPatterns)) OF RateMatchPatternId OPTIONAL, -- Need N + downlinkChannelBW-PerSCS-List SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier OPTIONAL -- Need S + ]], + [[ + supplementaryUplinkRelease-r16 ENUMERATED {true} OPTIONAL, -- Need N + tdd-UL-DL-ConfigurationDedicated-IAB-MT-r16 TDD-UL-DL-ConfigDedicated-IAB-MT-r16 OPTIONAL, -- Cond TDD_IAB + dormantBWP-Config-r16 SetupRelease { DormantBWP-Config-r16 } OPTIONAL, -- Need M + ca-SlotOffset-r16 CHOICE { + refSCS15kHz INTEGER (-2..2), + refSCS30KHz INTEGER (-5..5), + refSCS60KHz INTEGER (-10..10), + refSCS120KHz INTEGER (-20..20) + } OPTIONAL, -- Cond AsyncCA + dummy2 SetupRelease { DummyJ } OPTIONAL, -- Need M + intraCellGuardBandsDL-List-r16 SEQUENCE (SIZE (1..maxSCSs)) OF IntraCellGuardBandsPerSCS-r16 OPTIONAL, -- Need S + intraCellGuardBandsUL-List-r16 SEQUENCE (SIZE (1..maxSCSs)) OF IntraCellGuardBandsPerSCS-r16 OPTIONAL, -- Need S + csi-RS-ValidationWithDCI-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + lte-CRS-PatternList1-r16 SetupRelease { LTE-CRS-PatternList-r16 } OPTIONAL, -- Need M + lte-CRS-PatternList2-r16 SetupRelease { LTE-CRS-PatternList-r16 } OPTIONAL, -- Need M + crs-RateMatch-PerCORESETPoolIndex-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + enableTwoDefaultTCI-States-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + enableDefaultTCI-StatePerCoresetPoolIndex-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + enableBeamSwitchTiming-r16 ENUMERATED {true} OPTIONAL, -- Need R + cbg-TxDiffTBsProcessingType1-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + cbg-TxDiffTBsProcessingType2-r16 ENUMERATED {enabled} OPTIONAL -- Need R + ]], + [[ + directionalCollisionHandling-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + channelAccessConfig-r16 SetupRelease { ChannelAccessConfig-r16 } OPTIONAL -- Need M + ]] +} + +UplinkConfig ::= SEQUENCE { + initialUplinkBWP BWP-UplinkDedicated OPTIONAL, -- Need M + uplinkBWP-ToReleaseList SEQUENCE (SIZE (1..maxNrofBWPs)) OF BWP-Id OPTIONAL, -- Need N + uplinkBWP-ToAddModList SEQUENCE (SIZE (1..maxNrofBWPs)) OF BWP-Uplink OPTIONAL, -- Need N + firstActiveUplinkBWP-Id BWP-Id OPTIONAL, -- Cond SyncAndCellAdd + pusch-ServingCellConfig SetupRelease { PUSCH-ServingCellConfig } OPTIONAL, -- Need M + carrierSwitching SetupRelease { SRS-CarrierSwitching } OPTIONAL, -- Need M + ..., + [[ + powerBoostPi2BPSK BOOLEAN OPTIONAL, -- Need M + uplinkChannelBW-PerSCS-List SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier OPTIONAL -- Need S + ]], + [[ + enablePL-RS-UpdateForPUSCH-SRS-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + enableDefaultBeamPL-ForPUSCH0-0-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + enableDefaultBeamPL-ForPUCCH-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + enableDefaultBeamPL-ForSRS-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + uplinkTxSwitching-r16 SetupRelease { UplinkTxSwitching-r16 } OPTIONAL, -- Need M + mpr-PowerBoost-FR2-r16 ENUMERATED {true} OPTIONAL -- Need R + ]] +} + +DummyJ ::= SEQUENCE { + maxEnergyDetectionThreshold-r16 INTEGER(-85..-52), + energyDetectionThresholdOffset-r16 INTEGER (-20..-13), + ul-toDL-COT-SharingED-Threshold-r16 INTEGER (-85..-52) OPTIONAL, -- Need R + absenceOfAnyOtherTechnology-r16 ENUMERATED {true} OPTIONAL -- Need R +} + +ChannelAccessConfig-r16 ::= SEQUENCE { + energyDetectionConfig-r16 CHOICE { + maxEnergyDetectionThreshold-r16 INTEGER (-85..-52), + energyDetectionThresholdOffset-r16 INTEGER (-13..20) + } OPTIONAL, -- Need R + ul-toDL-COT-SharingED-Threshold-r16 INTEGER (-85..-52) OPTIONAL, -- Need R + absenceOfAnyOtherTechnology-r16 ENUMERATED {true} OPTIONAL -- Need R +} + +IntraCellGuardBandsPerSCS-r16 ::= SEQUENCE { + guardBandSCS-r16 SubcarrierSpacing, + intraCellGuardBands-r16 SEQUENCE (SIZE (1..4)) OF GuardBand-r16 +} + +GuardBand-r16 ::= SEQUENCE { + startCRB-r16 INTEGER (0..274), + nrofCRBs-r16 INTEGER (0..15) +} + +DormancyGroupID-r16 ::= INTEGER (0..4) + +DormantBWP-Config-r16::= SEQUENCE { + dormantBWP-Id-r16 BWP-Id OPTIONAL, -- Need M + withinActiveTimeConfig-r16 SetupRelease { WithinActiveTimeConfig-r16 } OPTIONAL, -- Need M + outsideActiveTimeConfig-r16 SetupRelease { OutsideActiveTimeConfig-r16 } OPTIONAL -- Need M +} + +WithinActiveTimeConfig-r16 ::= SEQUENCE { + firstWithinActiveTimeBWP-Id-r16 BWP-Id OPTIONAL, -- Need M + dormancyGroupWithinActiveTime-r16 DormancyGroupID-r16 OPTIONAL -- Need R +} + +OutsideActiveTimeConfig-r16 ::= SEQUENCE { + firstOutsideActiveTimeBWP-Id-r16 BWP-Id OPTIONAL, -- Need M + dormancyGroupOutsideActiveTime-r16 DormancyGroupID-r16 OPTIONAL -- Need R +} + +UplinkTxSwitching-r16 ::= SEQUENCE { + uplinkTxSwitchingPeriodLocation-r16 BOOLEAN, + uplinkTxSwitchingCarrier-r16 ENUMERATED {carrier1, carrier2} +} + +-- TAG-SERVINGCELLCONFIG-STOP +-- TAG-SERVINGCELLCONFIGCOMMON-START + +ServingCellConfigCommon ::= SEQUENCE { + physCellId PhysCellId OPTIONAL, -- Cond HOAndServCellAdd, + downlinkConfigCommon DownlinkConfigCommon OPTIONAL, -- Cond HOAndServCellAdd + uplinkConfigCommon UplinkConfigCommon OPTIONAL, -- Need M + supplementaryUplinkConfig UplinkConfigCommon OPTIONAL, -- Need S + n-TimingAdvanceOffset ENUMERATED { n0, n25600, n39936 } OPTIONAL, -- Need S + ssb-PositionsInBurst CHOICE { + shortBitmap BIT STRING (SIZE (4)), + mediumBitmap BIT STRING (SIZE (8)), + longBitmap BIT STRING (SIZE (64)) + } OPTIONAL, -- Cond AbsFreqSSB + ssb-periodicityServingCell ENUMERATED { ms5, ms10, ms20, ms40, ms80, ms160, spare2, spare1 } OPTIONAL, -- Need S + dmrs-TypeA-Position ENUMERATED {pos2, pos3}, + lte-CRS-ToMatchAround SetupRelease { RateMatchPatternLTE-CRS } OPTIONAL, -- Need M + rateMatchPatternToAddModList SEQUENCE (SIZE (1..maxNrofRateMatchPatterns)) OF RateMatchPattern OPTIONAL, -- Need N + rateMatchPatternToReleaseList SEQUENCE (SIZE (1..maxNrofRateMatchPatterns)) OF RateMatchPatternId OPTIONAL, -- Need N + ssbSubcarrierSpacing SubcarrierSpacing OPTIONAL, -- Cond HOAndServCellWithSSB + tdd-UL-DL-ConfigurationCommon TDD-UL-DL-ConfigCommon OPTIONAL, -- Cond TDD + ss-PBCH-BlockPower INTEGER (-60..50), + ..., + [[ + channelAccessMode-r16 CHOICE { + dynamic NULL, + semiStatic SemiStaticChannelAccessConfig-r16 + } OPTIONAL, -- Cond SharedSpectrum + discoveryBurstWindowLength-r16 ENUMERATED {ms0dot5, ms1, ms2, ms3, ms4, ms5} OPTIONAL, -- Need R + ssb-PositionQCL-r16 SSB-PositionQCL-Relation-r16 OPTIONAL, -- Cond SharedSpectrum + highSpeedConfig-r16 HighSpeedConfig-r16 OPTIONAL -- Need R + ]] +} + +-- TAG-SERVINGCELLCONFIGCOMMON-STOP +-- TAG-SERVINGCELLCONFIGCOMMONSIB-START + +ServingCellConfigCommonSIB ::= SEQUENCE { + downlinkConfigCommon DownlinkConfigCommonSIB, + uplinkConfigCommon UplinkConfigCommonSIB OPTIONAL, -- Need R + supplementaryUplink UplinkConfigCommonSIB OPTIONAL, -- Need R + n-TimingAdvanceOffset ENUMERATED { n0, n25600, n39936 } OPTIONAL, -- Need S + ssb-PositionsInBurst SEQUENCE { + inOneGroup BIT STRING (SIZE (8)), + groupPresence BIT STRING (SIZE (8)) OPTIONAL -- Cond FR2-Only + }, + ssb-PeriodicityServingCell ENUMERATED {ms5, ms10, ms20, ms40, ms80, ms160}, + tdd-UL-DL-ConfigurationCommon TDD-UL-DL-ConfigCommon OPTIONAL, -- Cond TDD + ss-PBCH-BlockPower INTEGER (-60..50), + ..., + [[ + channelAccessMode-r16 CHOICE { + dynamic NULL, + semiStatic SemiStaticChannelAccessConfig-r16 + } OPTIONAL, -- Cond SharedSpectrum + discoveryBurstWindowLength-r16 ENUMERATED {ms0dot5, ms1, ms2, ms3, ms4, ms5} OPTIONAL, -- Need R + highSpeedConfig-r16 HighSpeedConfig-r16 OPTIONAL -- Need R + ]] +} + +-- TAG-SERVINGCELLCONFIGCOMMONSIB-STOP +-- TAG-SHORTI-RNTI-VALUE-START + +ShortI-RNTI-Value ::= BIT STRING (SIZE(24)) + +-- TAG-SHORTI-RNTI-VALUE-STOP +-- TAG-SHORTMAC-I-START + +ShortMAC-I ::= BIT STRING (SIZE (16)) + +-- TAG-SHORTMAC-I-STOP +-- TAG-SINR-RANGE-START + +SINR-Range ::= INTEGER(0..127) + +-- TAG-SINR-RANGE-STOP +-- TAG-SI-REQUESTCONFIG-START + +SI-RequestConfig::= SEQUENCE { + rach-OccasionsSI SEQUENCE { + rach-ConfigSI RACH-ConfigGeneric, + ssb-perRACH-Occasion ENUMERATED {oneEighth, oneFourth, oneHalf, one, two, four, eight, sixteen} + } OPTIONAL, -- Need R + si-RequestPeriod ENUMERATED {one, two, four, six, eight, ten, twelve, sixteen} OPTIONAL, -- Need R + si-RequestResources SEQUENCE (SIZE (1..maxSI-Message)) OF SI-RequestResources +} + +SI-RequestResources ::= SEQUENCE { + ra-PreambleStartIndex INTEGER (0..63), + ra-AssociationPeriodIndex INTEGER (0..15) OPTIONAL, -- Need R + ra-ssb-OccasionMaskIndex INTEGER (0..15) OPTIONAL -- Need R +} + +-- TAG-SI-SCHEDULINGINFO-START + +SI-SchedulingInfo ::= SEQUENCE { + schedulingInfoList SEQUENCE (SIZE (1..maxSI-Message)) OF SchedulingInfo, + si-WindowLength ENUMERATED {s5, s10, s20, s40, s80, s160, s320, s640, s1280}, + si-RequestConfig SI-RequestConfig OPTIONAL, -- Cond MSG-1 + si-RequestConfigSUL SI-RequestConfig OPTIONAL, -- Cond SUL-MSG-1 + systemInformationAreaID BIT STRING (SIZE (24)) OPTIONAL, -- Need R + ... +} + +SchedulingInfo ::= SEQUENCE { + si-BroadcastStatus ENUMERATED {broadcasting, notBroadcasting}, + si-Periodicity ENUMERATED {rf8, rf16, rf32, rf64, rf128, rf256, rf512}, + sib-MappingInfo SIB-Mapping +} + +SIB-Mapping ::= SEQUENCE (SIZE (1..maxSIB)) OF SIB-TypeInfo + +SIB-TypeInfo ::= SEQUENCE { + type ENUMERATED {sibType2, sibType3, sibType4, sibType5, sibType6, sibType7, sibType8, sibType9, + sibType10-v1610, sibType11-v1610, sibType12-v1610, sibType13-v1610, sibType14-v1610, + spare3, spare2, spare1,... }, + valueTag INTEGER (0..31) OPTIONAL, -- Cond SIB-TYPE + areaScope ENUMERATED {true} OPTIONAL -- Need S +} + +-- TAG-SI-SCHEDULINGINFO-STOP +-- TAG-SKCOUNTER-START + +SK-Counter ::= INTEGER (0..65535) + +-- TAG-SKCOUNTER-STOP +-- TAG-SLOTFORMATCOMBINATIONSPERCELL-START + +SlotFormatCombinationsPerCell ::= SEQUENCE { + servingCellId ServCellIndex, + subcarrierSpacing SubcarrierSpacing, + subcarrierSpacing2 SubcarrierSpacing OPTIONAL, -- Need R + slotFormatCombinations SEQUENCE (SIZE (1..maxNrofSlotFormatCombinationsPerSet)) OF SlotFormatCombination + OPTIONAL, -- Need M + positionInDCI INTEGER(0..maxSFI-DCI-PayloadSize-1) OPTIONAL, -- Need M + ..., + [[ + enableConfiguredUL-r16 ENUMERATED {enabled} OPTIONAL -- Need R + ]] + +} + +SlotFormatCombination ::= SEQUENCE { + slotFormatCombinationId SlotFormatCombinationId, + slotFormats SEQUENCE (SIZE (1..maxNrofSlotFormatsPerCombination)) OF INTEGER (0..255) +} + +SlotFormatCombinationId ::= INTEGER (0..maxNrofSlotFormatCombinationsPerSet-1) + +-- TAG-SLOTFORMATCOMBINATIONSPERCELL-STOP +-- TAG-SLOTFORMATINDICATOR-START + +SlotFormatIndicator ::= SEQUENCE { + sfi-RNTI RNTI-Value, + dci-PayloadSize INTEGER (1..maxSFI-DCI-PayloadSize), + slotFormatCombToAddModList SEQUENCE (SIZE(1..maxNrofAggregatedCellsPerCellGroup)) OF SlotFormatCombinationsPerCell + OPTIONAL, -- Need N + slotFormatCombToReleaseList SEQUENCE (SIZE(1..maxNrofAggregatedCellsPerCellGroup)) OF ServCellIndex OPTIONAL, -- Need N + ..., + [[ + availableRB-SetsToAddModList-r16 SEQUENCE (SIZE(1..maxNrofAggregatedCellsPerCellGroup)) OF AvailableRB-SetsPerCell-r16 OPTIONAL, -- Need N + availableRB-SetsToReleaseList-r16 SEQUENCE (SIZE(1..maxNrofAggregatedCellsPerCellGroup)) OF ServCellIndex OPTIONAL, -- Need N + switchTriggerToAddModList-r16 SEQUENCE (SIZE(1..4)) OF SearchSpaceSwitchTrigger-r16 OPTIONAL, -- Need N + switchTriggerToReleaseList-r16 SEQUENCE (SIZE(1..4)) OF ServCellIndex OPTIONAL, -- Need N + co-DurationsPerCellToAddModList-r16 SEQUENCE (SIZE(1..maxNrofAggregatedCellsPerCellGroup)) OF CO-DurationsPerCell-r16 OPTIONAL, -- Need N + co-DurationsPerCellToReleaseList-r16 SEQUENCE (SIZE(1..maxNrofAggregatedCellsPerCellGroup)) OF ServCellIndex OPTIONAL -- Need N + ]] +} + +CO-DurationsPerCell-r16 ::= SEQUENCE { + servingCellId-r16 ServCellIndex, + positionInDCI-r16 INTEGER(0..maxSFI-DCI-PayloadSize-1), + subcarrierSpacing-r16 SubcarrierSpacing, + co-DurationList-r16 SEQUENCE (SIZE(1..64)) OF CO-Duration-r16 +} + +CO-Duration-r16 ::= INTEGER (0..1120) + +AvailableRB-SetsPerCell-r16 ::= SEQUENCE { + servingCellId-r16 ServCellIndex, + positionInDCI-r16 INTEGER(0..maxSFI-DCI-PayloadSize-1) +} + +SearchSpaceSwitchTrigger-r16 ::= SEQUENCE { + servingCellId-r16 ServCellIndex, + positionInDCI-r16 INTEGER(0..maxSFI-DCI-PayloadSize-1) +} + +-- TAG-SLOTFORMATINDICATOR-STOP +-- TAG-S-NSSAI-START + +S-NSSAI ::= CHOICE{ + sst BIT STRING (SIZE (8)), + sst-SD BIT STRING (SIZE (32)) +} + +-- TAG-S-NSSAI-STOP +-- TAG-SPEEDSTATESCALEFACTORS-START + +SpeedStateScaleFactors ::= SEQUENCE { + sf-Medium ENUMERATED {oDot25, oDot5, oDot75, lDot0}, + sf-High ENUMERATED {oDot25, oDot5, oDot75, lDot0} +} +-- TAG-SPEEDSTATESCALEFACTORS-STOP +-- TAG-SPS-CONFIG-START + +SPS-Config ::= SEQUENCE { + periodicity ENUMERATED {ms10, ms20, ms32, ms40, ms64, ms80, ms128, ms160, ms320, ms640, + spare6, spare5, spare4, spare3, spare2, spare1}, + nrofHARQ-Processes INTEGER (1..8), + n1PUCCH-AN PUCCH-ResourceId OPTIONAL, -- Need M + mcs-Table ENUMERATED {qam64LowSE} OPTIONAL, -- Need S + ..., + [[ + sps-ConfigIndex-r16 SPS-ConfigIndex-r16 OPTIONAL, -- Cond SPS-List + harq-ProcID-Offset-r16 INTEGER (0..15) OPTIONAL, -- Need R + periodicityExt-r16 INTEGER (1..5120) OPTIONAL, -- Need R + harq-CodebookID-r16 INTEGER (1..2) OPTIONAL, -- Need R + pdsch-AggregationFactor-r16 ENUMERATED {n1, n2, n4, n8 } OPTIONAL -- Need S + ]] +} + +-- TAG-SPS-CONFIG-STOP +-- TAG-SPS-CONFIGINDEX-START + +SPS-ConfigIndex-r16 ::= INTEGER (0.. maxNrofSPS-Config-r16-1) + +-- TAG-SPS-CONFIGINDEX-STOP +-- TAG-SPS-PUCCH-AN-START + +SPS-PUCCH-AN-r16 ::= SEQUENCE { + sps-PUCCH-AN-ResourceID-r16 PUCCH-ResourceId, + maxPayloadSize-r16 INTEGER (4..256) OPTIONAL -- Need R +} + +-- TAG-SPS-PUCCH-AN-STOP +-- TAG-SPS-PUCCH-AN-LIST-START + +SPS-PUCCH-AN-List-r16 ::= SEQUENCE (SIZE(1..4)) OF SPS-PUCCH-AN-r16 + +-- TAG-SPS-PUCCH-AN-LIST-STOP +-- TAG-SRB-IDENTITY-START + +SRB-Identity ::= INTEGER (1..3) + +-- TAG-SRB-IDENTITY-STOP +-- TAG-SRS-CARRIERSWITCHING-START + +SRS-CarrierSwitching ::= SEQUENCE { + srs-SwitchFromServCellIndex INTEGER (0..31) OPTIONAL, -- Need M + srs-SwitchFromCarrier ENUMERATED {sUL, nUL}, + srs-TPC-PDCCH-Group CHOICE { + typeA SEQUENCE (SIZE (1..32)) OF SRS-TPC-PDCCH-Config, + typeB SRS-TPC-PDCCH-Config + } OPTIONAL, -- Need M + monitoringCells SEQUENCE (SIZE (1..maxNrofServingCells)) OF ServCellIndex OPTIONAL, -- Need M + ... +} + +SRS-TPC-PDCCH-Config ::= SEQUENCE { + srs-CC-SetIndexlist SEQUENCE (SIZE(1..4)) OF SRS-CC-SetIndex OPTIONAL -- Need M +} + +SRS-CC-SetIndex ::= SEQUENCE { + cc-SetIndex INTEGER (0..3) OPTIONAL, -- Need M + cc-IndexInOneCC-Set INTEGER (0..7) OPTIONAL -- Need M +} + +-- TAG-SRS-CARRIERSWITCHING-STOP +-- TAG-SRS-CONFIG-START + +SRS-Config ::= SEQUENCE { + srs-ResourceSetToReleaseList SEQUENCE (SIZE(1..maxNrofSRS-ResourceSets)) OF SRS-ResourceSetId OPTIONAL, -- Need N + srs-ResourceSetToAddModList SEQUENCE (SIZE(1..maxNrofSRS-ResourceSets)) OF SRS-ResourceSet OPTIONAL, -- Need N + srs-ResourceToReleaseList SEQUENCE (SIZE(1..maxNrofSRS-Resources)) OF SRS-ResourceId OPTIONAL, -- Need N + srs-ResourceToAddModList SEQUENCE (SIZE(1..maxNrofSRS-Resources)) OF SRS-Resource OPTIONAL, -- Need N + tpc-Accumulation ENUMERATED {disabled} OPTIONAL, -- Need S + ..., + [[ + srs-RequestDCI-1-2-r16 INTEGER (1..2) OPTIONAL, -- Need S + srs-RequestDCI-0-2-r16 INTEGER (1..2) OPTIONAL, -- Need S + srs-ResourceSetToAddModListDCI-0-2-r16 SEQUENCE (SIZE(1..maxNrofSRS-ResourceSets)) OF SRS-ResourceSet OPTIONAL, -- Need N + srs-ResourceSetToReleaseListDCI-0-2-r16 SEQUENCE (SIZE(1..maxNrofSRS-ResourceSets)) OF SRS-ResourceSetId OPTIONAL, -- Need N + srs-PosResourceSetToReleaseList-r16 SEQUENCE (SIZE(1..maxNrofSRS-PosResourceSets-r16)) OF SRS-PosResourceSetId-r16 + OPTIONAL, -- Need N + srs-PosResourceSetToAddModList-r16 SEQUENCE (SIZE(1..maxNrofSRS-PosResourceSets-r16)) OF SRS-PosResourceSet-r16 OPTIONAL,-- Need N + srs-PosResourceToReleaseList-r16 SEQUENCE (SIZE(1..maxNrofSRS-PosResources-r16)) OF SRS-PosResourceId-r16 OPTIONAL,-- Need N + srs-PosResourceToAddModList-r16 SEQUENCE (SIZE(1..maxNrofSRS-PosResources-r16)) OF SRS-PosResource-r16 OPTIONAL -- Need N + ]] +} + +SRS-ResourceSet ::= SEQUENCE { + srs-ResourceSetId SRS-ResourceSetId, + srs-ResourceIdList SEQUENCE (SIZE(1..maxNrofSRS-ResourcesPerSet)) OF SRS-ResourceId OPTIONAL, -- Cond Setup + resourceType CHOICE { + aperiodic SEQUENCE { + aperiodicSRS-ResourceTrigger INTEGER (1..maxNrofSRS-TriggerStates-1), + csi-RS NZP-CSI-RS-ResourceId OPTIONAL, -- Cond NonCodebook + slotOffset INTEGER (1..32) OPTIONAL, -- Need S + ..., + [[ + aperiodicSRS-ResourceTriggerList SEQUENCE (SIZE(1..maxNrofSRS-TriggerStates-2)) + OF INTEGER (1..maxNrofSRS-TriggerStates-1) OPTIONAL -- Need M + ]] + }, + semi-persistent SEQUENCE { + associatedCSI-RS NZP-CSI-RS-ResourceId OPTIONAL, -- Cond NonCodebook + ... + }, + periodic SEQUENCE { + associatedCSI-RS NZP-CSI-RS-ResourceId OPTIONAL, -- Cond NonCodebook + ... + } + }, + usage ENUMERATED {beamManagement, codebook, nonCodebook, antennaSwitching}, + alpha Alpha OPTIONAL, -- Need S + p0 INTEGER (-202..24) OPTIONAL, -- Cond Setup + pathlossReferenceRS PathlossReferenceRS-Config OPTIONAL, -- Need M + srs-PowerControlAdjustmentStates ENUMERATED { sameAsFci2, separateClosedLoop} OPTIONAL, -- Need S + ..., + [[ + pathlossReferenceRSList-r16 SetupRelease { PathlossReferenceRSList-r16} OPTIONAL -- Need M + ]] +} + +PathlossReferenceRS-Config ::= CHOICE { + ssb-Index SSB-Index, + csi-RS-Index NZP-CSI-RS-ResourceId +} + +PathlossReferenceRSList-r16 ::= SEQUENCE (SIZE (1..maxNrofSRS-PathlossReferenceRS-r16)) OF PathlossReferenceRS-r16 + +PathlossReferenceRS-r16 ::= SEQUENCE { + srs-PathlossReferenceRS-Id-r16 SRS-PathlossReferenceRS-Id-r16, + pathlossReferenceRS-r16 PathlossReferenceRS-Config +} + +SRS-PathlossReferenceRS-Id-r16 ::= INTEGER (0..maxNrofSRS-PathlossReferenceRS-1-r16) + +SRS-PosResourceSet-r16 ::= SEQUENCE { + srs-PosResourceSetId-r16 SRS-PosResourceSetId-r16, + srs-PosResourceIdList-r16 SEQUENCE (SIZE(1..maxNrofSRS-ResourcesPerSet)) OF SRS-PosResourceId-r16 + OPTIONAL, -- Cond Setup + resourceType-r16 CHOICE { + aperiodic-r16 SEQUENCE { + aperiodicSRS-ResourceTriggerList-r16 SEQUENCE (SIZE(1..maxNrofSRS-TriggerStates-1)) + OF INTEGER (1..maxNrofSRS-TriggerStates-1) OPTIONAL, -- Need M + ... + }, + semi-persistent-r16 SEQUENCE { + ... + }, + periodic-r16 SEQUENCE { + ... + } + }, + alpha-r16 Alpha OPTIONAL, -- Need S + p0-r16 INTEGER (-202..24) OPTIONAL, -- Cond Setup + pathlossReferenceRS-Pos-r16 CHOICE { + ssb-IndexServing-r16 SSB-Index, + ssb-Ncell-r16 SSB-InfoNcell-r16, + dl-PRS-r16 DL-PRS-Info-r16 + } OPTIONAL, -- Need M + ... +} + +SRS-ResourceSetId ::= INTEGER (0..maxNrofSRS-ResourceSets-1) + +SRS-PosResourceSetId-r16 ::= INTEGER (0..maxNrofSRS-PosResourceSets-1-r16) + +SRS-Resource ::= SEQUENCE { + srs-ResourceId SRS-ResourceId, + nrofSRS-Ports ENUMERATED {port1, ports2, ports4}, + ptrs-PortIndex ENUMERATED {n0, n1 } OPTIONAL, -- Need R + transmissionComb CHOICE { + n2 SEQUENCE { + combOffset-n2 INTEGER (0..1), + cyclicShift-n2 INTEGER (0..7) + }, + n4 SEQUENCE { + combOffset-n4 INTEGER (0..3), + cyclicShift-n4 INTEGER (0..11) + } + }, + resourceMapping SEQUENCE { + startPosition INTEGER (0..5), + nrofSymbols ENUMERATED {n1, n2, n4}, + repetitionFactor ENUMERATED {n1, n2, n4} + }, + freqDomainPosition INTEGER (0..67), + freqDomainShift INTEGER (0..268), + freqHopping SEQUENCE { + c-SRS INTEGER (0..63), + b-SRS INTEGER (0..3), + b-hop INTEGER (0..3) + }, + groupOrSequenceHopping ENUMERATED { neither, groupHopping, sequenceHopping }, + resourceType CHOICE { + aperiodic SEQUENCE { + ... + }, + semi-persistent SEQUENCE { + periodicityAndOffset-sp SRS-PeriodicityAndOffset, + ... + }, + periodic SEQUENCE { + periodicityAndOffset-p SRS-PeriodicityAndOffset, + ... + } + }, + sequenceId INTEGER (0..1023), + spatialRelationInfo SRS-SpatialRelationInfo OPTIONAL, -- Need R + ..., + [[ + resourceMapping-r16 SEQUENCE { + startPosition-r16 INTEGER (0..13), + nrofSymbols-r16 ENUMERATED {n1, n2, n4}, + repetitionFactor-r16 ENUMERATED {n1, n2, n4} + } OPTIONAL -- Need R + ]] + +} + +SRS-PosResource-r16::= SEQUENCE { + srs-PosResourceId-r16 SRS-PosResourceId-r16, + transmissionComb-r16 CHOICE { + n2-r16 SEQUENCE { + combOffset-n2-r16 INTEGER (0..1), + cyclicShift-n2-r16 INTEGER (0..7) + }, + n4-r16 SEQUENCE { + combOffset-n4-16 INTEGER (0..3), + cyclicShift-n4-r16 INTEGER (0..11) + }, + n8-r16 SEQUENCE { + combOffset-n8-r16 INTEGER (0..7), + cyclicShift-n8-r16 INTEGER (0..5) + }, + ... + }, + resourceMapping-r16 SEQUENCE { + startPosition-r16 INTEGER (0..13), + nrofSymbols-r16 ENUMERATED {n1, n2, n4, n8, n12} + }, + freqDomainShift-r16 INTEGER (0..268), + freqHopping-r16 SEQUENCE { + c-SRS-r16 INTEGER (0..63), + ... + }, + groupOrSequenceHopping-r16 ENUMERATED { neither, groupHopping, sequenceHopping }, + resourceType-r16 CHOICE { + aperiodic-r16 SEQUENCE { + slotOffset-r16 INTEGER (1..32) OPTIONAL, -- Need S + ... + }, + semi-persistent-r16 SEQUENCE { + periodicityAndOffset-sp-r16 SRS-PeriodicityAndOffset-r16, + ... + }, + periodic-r16 SEQUENCE { + periodicityAndOffset-p-r16 SRS-PeriodicityAndOffset-r16, + ... + } + }, + sequenceId-r16 INTEGER (0..65535), + spatialRelationInfoPos-r16 SRS-SpatialRelationInfoPos-r16 OPTIONAL, -- Need R + ... +} + +SRS-SpatialRelationInfo ::= SEQUENCE { + servingCellId ServCellIndex OPTIONAL, -- Need S + referenceSignal CHOICE { + ssb-Index SSB-Index, + csi-RS-Index NZP-CSI-RS-ResourceId, + srs SEQUENCE { + resourceId SRS-ResourceId, + uplinkBWP BWP-Id + } + } +} + +SRS-SpatialRelationInfoPos-r16 ::= CHOICE { + servingRS-r16 SEQUENCE { + servingCellId ServCellIndex OPTIONAL, -- Need S + referenceSignal-r16 CHOICE { + ssb-IndexServing-r16 SSB-Index, + csi-RS-IndexServing-r16 NZP-CSI-RS-ResourceId, + srs-SpatialRelation-r16 SEQUENCE { + resourceSelection-r16 CHOICE { + srs-ResourceId-r16 SRS-ResourceId, + srs-PosResourceId-r16 SRS-PosResourceId-r16 + }, + uplinkBWP-r16 BWP-Id + } + } + }, + ssb-Ncell-r16 SSB-InfoNcell-r16, + dl-PRS-r16 DL-PRS-Info-r16 +} + +SSB-Configuration-r16 ::= SEQUENCE { + ssb-Freq-r16 ARFCN-ValueNR, + halfFrameIndex-r16 ENUMERATED {zero, one}, + ssbSubcarrierSpacing-r16 SubcarrierSpacing, + ssb-Periodicity-r16 ENUMERATED { ms5, ms10, ms20, ms40, ms80, ms160, spare2,spare1 } OPTIONAL, -- Need S + sfn0-Offset-r16 SEQUENCE { + sfn-Offset-r16 INTEGER (0..1023), + integerSubframeOffset-r16 INTEGER (0..9) OPTIONAL -- Need R + } OPTIONAL, -- Need R + sfn-SSB-Offset-r16 INTEGER (0..15), + ss-PBCH-BlockPower-r16 INTEGER (-60..50) OPTIONAL -- Cond Pathloss +} + +SSB-InfoNcell-r16 ::= SEQUENCE { + physicalCellId-r16 PhysCellId, + ssb-IndexNcell-r16 SSB-Index OPTIONAL, -- Need S + ssb-Configuration-r16 SSB-Configuration-r16 OPTIONAL -- Need S +} + +DL-PRS-Info-r16 ::= SEQUENCE { + dl-PRS-ID-r16 INTEGER (0..255), + dl-PRS-ResourceSetId-r16 INTEGER (0..7), + dl-PRS-ResourceId-r16 INTEGER (0..63) OPTIONAL -- Need S +} + +SRS-ResourceId ::= INTEGER (0..maxNrofSRS-Resources-1) +SRS-PosResourceId-r16 ::= INTEGER (0..maxNrofSRS-PosResources-1-r16) + +SRS-PeriodicityAndOffset ::= CHOICE { + sl1 NULL, + sl2 INTEGER(0..1), + sl4 INTEGER(0..3), + sl5 INTEGER(0..4), + sl8 INTEGER(0..7), + sl10 INTEGER(0..9), + sl16 INTEGER(0..15), + sl20 INTEGER(0..19), + sl32 INTEGER(0..31), + sl40 INTEGER(0..39), + sl64 INTEGER(0..63), + sl80 INTEGER(0..79), + sl160 INTEGER(0..159), + sl320 INTEGER(0..319), + sl640 INTEGER(0..639), + sl1280 INTEGER(0..1279), + sl2560 INTEGER(0..2559) +} + +SRS-PeriodicityAndOffset-r16 ::= CHOICE { + sl1 NULL, + sl2 INTEGER(0..1), + sl4 INTEGER(0..3), + sl5 INTEGER(0..4), + sl8 INTEGER(0..7), + sl10 INTEGER(0..9), + sl16 INTEGER(0..15), + sl20 INTEGER(0..19), + sl32 INTEGER(0..31), + sl40 INTEGER(0..39), + sl64 INTEGER(0..63), + sl80 INTEGER(0..79), + sl160 INTEGER(0..159), + sl320 INTEGER(0..319), + sl640 INTEGER(0..639), + sl1280 INTEGER(0..1279), + sl2560 INTEGER(0..2559), + sl5120 INTEGER(0..5119), + sl10240 INTEGER(0..10239), + sl40960 INTEGER(0..40959), + sl81920 INTEGER(0..81919), + ... +} + +-- TAG-SRS-CONFIG-STOP +-- TAG-SRS-RSRP-RANGE-START + +SRS-RSRP-Range-r16 ::= INTEGER(0..98) + +-- TAG-SRS-RSRP-RANGE-STOP +-- TAG-SRS-TPC-COMMANDCONFIG-START + +SRS-TPC-CommandConfig ::= SEQUENCE { + startingBitOfFormat2-3 INTEGER (1..31) OPTIONAL, -- Need R + fieldTypeFormat2-3 INTEGER (0..1) OPTIONAL, -- Need R + ..., + [[ + startingBitOfFormat2-3SUL INTEGER (1..31) OPTIONAL -- Need R + ]] +} + +-- TAG-SRS-TPC-COMMANDCONFIG-STOP +-- TAG-SSB-INDEX-START + +SSB-Index ::= INTEGER (0..maxNrofSSBs-1) + +-- TAG-SSB-INDEX-STOP +-- TAG-SSB-MTC-START + +SSB-MTC ::= SEQUENCE { + periodicityAndOffset CHOICE { + sf5 INTEGER (0..4), + sf10 INTEGER (0..9), + sf20 INTEGER (0..19), + sf40 INTEGER (0..39), + sf80 INTEGER (0..79), + sf160 INTEGER (0..159) + }, + duration ENUMERATED { sf1, sf2, sf3, sf4, sf5 } +} + +SSB-MTC2 ::= SEQUENCE { + pci-List SEQUENCE (SIZE (1..maxNrofPCIsPerSMTC)) OF PhysCellId OPTIONAL, -- Need M + periodicity ENUMERATED {sf5, sf10, sf20, sf40, sf80, spare3, spare2, spare1} +} + +SSB-MTC2-LP-r16 ::= SEQUENCE { + pci-List SEQUENCE (SIZE (1..maxNrofPCIsPerSMTC)) OF PhysCellId OPTIONAL, -- Need R + periodicity ENUMERATED {sf10, sf20, sf40, sf80, sf160, spare3, spare2, spare1} +} + +SSB-MTC3-r16 ::= SEQUENCE { + periodicityAndOffset-r16 CHOICE { + sf5-r16 INTEGER (0..4), + sf10-r16 INTEGER (0..9), + sf20-r16 INTEGER (0..19), + sf40-r16 INTEGER (0..39), + sf80-r16 INTEGER (0..79), + sf160-r16 INTEGER (0..159), + sf320-r16 INTEGER (0..319), + sf640-r16 INTEGER (0..639), + sf1280-r16 INTEGER (0..1279) + }, + duration-r16 ENUMERATED {sf1, sf2, sf3, sf4, sf5}, + pci-List-r16 SEQUENCE (SIZE (1..maxNrofPCIsPerSMTC)) OF PhysCellId OPTIONAL, -- Need M + ssb-ToMeasure-r16 SetupRelease { SSB-ToMeasure } OPTIONAL -- Need M +} + + +-- TAG-SSB-MTC-STOP +-- TAG-SSB-POSITIONQCL-RELATION-START + +SSB-PositionQCL-Relation-r16 ::= ENUMERATED {n1,n2,n4,n8} + +-- TAG-SSB-POSITIONQCL-RELATION-STOP +-- TAG-SSB-TOMEASURE-START + +SSB-ToMeasure ::= CHOICE { + shortBitmap BIT STRING (SIZE (4)), + mediumBitmap BIT STRING (SIZE (8)), + longBitmap BIT STRING (SIZE (64)) +} + +-- TAG-SSB-TOMEASURE-STOP +-- TAG-SS-RSSI-MEASUREMENT-START + +SS-RSSI-Measurement ::= SEQUENCE { + measurementSlots BIT STRING (SIZE (1..80)), + endSymbol INTEGER(0..3) +} + +-- TAG-SS-RSSI-MEASUREMENT-STOP +-- TAG-SUBCARRIERSPACING-START + +SubcarrierSpacing ::= ENUMERATED {kHz15, kHz30, kHz60, kHz120, kHz240, spare3, spare2, spare1} + +-- TAG-SUBCARRIERSPACING-STOP +-- TAG-TAG-CONFIG-START + +TAG-Config ::= SEQUENCE { + tag-ToReleaseList SEQUENCE (SIZE (1..maxNrofTAGs)) OF TAG-Id OPTIONAL, -- Need N + tag-ToAddModList SEQUENCE (SIZE (1..maxNrofTAGs)) OF TAG OPTIONAL -- Need N +} + +TAG ::= SEQUENCE { + tag-Id TAG-Id, + timeAlignmentTimer TimeAlignmentTimer, + ... +} + +TAG-Id ::= INTEGER (0..maxNrofTAGs-1) + +TimeAlignmentTimer ::= ENUMERATED {ms500, ms750, ms1280, ms1920, ms2560, ms5120, ms10240, infinity} + +-- TAG-TAG-CONFIG-STOP +-- TAG-TCI-STATE-START + +TCI-State ::= SEQUENCE { + tci-StateId TCI-StateId, + qcl-Type1 QCL-Info, + qcl-Type2 QCL-Info OPTIONAL, -- Need R + ... +} + +QCL-Info ::= SEQUENCE { + cell ServCellIndex OPTIONAL, -- Need R + bwp-Id BWP-Id OPTIONAL, -- Cond CSI-RS-Indicated + referenceSignal CHOICE { + csi-rs NZP-CSI-RS-ResourceId, + ssb SSB-Index + }, + qcl-Type ENUMERATED {typeA, typeB, typeC, typeD}, + ... +} + +-- TAG-TCI-STATE-STOP +-- TAG-TCI-STATEID-START + +TCI-StateId ::= INTEGER (0..maxNrofTCI-States-1) + +-- TAG-TCI-STATEID-STOP +-- TAG-TDD-UL-DL-CONFIGCOMMON-START + +TDD-UL-DL-ConfigCommon ::= SEQUENCE { + referenceSubcarrierSpacing SubcarrierSpacing, + pattern1 TDD-UL-DL-Pattern, + pattern2 TDD-UL-DL-Pattern OPTIONAL, -- Need R + ... +} + +TDD-UL-DL-Pattern ::= SEQUENCE { + dl-UL-TransmissionPeriodicity ENUMERATED {ms0p5, ms0p625, ms1, ms1p25, ms2, ms2p5, ms5, ms10}, + nrofDownlinkSlots INTEGER (0..maxNrofSlots), + nrofDownlinkSymbols INTEGER (0..maxNrofSymbols-1), + nrofUplinkSlots INTEGER (0..maxNrofSlots), + nrofUplinkSymbols INTEGER (0..maxNrofSymbols-1), + ..., + [[ + dl-UL-TransmissionPeriodicity-v1530 ENUMERATED {ms3, ms4} OPTIONAL -- Need R + ]] +} + +-- TAG-TDD-UL-DL-CONFIGCOMMON-STOP +-- TAG-TDD-UL-DL-CONFIGDEDICATED-START + +TDD-UL-DL-ConfigDedicated ::= SEQUENCE { + slotSpecificConfigurationsToAddModList SEQUENCE (SIZE (1..maxNrofSlots)) OF TDD-UL-DL-SlotConfig OPTIONAL, -- Need N + slotSpecificConfigurationsToReleaseList SEQUENCE (SIZE (1..maxNrofSlots)) OF TDD-UL-DL-SlotIndex OPTIONAL, -- Need N + ... +} + +TDD-UL-DL-ConfigDedicated-IAB-MT-r16::= SEQUENCE { + slotSpecificConfigurationsToAddModList-IAB-MT-r16 SEQUENCE (SIZE (1..maxNrofSlots)) OF TDD-UL-DL-SlotConfig-IAB-MT-r16 OPTIONAL, -- Need N + slotSpecificConfigurationsToReleaseList-IAB-MT-r16 SEQUENCE (SIZE (1..maxNrofSlots)) OF TDD-UL-DL-SlotIndex OPTIONAL, -- Need N + ... +} + +TDD-UL-DL-SlotConfig ::= SEQUENCE { + slotIndex TDD-UL-DL-SlotIndex, + symbols CHOICE { + allDownlink NULL, + allUplink NULL, + explicit SEQUENCE { + nrofDownlinkSymbols INTEGER (1..maxNrofSymbols-1) OPTIONAL, -- Need S + nrofUplinkSymbols INTEGER (1..maxNrofSymbols-1) OPTIONAL -- Need S + } + } +} + +TDD-UL-DL-SlotConfig-IAB-MT-r16::= SEQUENCE { + slotIndex-r16 TDD-UL-DL-SlotIndex, + symbols-IAB-MT-r16 CHOICE { + allDownlink-r16 NULL, + allUplink-r16 NULL, + explicit-r16 SEQUENCE { + nrofDownlinkSymbols-r16 INTEGER (1..maxNrofSymbols-1) OPTIONAL, -- Need S + nrofUplinkSymbols-r16 INTEGER (1..maxNrofSymbols-1) OPTIONAL -- Need S + }, + explicit-IAB-MT-r16 SEQUENCE { + nrofDownlinkSymbols-r16 INTEGER (1..maxNrofSymbols-1) OPTIONAL, -- Need S + nrofUplinkSymbols-r16 INTEGER (1..maxNrofSymbols-1) OPTIONAL -- Need S + } + } +} + +TDD-UL-DL-SlotIndex ::= INTEGER (0..maxNrofSlots-1) + +-- TAG-TDD-UL-DL-CONFIGDEDICATED-STOP +-- TAG-TRACKINGAREACODE-START + +TrackingAreaCode ::= BIT STRING (SIZE (24)) + +-- TAG-TRACKINGAREACODE-STOP +-- TAG-TRESELECTION-START + +T-Reselection ::= INTEGER (0..7) + +-- TAG-TRESELECTION-STOP +-- TAG-TIMETOTRIGGER-START + +TimeToTrigger ::= ENUMERATED { + ms0, ms40, ms64, ms80, ms100, ms128, ms160, ms256, + ms320, ms480, ms512, ms640, ms1024, ms1280, ms2560, + ms5120} + +-- TAG-TIMETOTRIGGER-STOP +-- TAG-UAC-BARRINGINFOSETINDEX-START + +UAC-BarringInfoSetIndex ::= INTEGER (1..maxBarringInfoSet) + +-- TAG-UAC-BARRINGINFOSETINDEX-STOP +-- TAG-UAC-BARRINGINFOSETLIST-START + +UAC-BarringInfoSetList ::= SEQUENCE (SIZE(1..maxBarringInfoSet)) OF UAC-BarringInfoSet + +UAC-BarringInfoSet ::= SEQUENCE { + uac-BarringFactor ENUMERATED {p00, p05, p10, p15, p20, p25, p30, p40, + p50, p60, p70, p75, p80, p85, p90, p95}, + uac-BarringTime ENUMERATED {s4, s8, s16, s32, s64, s128, s256, s512}, + uac-BarringForAccessIdentity BIT STRING (SIZE(7)) +} + +-- TAG-UAC-BARRINGINFOSETLIST-STOP +-- TAG-UAC-BARRINGPERCATLIST-START + +UAC-BarringPerCatList ::= SEQUENCE (SIZE (1..maxAccessCat-1)) OF UAC-BarringPerCat + +UAC-BarringPerCat ::= SEQUENCE { + accessCategory INTEGER (1..maxAccessCat-1), + uac-barringInfoSetIndex UAC-BarringInfoSetIndex +} + +-- TAG-UAC-BARRINGPERCATLIST-STOP +-- TAG-UAC-BARRINGPERPLMN-LIST-START + +UAC-BarringPerPLMN-List ::= SEQUENCE (SIZE (1.. maxPLMN)) OF UAC-BarringPerPLMN + +UAC-BarringPerPLMN ::= SEQUENCE { + plmn-IdentityIndex INTEGER (1..maxPLMN), + uac-ACBarringListType CHOICE{ + uac-ImplicitACBarringList SEQUENCE (SIZE(maxAccessCat-1)) OF UAC-BarringInfoSetIndex, + uac-ExplicitACBarringList UAC-BarringPerCatList + } OPTIONAL -- Need S +} + +-- TAG-UAC-BARRINGPERPLMN-LIST-STOP +-- TAG-UE-TIMERSANDCONSTANTS-START + +UE-TimersAndConstants ::= SEQUENCE { + t300 ENUMERATED {ms100, ms200, ms300, ms400, ms600, ms1000, ms1500, ms2000}, + t301 ENUMERATED {ms100, ms200, ms300, ms400, ms600, ms1000, ms1500, ms2000}, + t310 ENUMERATED {ms0, ms50, ms100, ms200, ms500, ms1000, ms2000}, + n310 ENUMERATED {n1, n2, n3, n4, n6, n8, n10, n20}, + t311 ENUMERATED {ms1000, ms3000, ms5000, ms10000, ms15000, ms20000, ms30000}, + n311 ENUMERATED {n1, n2, n3, n4, n5, n6, n8, n10}, + t319 ENUMERATED {ms100, ms200, ms300, ms400, ms600, ms1000, ms1500, ms2000}, + ... +} + +-- TAG-UE-TIMERSANDCONSTANTS-STOP +-- TAG-ULDELAYVALUECONFIG-START + +UL-DelayValueConfig-r16 ::= SEQUENCE { + delay-DRBlist SEQUENCE (SIZE(1..maxDRB)) OF DRB-Identity +} + +-- TAG-ULDELAYVALUECONFIG-STOP +-- TAG-UPLINKCANCELLATION-START + +UplinkCancellation-r16 ::= SEQUENCE { + ci-RNTI-r16 RNTI-Value, + dci-PayloadSizeForCI-r16 INTEGER (0..maxCI-DCI-PayloadSize-r16), + ci-ConfigurationPerServingCell-r16 SEQUENCE (SIZE (1..maxNrofServingCells)) OF CI-ConfigurationPerServingCell-r16, + ... +} + +CI-ConfigurationPerServingCell-r16 ::= SEQUENCE { + servingCellId ServCellIndex, + positionInDCI-r16 INTEGER (0..maxCI-DCI-PayloadSize-r16-1), + positionInDCI-ForSUL-r16 INTEGER (0..maxCI-DCI-PayloadSize-r16-1) OPTIONAL, -- Cond SUL-Only + ci-PayloadSize-r16 ENUMERATED {n1, n2, n4, n5, n7, n8, n10, n14, n16, n20, n28, n32, n35, n42, n56, n112}, + timeFrequencyRegion-r16 SEQUENCE { + timeDurationForCI-r16 ENUMERATED {n2, n4, n7, n14} OPTIONAL, -- Cond SymbolPeriodicity + timeGranularityForCI-r16 ENUMERATED {n1, n2, n4, n7, n14, n28}, + frequencyRegionForCI-r16 INTEGER (0..37949), + deltaOffset-r16 INTEGER (0..2), + ... + }, + uplinkCancellationPriority-v1610 ENUMERATED {enabled} OPTIONAL -- Need S +} + +-- TAG-UPLINKCANCELLATION-STOP +-- TAG-UPLINKCONFIGCOMMON-START + +UplinkConfigCommon ::= SEQUENCE { + frequencyInfoUL FrequencyInfoUL OPTIONAL, -- Cond InterFreqHOAndServCellAdd + initialUplinkBWP BWP-UplinkCommon OPTIONAL, -- Cond ServCellAdd + dummy TimeAlignmentTimer +} + +-- TAG-UPLINKCONFIGCOMMON-STOP +-- TAG-UPLINKCONFIGCOMMONSIB-START + +UplinkConfigCommonSIB ::= SEQUENCE { + frequencyInfoUL FrequencyInfoUL-SIB, + initialUplinkBWP BWP-UplinkCommon, + timeAlignmentTimerCommon TimeAlignmentTimer +} + +-- TAG-UPLINKCONFIGCOMMONSIB-STOP +-- TAG-UPLINKTXDIRECTCURRENTLIST-START + +UplinkTxDirectCurrentList ::= SEQUENCE (SIZE (1..maxNrofServingCells)) OF UplinkTxDirectCurrentCell + +UplinkTxDirectCurrentCell ::= SEQUENCE { + servCellIndex ServCellIndex, + uplinkDirectCurrentBWP SEQUENCE (SIZE (1..maxNrofBWPs)) OF UplinkTxDirectCurrentBWP, + ..., + [[ + uplinkDirectCurrentBWP-SUL SEQUENCE (SIZE (1..maxNrofBWPs)) OF UplinkTxDirectCurrentBWP OPTIONAL + ]] +} + +UplinkTxDirectCurrentBWP ::= SEQUENCE { + bwp-Id BWP-Id, + shift7dot5kHz BOOLEAN, + txDirectCurrentLocation INTEGER (0..3301) +} + +-- TAG-UPLINKTXDIRECTCURRENTLIST-STOP +-- TAG-UPLINKTXDIRECTCURRENTTWOCARRIERLIST-START + +UplinkTxDirectCurrentTwoCarrierList-r16 ::= SEQUENCE (SIZE (1..maxNrofTxDC-TwoCarrier-r16)) OF UplinkTxDirectCurrentTwoCarrier-r16 + +UplinkTxDirectCurrentTwoCarrier-r16 ::= SEQUENCE { + carrierOneInfo-r16 UplinkTxDirectCurrentCarrierInfo-r16, + carrierTwoInfo-r16 UplinkTxDirectCurrentCarrierInfo-r16, + singlePA-TxDirectCurrent-r16 UplinkTxDirectCurrentTwoCarrierInfo-r16, + secondPA-TxDirectCurrent-r16 UplinkTxDirectCurrentTwoCarrierInfo-r16 OPTIONAL +} + +UplinkTxDirectCurrentCarrierInfo-r16 ::= SEQUENCE { + servCellIndex-r16 ServCellIndex, + servCellInfo-r16 CHOICE { + bwp-Id-r16 BWP-Id, + deactivatedCarrier-r16 ENUMERATED {deactivated} + } +} + +UplinkTxDirectCurrentTwoCarrierInfo-r16 ::= SEQUENCE { + referenceCarrierIndex-r16 ServCellIndex, + shift7dot5kHz-r16 BOOLEAN, + txDirectCurrentLocation-r16 INTEGER (0..3301) +} + +-- TAG-UPLINKTXDIRECTCURRENTTWOCARRIERLIST-STOP +-- TAG-ZP-CSI-RS-RESOURCE-START + +ZP-CSI-RS-Resource ::= SEQUENCE { + zp-CSI-RS-ResourceId ZP-CSI-RS-ResourceId, + resourceMapping CSI-RS-ResourceMapping, + periodicityAndOffset CSI-ResourcePeriodicityAndOffset OPTIONAL, --Cond PeriodicOrSemiPersistent + ... +} + +ZP-CSI-RS-ResourceId ::= INTEGER (0..maxNrofZP-CSI-RS-Resources-1) + +-- TAG-ZP-CSI-RS-RESOURCE-STOP +-- TAG-ZP-CSI-RS-RESOURCESET-START + +ZP-CSI-RS-ResourceSet ::= SEQUENCE { + zp-CSI-RS-ResourceSetId ZP-CSI-RS-ResourceSetId, + zp-CSI-RS-ResourceIdList SEQUENCE (SIZE(1..maxNrofZP-CSI-RS-ResourcesPerSet)) OF ZP-CSI-RS-ResourceId, + ... +} + +-- TAG-ZP-CSI-RS-RESOURCESET-STOP +-- TAG-ZP-CSI-RS-RESOURCESETID-START + +ZP-CSI-RS-ResourceSetId ::= INTEGER (0..maxNrofZP-CSI-RS-ResourceSets-1) + +-- TAG-ZP-CSI-RS-RESOURCESETID-STOP +-- TAG-ACCESSSTRATUMRELEASE-START + +AccessStratumRelease ::= ENUMERATED { + rel15, rel16, spare6, spare5, spare4, spare3, spare2, spare1, ... } + +-- TAG-ACCESSSTRATUMRELEASE-STOP +-- TAG-BANDCOMBINATIONLIST-START + +BandCombinationList ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination + +BandCombinationList-v1540 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1540 + +BandCombinationList-v1550 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1550 + +BandCombinationList-v1560 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1560 + +BandCombinationList-v1570 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1570 + +BandCombinationList-v1580 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1580 + +BandCombinationList-v1590 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1590 + +BandCombinationList-v1610 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1610 + +BandCombinationList-v1630 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1630 + +BandCombinationList-v1640 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-v1640 + +BandCombinationList-UplinkTxSwitch-r16 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-UplinkTxSwitch-r16 + +BandCombinationList-UplinkTxSwitch-v1630 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-UplinkTxSwitch-v1630 + +BandCombinationList-UplinkTxSwitch-v1640 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombination-UplinkTxSwitch-v1640 + +BandCombination ::= SEQUENCE { + bandList SEQUENCE (SIZE (1..maxSimultaneousBands)) OF BandParameters, + featureSetCombination FeatureSetCombinationId, + ca-ParametersEUTRA CA-ParametersEUTRA OPTIONAL, + ca-ParametersNR CA-ParametersNR OPTIONAL, + mrdc-Parameters MRDC-Parameters OPTIONAL, + supportedBandwidthCombinationSet BIT STRING (SIZE (1..32)) OPTIONAL, + powerClass-v1530 ENUMERATED {pc2} OPTIONAL +} + +BandCombination-v1540::= SEQUENCE { + bandList-v1540 SEQUENCE (SIZE (1..maxSimultaneousBands)) OF BandParameters-v1540, + ca-ParametersNR-v1540 CA-ParametersNR-v1540 OPTIONAL +} + +BandCombination-v1550 ::= SEQUENCE { + ca-ParametersNR-v1550 CA-ParametersNR-v1550 +} +BandCombination-v1560::= SEQUENCE { + ne-DC-BC ENUMERATED {supported} OPTIONAL, + ca-ParametersNRDC CA-ParametersNRDC OPTIONAL, + ca-ParametersEUTRA-v1560 CA-ParametersEUTRA-v1560 OPTIONAL, + ca-ParametersNR-v1560 CA-ParametersNR-v1560 OPTIONAL +} + +BandCombination-v1570 ::= SEQUENCE { + ca-ParametersEUTRA-v1570 CA-ParametersEUTRA-v1570 +} + +BandCombination-v1580 ::= SEQUENCE { + mrdc-Parameters-v1580 MRDC-Parameters-v1580 +} + +BandCombination-v1590::= SEQUENCE { + supportedBandwidthCombinationSetIntraENDC BIT STRING (SIZE (1..32)) OPTIONAL, + mrdc-Parameters-v1590 MRDC-Parameters-v1590 +} + +BandCombination-v1610 ::= SEQUENCE { + bandList-v1610 SEQUENCE (SIZE (1..maxSimultaneousBands)) OF BandParameters-v1610 OPTIONAL, + ca-ParametersNR-v1610 CA-ParametersNR-v1610 OPTIONAL, + ca-ParametersNRDC-v1610 CA-ParametersNRDC-v1610 OPTIONAL, + powerClass-v1610 ENUMERATED {pc1dot5} OPTIONAL, + powerClassNRPart-r16 ENUMERATED {pc1, pc2, pc3, pc5} OPTIONAL, + featureSetCombinationDAPS-r16 FeatureSetCombinationId OPTIONAL, + mrdc-Parameters-v1620 MRDC-Parameters-v1620 OPTIONAL +} + +BandCombination-v1630 ::= SEQUENCE { + ca-ParametersNR-v1630 CA-ParametersNR-v1630 OPTIONAL, + ca-ParametersNRDC-v1630 CA-ParametersNRDC-v1630 OPTIONAL, + mrdc-Parameters-v1630 MRDC-Parameters-v1630 OPTIONAL, + supportedTxBandCombListPerBC-Sidelink-r16 BIT STRING (SIZE (1..maxBandComb)) OPTIONAL, + supportedRxBandCombListPerBC-Sidelink-r16 BIT STRING (SIZE (1..maxBandComb)) OPTIONAL, + scalingFactorTxSidelink-r16 SEQUENCE (SIZE (1..maxBandComb)) OF ScalingFactorSidelink-r16 OPTIONAL, + scalingFactorRxSidelink-r16 SEQUENCE (SIZE (1..maxBandComb)) OF ScalingFactorSidelink-r16 OPTIONAL +} + +BandCombination-v1640 ::= SEQUENCE { + ca-ParametersNR-v1640 CA-ParametersNR-v1640 OPTIONAL, + ca-ParametersNRDC-v1640 CA-ParametersNRDC-v1640 OPTIONAL +} + +BandCombination-UplinkTxSwitch-r16 ::= SEQUENCE { + bandCombination-r16 BandCombination, + bandCombination-v1540 BandCombination-v1540 OPTIONAL, + bandCombination-v1560 BandCombination-v1560 OPTIONAL, + bandCombination-v1570 BandCombination-v1570 OPTIONAL, + bandCombination-v1580 BandCombination-v1580 OPTIONAL, + bandCombination-v1590 BandCombination-v1590 OPTIONAL, + bandCombination-v1610 BandCombination-v1610 OPTIONAL, + supportedBandPairListNR-r16 SEQUENCE (SIZE (1..maxULTxSwitchingBandPairs)) OF ULTxSwitchingBandPair-r16, + uplinkTxSwitching-OptionSupport-r16 ENUMERATED {switchedUL, dualUL, both} OPTIONAL, + uplinkTxSwitching-PowerBoosting-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +BandCombination-UplinkTxSwitch-v1630 ::= SEQUENCE { + bandCombination-v1630 BandCombination-v1630 OPTIONAL +} + +BandCombination-UplinkTxSwitch-v1640 ::= SEQUENCE { + bandCombination-v1640 BandCombination-v1640 OPTIONAL +} + +ULTxSwitchingBandPair-r16 ::= SEQUENCE { + bandIndexUL1-r16 INTEGER(1..maxSimultaneousBands), + bandIndexUL2-r16 INTEGER(1..maxSimultaneousBands), + uplinkTxSwitchingPeriod-r16 ENUMERATED {n35us, n140us, n210us}, + uplinkTxSwitching-DL-Interruption-r16 BIT STRING (SIZE(1..maxSimultaneousBands)) OPTIONAL +} + +BandParameters ::= CHOICE { + eutra SEQUENCE { + bandEUTRA FreqBandIndicatorEUTRA, + ca-BandwidthClassDL-EUTRA CA-BandwidthClassEUTRA OPTIONAL, + ca-BandwidthClassUL-EUTRA CA-BandwidthClassEUTRA OPTIONAL + }, + nr SEQUENCE { + bandNR FreqBandIndicatorNR, + ca-BandwidthClassDL-NR CA-BandwidthClassNR OPTIONAL, + ca-BandwidthClassUL-NR CA-BandwidthClassNR OPTIONAL + } +} + +BandParameters-v1540 ::= SEQUENCE { + srs-CarrierSwitch CHOICE { + nr SEQUENCE { + srs-SwitchingTimesListNR SEQUENCE (SIZE (1..maxSimultaneousBands)) OF SRS-SwitchingTimeNR + }, + eutra SEQUENCE { + srs-SwitchingTimesListEUTRA SEQUENCE (SIZE (1..maxSimultaneousBands)) OF SRS-SwitchingTimeEUTRA + } + } OPTIONAL, + srs-TxSwitch SEQUENCE { + supportedSRS-TxPortSwitch ENUMERATED {t1r2, t1r4, t2r4, t1r4-t2r4, t1r1, t2r2, t4r4, notSupported}, + txSwitchImpactToRx INTEGER (1..32) OPTIONAL, + txSwitchWithAnotherBand INTEGER (1..32) OPTIONAL + } OPTIONAL +} + +BandParameters-v1610 ::= SEQUENCE { + srs-TxSwitch-v1610 SEQUENCE { + supportedSRS-TxPortSwitch-v1610 ENUMERATED {t1r1-t1r2, t1r1-t1r2-t1r4, t1r1-t1r2-t2r2-t2r4, t1r1-t1r2-t2r2-t1r4-t2r4, + t1r1-t2r2, t1r1-t2r2-t4r4} + } OPTIONAL +} + +ScalingFactorSidelink-r16 ::= ENUMERATED {f0p4, f0p75, f0p8, f1} + +-- TAG-BANDCOMBINATIONLIST-STOP +-- TAG-BANDCOMBINATIONLISTSIDELINKEUTRANR-START + +BandCombinationListSidelinkEUTRA-NR-r16 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombinationParametersSidelinkEUTRA-NR-r16 + +BandCombinationListSidelinkEUTRA-NR-v1630 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombinationParametersSidelinkEUTRA-NR-v1630 + +BandCombinationParametersSidelinkEUTRA-NR-r16 ::= SEQUENCE (SIZE (1..maxSimultaneousBands)) OF BandParametersSidelinkEUTRA-NR-r16 + +BandCombinationParametersSidelinkEUTRA-NR-v1630 ::= SEQUENCE (SIZE (1..maxSimultaneousBands)) OF BandParametersSidelinkEUTRA-NR-v1630 + +BandParametersSidelinkEUTRA-NR-r16 ::= CHOICE { + eutra SEQUENCE { + bandParametersSidelinkEUTRA1-r16 OCTET STRING OPTIONAL, + bandParametersSidelinkEUTRA2-r16 OCTET STRING OPTIONAL + }, + nr SEQUENCE { + bandParametersSidelinkNR-r16 BandParametersSidelink-r16 + } +} + +BandParametersSidelinkEUTRA-NR-v1630 ::= CHOICE { + eutra NULL, + nr SEQUENCE { + tx-Sidelink-r16 ENUMERATED {supported} OPTIONAL, + rx-Sidelink-r16 ENUMERATED {supported} OPTIONAL, + sl-CrossCarrierScheduling-r16 ENUMERATED {supported} OPTIONAL + } +} + +BandParametersSidelink-r16 ::= SEQUENCE { + freqBandSidelink-r16 FreqBandIndicatorNR +} + +-- TAG-BANDCOMBINATIONLISTSIDELINKEUTRANR-STOP +-- TAG-CA-BANDWIDTHCLASSEUTRA-START + +CA-BandwidthClassEUTRA ::= ENUMERATED {a, b, c, d, e, f, ...} + +-- TAG-CA-BANDWIDTHCLASSEUTRA-STOP +-- TAG-CA-BANDWIDTHCLASSNR-START + +CA-BandwidthClassNR ::= ENUMERATED {a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, ...} + +-- TAG-CA-BANDWIDTHCLASSNR-STOP +-- TAG-CA-PARAMETERSEUTRA-START + +CA-ParametersEUTRA ::= SEQUENCE { + multipleTimingAdvance ENUMERATED {supported} OPTIONAL, + simultaneousRx-Tx ENUMERATED {supported} OPTIONAL, + supportedNAICS-2CRS-AP BIT STRING (SIZE (1..8)) OPTIONAL, + additionalRx-Tx-PerformanceReq ENUMERATED {supported} OPTIONAL, + ue-CA-PowerClass-N ENUMERATED {class2} OPTIONAL, + supportedBandwidthCombinationSetEUTRA-v1530 BIT STRING (SIZE (1..32)) OPTIONAL, + ... +} + +CA-ParametersEUTRA-v1560 ::= SEQUENCE { + fd-MIMO-TotalWeightedLayers INTEGER (2..128) OPTIONAL +} + +CA-ParametersEUTRA-v1570 ::= SEQUENCE { + dl-1024QAM-TotalWeightedLayers INTEGER (0..10) OPTIONAL +} + +-- TAG-CA-PARAMETERSEUTRA-STOP +-- TAG-CA-PARAMETERSNR-START + +CA-ParametersNR ::= SEQUENCE { + dummy ENUMERATED {supported} OPTIONAL, + parallelTxSRS-PUCCH-PUSCH ENUMERATED {supported} OPTIONAL, + parallelTxPRACH-SRS-PUCCH-PUSCH ENUMERATED {supported} OPTIONAL, + simultaneousRxTxInterBandCA ENUMERATED {supported} OPTIONAL, + simultaneousRxTxSUL ENUMERATED {supported} OPTIONAL, + diffNumerologyAcrossPUCCH-Group ENUMERATED {supported} OPTIONAL, + diffNumerologyWithinPUCCH-GroupSmallerSCS ENUMERATED {supported} OPTIONAL, + supportedNumberTAG ENUMERATED {n2, n3, n4} OPTIONAL, + ... +} + +CA-ParametersNR-v1540 ::= SEQUENCE { + simultaneousSRS-AssocCSI-RS-AllCC INTEGER (5..32) OPTIONAL, + csi-RS-IM-ReceptionForFeedbackPerBandComb SEQUENCE { + maxNumberSimultaneousNZP-CSI-RS-ActBWP-AllCC INTEGER (1..64) OPTIONAL, + totalNumberPortsSimultaneousNZP-CSI-RS-ActBWP-AllCC INTEGER (2..256) OPTIONAL + } OPTIONAL, + simultaneousCSI-ReportsAllCC INTEGER (5..32) OPTIONAL, + dualPA-Architecture ENUMERATED {supported} OPTIONAL +} + +CA-ParametersNR-v1550 ::= SEQUENCE { + dummy ENUMERATED {supported} OPTIONAL +} + +CA-ParametersNR-v1560 ::= SEQUENCE { + diffNumerologyWithinPUCCH-GroupLargerSCS ENUMERATED {supported} OPTIONAL +} + +CA-ParametersNR-v1610 ::= SEQUENCE { + -- R1 9-3: Parallel MsgA and SRS/PUCCH/PUSCH transmissions across CCs in inter-band CA + parallelTxMsgA-SRS-PUCCH-PUSCH-r16 ENUMERATED {supported} OPTIONAL, + -- R1 9-4: MsgA operation in a band combination including SUL + msgA-SUL-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-9c: Joint search space group switching across multiple cells + jointSearchSpaceGroupSwitchingAcrossCells-r16 ENUMERATED {supported} OPTIONAL, + -- R1 14-5: Half-duplex UE behaviour in TDD CA for same SCS + half-DuplexTDD-CA-SameSCS-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-4: SCell dormancy within active time + scellDormancyWithinActiveTime-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-4a: SCell dormancy outside active time + scellDormancyOutsideActiveTime-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-6: Cross-carrier A-CSI RS triggering with different SCS + crossCarrierA-CSI-trigDiffSCS-r16 ENUMERATED {higherA-CSI-SCS,lowerA-CSI-SCS,both} OPTIONAL, + -- R1 18-6a: Default QCL assumption for cross-carrier A-CSI-RS triggering + defaultQCL-CrossCarrierA-CSI-Trig-r16 ENUMERATED {diffOnly, both} OPTIONAL, + -- R1 18-7: CA with non-aligned frame boundaries for inter-band CA + interCA-NonAlignedFrame-r16 ENUMERATED {supported} OPTIONAL, + simul-SRS-Trans-BC-r16 ENUMERATED {n2} OPTIONAL, + interFreqDAPS-r16 SEQUENCE { + interFreqAsyncDAPS-r16 ENUMERATED {supported} OPTIONAL, + interFreqDiffSCS-DAPS-r16 ENUMERATED {supported} OPTIONAL, + interFreqMultiUL-TransmissionDAPS-r16 ENUMERATED {supported} OPTIONAL, + interFreqSemiStaticPowerSharingDAPS-Mode1-r16 ENUMERATED {supported} OPTIONAL, + interFreqSemiStaticPowerSharingDAPS-Mode2-r16 ENUMERATED {supported} OPTIONAL, + interFreqDynamicPowerSharingDAPS-r16 ENUMERATED {short, long} OPTIONAL, + interFreqUL-TransCancellationDAPS-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + codebookParametersPerBC-r16 CodebookParameters-v1610 OPTIONAL, + -- R1 16-2a-10 Value of R for BD/CCE + blindDetectFactor-r16 INTEGER (1..2) OPTIONAL, + -- R1 11-2a: Capability on the number of CCs for monitoring a maximum number of BDs and non-overlapped CCEs per span when configured + -- with DL CA with Rel-16 PDCCH monitoring capability on all the serving cells + pdcch-MonitoringCA-r16 SEQUENCE { + maxNumberOfMonitoringCC-r16 INTEGER (2..16), + supportedSpanArrangement-r16 ENUMERATED {alignedOnly, alignedAndNonAligned} + } OPTIONAL, + -- R1 11-2c: Number of carriers for CCE/BD scaling with DL CA with mix of Rel. 16 and Rel. 15 PDCCH monitoring capabilities on + -- different carriers + pdcch-BlindDetectionCA-Mixed-r16 SEQUENCE { + pdcch-BlindDetectionCA1-r16 INTEGER (1..15), + pdcch-BlindDetectionCA2-r16 INTEGER (1..15), + supportedSpanArrangement-r16 ENUMERATED {alignedOnly, alignedAndNonAligned} + } OPTIONAL, + -- R1 11-2d: Capability on the number of CCs for monitoring a maximum number of BDs and non-overlapped CCEs per span for MCG and for + -- SCG when configured for NR-DC operation with Rel-16 PDCCH monitoring capability on all the serving cells + pdcch-BlindDetectionMCG-UE-r16 INTEGER (1..14) OPTIONAL, + pdcch-BlindDetectionSCG-UE-r16 INTEGER (1..14) OPTIONAL, + -- R1 11-2e: Number of carriers for CCE/BD scaling for MCG and for SCG when configured for NR-DC operation with mix of Rel. 16 and + -- Rel. 15 PDCCH monitoring capabilities on different carriers + pdcch-BlindDetectionMCG-UE-Mixed-r16 SEQUENCE { + pdcch-BlindDetectionMCG-UE1-r16 INTEGER (0..15), + pdcch-BlindDetectionMCG-UE2-r16 INTEGER (0..15) + } OPTIONAL, + pdcch-BlindDetectionSCG-UE-Mixed-r16 SEQUENCE { + pdcch-BlindDetectionSCG-UE1-r16 INTEGER (0..15), + pdcch-BlindDetectionSCG-UE2-r16 INTEGER (0..15) + } OPTIONAL, + -- R1 18-5 cross-carrier scheduling with different SCS in DL CA + crossCarrierSchedulingDL-DiffSCS-r16 ENUMERATED {low-to-high, high-to-low, both} OPTIONAL, + -- R1 18-5a Default QCL assumption for cross-carrier scheduling + crossCarrierSchedulingDefaultQCL-r16 ENUMERATED {diff-only, both} OPTIONAL, + -- R1 18-5b cross-carrier scheduling with different SCS in UL CA + crossCarrierSchedulingUL-DiffSCS-r16 ENUMERATED {low-to-high, high-to-low, both} OPTIONAL, + -- R1 13.19a Simultaneous positioning SRS and MIMO SRS transmission for a given BC + simul-SRS-MIMO-Trans-BC-r16 ENUMERATED {n2} OPTIONAL, + -- R1 16-3a, 16-3a-1, 16-3b, 16-3b-1: New Individual Codebook + codebookParametersAdditionPerBC-r16 CodebookParametersAdditionPerBC-r16 OPTIONAL, + -- R1 16-8: Mixed codebook + codebookComboParametersAdditionPerBC-r16 CodebookComboParametersAdditionPerBC-r16 OPTIONAL +} + +CA-ParametersNR-v1630 ::= SEQUENCE { + -- R1 22-5b: Simultaneous transmission of SRS for antenna switching and SRS for CB/NCB /BM for inter-band UL CA + -- R1 22-5d: Simultaneous transmission of SRS for antenna switching for inter-band UL CA + simulTX-SRS-AntSwitchingInterBandUL-CA-r16 SimulSRS-ForAntennaSwitching-r16 OPTIONAL, + -- R4 8-5: supported beam management type for inter-band CA + beamManagementType-r16 ENUMERATED {ibm, cbm} OPTIONAL, + -- R4 7-3a: UL frequency separation class with aggregate BW and Gap BW + intraBandFreqSeparationUL-AggBW-GapBW-r16 ENUMERATED {classI, classII, classIII} OPTIONAL, + -- RAN 89: Case B in case of Inter-band CA with non-aligned frame boundaries + interCA-NonAlignedFrame-B-r16 ENUMERATED {supported} OPTIONAL +} + +CA-ParametersNR-v1640 ::= SEQUENCE { + -- R4 7-5: Support of reporting UL Tx DC locations for uplink intra-band CA. + uplinkTxDC-TwoCarrierReport-r16 ENUMERATED {supported} OPTIONAL, + -- RAN 22-6: Support of up to 3 different numerologies in the same NR PUCCH group for NR part of EN-DC, NGEN-DC, NE-DC and NR-CA + -- where UE is not configured with two NR PUCCH groups + maxUpTo3Diff-NumerologiesConfigSinglePUCCH-grp-r16 PUCCH-Grp-CarrierTypes-r16 OPTIONAL, + -- RAN 22-6a: Support of up to 4 different numerologies in the same NR PUCCH group for NR part of EN-DC, NGEN-DC, NE-DC and NR-CA + -- where UE is not configured with two NR PUCCH groups + maxUpTo4Diff-NumerologiesConfigSinglePUCCH-grp-r16 PUCCH-Grp-CarrierTypes-r16 OPTIONAL, + -- RAN 22-7: Support two PUCCH groups for NR-CA with 3 or more bands with at least two carrier types + twoPUCCH-Grp-ConfigurationsList-r16 SEQUENCE (SIZE (1..maxTwoPUCCH-Grp-ConfigList-r16)) OF TwoPUCCH-Grp-Configurations-r16 OPTIONAL, + -- R1 22-7a: Different numerology across NR PUCCH groups + diffNumerologyAcrossPUCCH-Group-CarrierTypes-r16 ENUMERATED {supported} OPTIONAL, + -- R1 22-7b: Different numerologies across NR carriers within the same NR PUCCH group, with PUCCH on a carrier of smaller SCS + diffNumerologyWithinPUCCH-GroupSmallerSCS-CarrierTypes-r16 ENUMERATED {supported} OPTIONAL, + -- R1 22-7c: Different numerologies across NR carriers within the same NR PUCCH group, with PUCCH on a carrier of larger SCS + diffNumerologyWithinPUCCH-GroupLargerSCS-CarrierTypes-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-2f: add the replicated FGs of 11-2a/c with restriction for non-aligned span case + -- with DL CA with Rel-16 PDCCH monitoring capability on all the serving cells + pdcch-MonitoringCA-NonAlignedSpan-r16 INTEGER (2..16) OPTIONAL, + -- R1 11-2g: add the replicated FGs of 11-2a/c with restriction for non-aligned span case + pdcch-BlindDetectionCA-Mixed-NonAlignedSpan-r16 SEQUENCE { + pdcch-BlindDetectionCA1-r16 INTEGER (1..15), + pdcch-BlindDetectionCA2-r16 INTEGER (1..15) + } OPTIONAL +} + +SimulSRS-ForAntennaSwitching-r16 ::= SEQUENCE { + supportSRS-xTyR-xLessThanY-r16 ENUMERATED {supported} OPTIONAL, + supportSRS-xTyR-xEqualToY-r16 ENUMERATED {supported} OPTIONAL, + supportSRS-AntennaSwitching-r16 ENUMERATED {supported} OPTIONAL +} + +TwoPUCCH-Grp-Configurations-r16 ::= SEQUENCE { + pucch-PrimaryGroupMapping-r16 TwoPUCCH-Grp-ConfigParams-r16, + pucch-SecondaryGroupMapping-r16 TwoPUCCH-Grp-ConfigParams-r16 +} + +TwoPUCCH-Grp-ConfigParams-r16 ::= SEQUENCE { + pucch-GroupMapping-r16 PUCCH-Grp-CarrierTypes-r16, + pucch-TX-r16 PUCCH-Grp-CarrierTypes-r16 +} + +PUCCH-Grp-CarrierTypes-r16 ::= SEQUENCE { + fr1-NonSharedTDD-r16 ENUMERATED {supported} OPTIONAL, + fr1-SharedTDD-r16 ENUMERATED {supported} OPTIONAL, + fr1-NonSharedFDD-r16 ENUMERATED {supported} OPTIONAL, + fr2-r16 ENUMERATED {supported} OPTIONAL +} + +-- TAG-CA-PARAMETERSNR-STOP +-- TAG-CA-PARAMETERS-NRDC-START + +CA-ParametersNRDC ::= SEQUENCE { + ca-ParametersNR-ForDC CA-ParametersNR OPTIONAL, + ca-ParametersNR-ForDC-v1540 CA-ParametersNR-v1540 OPTIONAL, + ca-ParametersNR-ForDC-v1550 CA-ParametersNR-v1550 OPTIONAL, + ca-ParametersNR-ForDC-v1560 CA-ParametersNR-v1560 OPTIONAL, + featureSetCombinationDC FeatureSetCombinationId OPTIONAL +} + +CA-ParametersNRDC-v1610 ::= SEQUENCE { + -- R1 18-1: Semi-static power sharing mode1 between MCG and SCG cells of same FR for NR dual connectivity + intraFR-NR-DC-PwrSharingMode1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-1a: Semi-static power sharing mode 2 between MCG and SCG cells of same FR for NR dual connectivity + intraFR-NR-DC-PwrSharingMode2-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-1b: Dynamic power sharing between MCG and SCG cells of same FR for NR dual connectivity + intraFR-NR-DC-DynamicPwrSharing-r16 ENUMERATED {short, long} OPTIONAL, + asyncNRDC-r16 ENUMERATED {supported} OPTIONAL +} + +CA-ParametersNRDC-v1630 ::= SEQUENCE { + ca-ParametersNR-ForDC-v1610 CA-ParametersNR-v1610 OPTIONAL, + ca-ParametersNR-ForDC-v1630 CA-ParametersNR-v1630 OPTIONAL +} + +CA-ParametersNRDC-v1640 ::= SEQUENCE { + ca-ParametersNR-ForDC-v1640 CA-ParametersNR-v1640 OPTIONAL +} + +-- TAG-CA-PARAMETERS-NRDC-STOP +-- TAG-CARRIERAGGREGATIONVARIANT-START + +CarrierAggregationVariant ::= SEQUENCE { + fr1fdd-FR1TDD-CA-SpCellOnFR1FDD ENUMERATED {supported} OPTIONAL, + fr1fdd-FR1TDD-CA-SpCellOnFR1TDD ENUMERATED {supported} OPTIONAL, + fr1fdd-FR2TDD-CA-SpCellOnFR1FDD ENUMERATED {supported} OPTIONAL, + fr1fdd-FR2TDD-CA-SpCellOnFR2TDD ENUMERATED {supported} OPTIONAL, + fr1tdd-FR2TDD-CA-SpCellOnFR1TDD ENUMERATED {supported} OPTIONAL, + fr1tdd-FR2TDD-CA-SpCellOnFR2TDD ENUMERATED {supported} OPTIONAL, + fr1fdd-FR1TDD-FR2TDD-CA-SpCellOnFR1FDD ENUMERATED {supported} OPTIONAL, + fr1fdd-FR1TDD-FR2TDD-CA-SpCellOnFR1TDD ENUMERATED {supported} OPTIONAL, + fr1fdd-FR1TDD-FR2TDD-CA-SpCellOnFR2TDD ENUMERATED {supported} OPTIONAL +} + +-- TAG-CARRIERAGGREGATIONVARIANT-STOP +-- TAG-CODEBOOKPARAMETERS-START + +CodebookParameters ::= SEQUENCE { + type1 SEQUENCE { + singlePanel SEQUENCE { + supportedCSI-RS-ResourceList SEQUENCE (SIZE (1.. maxNrofCSI-RS-Resources)) OF SupportedCSI-RS-Resource, + modes ENUMERATED {mode1, mode1andMode2}, + maxNumberCSI-RS-PerResourceSet INTEGER (1..8) + }, + multiPanel SEQUENCE { + supportedCSI-RS-ResourceList SEQUENCE (SIZE (1.. maxNrofCSI-RS-Resources)) OF SupportedCSI-RS-Resource, + modes ENUMERATED {mode1, mode2, both}, + nrofPanels ENUMERATED {n2, n4}, + maxNumberCSI-RS-PerResourceSet INTEGER (1..8) + } OPTIONAL + }, + type2 SEQUENCE { + supportedCSI-RS-ResourceList SEQUENCE (SIZE (1.. maxNrofCSI-RS-Resources)) OF SupportedCSI-RS-Resource, + parameterLx INTEGER (2..4), + amplitudeScalingType ENUMERATED {wideband, widebandAndSubband}, + amplitudeSubsetRestriction ENUMERATED {supported} OPTIONAL + } OPTIONAL, + type2-PortSelection SEQUENCE { + supportedCSI-RS-ResourceList SEQUENCE (SIZE (1.. maxNrofCSI-RS-Resources)) OF SupportedCSI-RS-Resource, + parameterLx INTEGER (2..4), + amplitudeScalingType ENUMERATED {wideband, widebandAndSubband} + } OPTIONAL +} + +CodebookParameters-v1610 ::= SEQUENCE { + supportedCSI-RS-ResourceListAlt-r16 SEQUENCE { + type1-SinglePanel-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-Resources)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) OPTIONAL, + type1-MultiPanel-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-Resources)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) OPTIONAL, + type2-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-Resources)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) OPTIONAL, + type2-PortSelection-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-Resources)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) OPTIONAL + } OPTIONAL +} + +CodebookParametersAddition-r16 ::= SEQUENCE { + etype2-r16 SEQUENCE { + -- R1 16-3a Regular eType 2 R=1 + etype2R1-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF + INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + }, + -- R1 16-3a-1 Regular eType 2 R=2 + etype2R2-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF + INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + -- R1 16-3a-2: Support of parameter combinations 7-8 + paramComb7-8-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-3a-3: Support of rank 3,4 + rank3-4-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-3a-4: CBSR with soft amplitude restriction + amplitudeSubsetRestriction-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + etype2-PS-r16 SEQUENCE { + -- R1 16-3b Regular eType 2 R=1 PortSelection + etype2R1-PortSelection-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF + INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + }, + -- R1 16-3b-1 Regular eType 2 R=2 PortSelection + etype2R2-PortSelection-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF + INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + -- R1 16-3b-2: Support of rank 3,4 + rank3-4-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL +} + +CodebookComboParametersAddition-r16 ::= SEQUENCE { + -- R1 16-8 Mixed codebook types + type1SP-Type2-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1SP-Type2PS-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1SP-eType2R1-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1SP-eType2R2-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1SP-eType2R1PS-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1SP-eType2R2PS-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1SP-Type2-Type2PS-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1MP-Type2-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1MP-Type2PS-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1MP-eType2R1-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1MP-eType2R2-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1MP-eType2R1PS-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1MP-eType2R2PS-null-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL, + type1MP-Type2-Type2PS-r16 SEQUENCE { + supportedCSI-RS-ResourceListAdd-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + } OPTIONAL +} + +CodebookParametersAdditionPerBC-r16::= SEQUENCE { + -- R1 16-3a Regular eType 2 R=1 + etype2R1-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + -- R1 16-3a-1 Regular eType 2 R=2 + etype2R2-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + -- R1 16-3b Regular eType 2 R=1 PortSelection + etype2R1-PortSelection-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + -- R1 16-3b-1 Regular eType 2 R=2 PortSelection + etype2R2-PortSelection-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL +} + +CodebookComboParametersAdditionPerBC-r16::= SEQUENCE { + -- R1 16-8 Mixed codebook types + type1SP-Type2-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1SP-Type2PS-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1SP-eType2R1-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1SP-eType2R2-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1SP-eType2R1PS-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1SP-eType2R2PS-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1SP-Type2-Type2PS-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1MP-Type2-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1MP-Type2PS-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1MP-eType2R1-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1MP-eType2R2-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1MP-eType2R1PS-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1MP-eType2R2PS-null-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL, + type1MP-Type2-Type2PS-r16 SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesExt-r16)) OF INTEGER (0..maxNrofCSI-RS-ResourcesAlt-1-r16) + OPTIONAL +} + +CodebookVariantsList-r16 ::= SEQUENCE (SIZE (1..maxNrofCSI-RS-ResourcesAlt-r16)) OF SupportedCSI-RS-Resource + +SupportedCSI-RS-Resource ::= SEQUENCE { + maxNumberTxPortsPerResource ENUMERATED {p2, p4, p8, p12, p16, p24, p32}, + maxNumberResourcesPerBand INTEGER (1..64), + totalNumberTxPortsPerBand INTEGER (2..256) +} + +-- TAG-CODEBOOKPARAMETERS-STOP +-- TAG-FEATURESETCOMBINATION-START + +FeatureSetCombination ::= SEQUENCE (SIZE (1..maxSimultaneousBands)) OF FeatureSetsPerBand + +FeatureSetsPerBand ::= SEQUENCE (SIZE (1..maxFeatureSetsPerBand)) OF FeatureSet + +FeatureSet ::= CHOICE { + eutra SEQUENCE { + downlinkSetEUTRA FeatureSetEUTRA-DownlinkId, + uplinkSetEUTRA FeatureSetEUTRA-UplinkId + }, + nr SEQUENCE { + downlinkSetNR FeatureSetDownlinkId, + uplinkSetNR FeatureSetUplinkId + } +} + +-- TAG-FEATURESETCOMBINATION-STOP +-- TAG-FEATURESETCOMBINATIONID-START + +FeatureSetCombinationId ::= INTEGER (0.. maxFeatureSetCombinations) + +-- TAG-FEATURESETCOMBINATIONID-STOP +-- TAG-FEATURESETDOWNLINK-START + +FeatureSetDownlink ::= SEQUENCE { + featureSetListPerDownlinkCC SEQUENCE (SIZE (1..maxNrofServingCells)) OF FeatureSetDownlinkPerCC-Id, + + intraBandFreqSeparationDL FreqSeparationClass OPTIONAL, + scalingFactor ENUMERATED {f0p4, f0p75, f0p8} OPTIONAL, + dummy8 ENUMERATED {supported} OPTIONAL, + scellWithoutSSB ENUMERATED {supported} OPTIONAL, + csi-RS-MeasSCellWithoutSSB ENUMERATED {supported} OPTIONAL, + dummy1 ENUMERATED {supported} OPTIONAL, + type1-3-CSS ENUMERATED {supported} OPTIONAL, + pdcch-MonitoringAnyOccasions ENUMERATED {withoutDCI-Gap, withDCI-Gap} OPTIONAL, + dummy2 ENUMERATED {supported} OPTIONAL, + ue-SpecificUL-DL-Assignment ENUMERATED {supported} OPTIONAL, + searchSpaceSharingCA-DL ENUMERATED {supported} OPTIONAL, + timeDurationForQCL SEQUENCE { + scs-60kHz ENUMERATED {s7, s14, s28} OPTIONAL, + scs-120kHz ENUMERATED {s14, s28} OPTIONAL + } OPTIONAL, + pdsch-ProcessingType1-DifferentTB-PerSlot SEQUENCE { + scs-15kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL, + scs-30kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL, + scs-60kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL, + scs-120kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL + } OPTIONAL, + dummy3 DummyA OPTIONAL, + dummy4 SEQUENCE (SIZE (1.. maxNrofCodebooks)) OF DummyB OPTIONAL, + dummy5 SEQUENCE (SIZE (1.. maxNrofCodebooks)) OF DummyC OPTIONAL, + dummy6 SEQUENCE (SIZE (1.. maxNrofCodebooks)) OF DummyD OPTIONAL, + dummy7 SEQUENCE (SIZE (1.. maxNrofCodebooks)) OF DummyE OPTIONAL +} + +FeatureSetDownlink-v1540 ::= SEQUENCE { + oneFL-DMRS-TwoAdditionalDMRS-DL ENUMERATED {supported} OPTIONAL, + additionalDMRS-DL-Alt ENUMERATED {supported} OPTIONAL, + twoFL-DMRS-TwoAdditionalDMRS-DL ENUMERATED {supported} OPTIONAL, + oneFL-DMRS-ThreeAdditionalDMRS-DL ENUMERATED {supported} OPTIONAL, + pdcch-MonitoringAnyOccasionsWithSpanGap SEQUENCE { + scs-15kHz ENUMERATED {set1, set2, set3} OPTIONAL, + scs-30kHz ENUMERATED {set1, set2, set3} OPTIONAL, + scs-60kHz ENUMERATED {set1, set2, set3} OPTIONAL, + scs-120kHz ENUMERATED {set1, set2, set3} OPTIONAL + } OPTIONAL, + pdsch-SeparationWithGap ENUMERATED {supported} OPTIONAL, + pdsch-ProcessingType2 SEQUENCE { + scs-15kHz ProcessingParameters OPTIONAL, + scs-30kHz ProcessingParameters OPTIONAL, + scs-60kHz ProcessingParameters OPTIONAL + } OPTIONAL, + pdsch-ProcessingType2-Limited SEQUENCE { + differentTB-PerSlot-SCS-30kHz ENUMERATED {upto1, upto2, upto4, upto7} + } OPTIONAL, + dl-MCS-TableAlt-DynamicIndication ENUMERATED {supported} OPTIONAL +} + +FeatureSetDownlink-v15a0 ::= SEQUENCE { + supportedSRS-Resources SRS-Resources OPTIONAL +} + +FeatureSetDownlink-v1610 ::= SEQUENCE { + -- R1 22-4e/4f/4g/4h: CBG based reception for DL with unicast PDSCH(s) per slot per CC with UE processing time Capability 1 + cbgPDSCH-ProcessingType1-DifferentTB-PerSlot-r16 SEQUENCE { + scs-15kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL, + scs-30kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL, + scs-60kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL, + scs-120kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL + } OPTIONAL, + + -- R1 22-3e/3f/3g/3h: CBG based reception for DL with unicast PDSCH(s) per slot per CC with UE processing time Capability 2 + cbgPDSCH-ProcessingType2-DifferentTB-PerSlot-r16 SEQUENCE { + scs-15kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL, + scs-30kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL, + scs-60kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL, + scs-120kHz-r16 ENUMERATED {one, upto2, upto4, upto7} OPTIONAL + } OPTIONAL, + intraFreqDAPS-r16 SEQUENCE { + intraFreqDiffSCS-DAPS-r16 ENUMERATED {supported} OPTIONAL, + intraFreqAsyncDAPS-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + intraBandFreqSeparationDL-v1620 FreqSeparationClassDL-v1620 OPTIONAL, + intraBandFreqSeparationDL-Only-r16 FreqSeparationClassDL-Only-r16 OPTIONAL, + + -- R1 11-2: Rel-16 PDCCH monitoring capability + pdcch-Monitoring-r16 SEQUENCE { + pdsch-ProcessingType1-r16 SEQUENCE { + scs-15kHz-r16 PDCCH-MonitoringOccasions-r16 OPTIONAL, + scs-30kHz-r16 PDCCH-MonitoringOccasions-r16 OPTIONAL + } OPTIONAL, + pdsch-ProcessingType2-r16 SEQUENCE { + scs-15kHz-r16 PDCCH-MonitoringOccasions-r16 OPTIONAL, + scs-30kHz-r16 PDCCH-MonitoringOccasions-r16 OPTIONAL + } OPTIONAL + } OPTIONAL, + + -- R1 11-2b: Mix of Rel. 16 PDCCH monitoring capability and Rel. 15 PDCCH monitoring capability on different carriers + pdcch-MonitoringMixed-r16 ENUMERATED {supported} OPTIONAL, + + -- R1 18-5c: Processing up to X unicast DCI scheduling for DL per scheduled CC + crossCarrierSchedulingProcessing-DiffSCS-r16 SEQUENCE { + scs-15kHz-120kHz-r16 ENUMERATED {n1,n2,n4} OPTIONAL, + scs-15kHz-60kHz-r16 ENUMERATED {n1,n2,n4} OPTIONAL, + scs-30kHz-120kHz-r16 ENUMERATED {n1,n2,n4} OPTIONAL, + scs-15kHz-30kHz-r16 ENUMERATED {n2} OPTIONAL, + scs-30kHz-60kHz-r16 ENUMERATED {n2} OPTIONAL, + scs-60kHz-120kHz-r16 ENUMERATED {n2} OPTIONAL + } OPTIONAL, + + -- R1 16-2b-1: Support of single-DCI based SDM scheme + singleDCI-SDM-scheme-r16 ENUMERATED {supported} OPTIONAL +} + +PDCCH-MonitoringOccasions-r16 ::= SEQUENCE { + period7span3-r16 ENUMERATED {supported} OPTIONAL, + period4span3-r16 ENUMERATED {supported} OPTIONAL, + period2span2-r16 ENUMERATED {supported} OPTIONAL +} + +DummyA ::= SEQUENCE { + maxNumberNZP-CSI-RS-PerCC INTEGER (1..32), + maxNumberPortsAcrossNZP-CSI-RS-PerCC ENUMERATED {p2, p4, p8, p12, p16, p24, p32, p40, p48, p56, p64, p72, p80, + p88, p96, p104, p112, p120, p128, p136, p144, p152, p160, p168, + p176, p184, p192, p200, p208, p216, p224, p232, p240, p248, p256}, + maxNumberCS-IM-PerCC ENUMERATED {n1, n2, n4, n8, n16, n32}, + maxNumberSimultaneousCSI-RS-ActBWP-AllCC ENUMERATED {n5, n6, n7, n8, n9, n10, n12, n14, n16, n18, n20, n22, n24, n26, + n28, n30, n32, n34, n36, n38, n40, n42, n44, n46, n48, n50, n52, + n54, n56, n58, n60, n62, n64}, + totalNumberPortsSimultaneousCSI-RS-ActBWP-AllCC ENUMERATED {p8, p12, p16, p24, p32, p40, p48, p56, p64, p72, p80, + p88, p96, p104, p112, p120, p128, p136, p144, p152, p160, p168, + p176, p184, p192, p200, p208, p216, p224, p232, p240, p248, p256} +} + +DummyB ::= SEQUENCE { + maxNumberTxPortsPerResource ENUMERATED {p2, p4, p8, p12, p16, p24, p32}, + maxNumberResources INTEGER (1..64), + totalNumberTxPorts INTEGER (2..256), + supportedCodebookMode ENUMERATED {mode1, mode1AndMode2}, + maxNumberCSI-RS-PerResourceSet INTEGER (1..8) +} + +DummyC ::= SEQUENCE { + maxNumberTxPortsPerResource ENUMERATED {p8, p16, p32}, + maxNumberResources INTEGER (1..64), + totalNumberTxPorts INTEGER (2..256), + supportedCodebookMode ENUMERATED {mode1, mode2, both}, + supportedNumberPanels ENUMERATED {n2, n4}, + maxNumberCSI-RS-PerResourceSet INTEGER (1..8) +} + +DummyD ::= SEQUENCE { + maxNumberTxPortsPerResource ENUMERATED {p4, p8, p12, p16, p24, p32}, + maxNumberResources INTEGER (1..64), + totalNumberTxPorts INTEGER (2..256), + parameterLx INTEGER (2..4), + amplitudeScalingType ENUMERATED {wideband, widebandAndSubband}, + amplitudeSubsetRestriction ENUMERATED {supported} OPTIONAL, + maxNumberCSI-RS-PerResourceSet INTEGER (1..8) +} + +DummyE ::= SEQUENCE { + maxNumberTxPortsPerResource ENUMERATED {p4, p8, p12, p16, p24, p32}, + maxNumberResources INTEGER (1..64), + totalNumberTxPorts INTEGER (2..256), + parameterLx INTEGER (2..4), + amplitudeScalingType ENUMERATED {wideband, widebandAndSubband}, + maxNumberCSI-RS-PerResourceSet INTEGER (1..8) +} + +-- TAG-FEATURESETDOWNLINK-STOP +-- TAG-FEATURESETDOWNLINKID-START + +FeatureSetDownlinkId ::= INTEGER (0..maxDownlinkFeatureSets) + +-- TAG-FEATURESETDOWNLINKID-STOP +-- TAG-FEATURESETDOWNLINKPERCC-START + +FeatureSetDownlinkPerCC ::= SEQUENCE { + supportedSubcarrierSpacingDL SubcarrierSpacing, + supportedBandwidthDL SupportedBandwidth, + channelBW-90mhz ENUMERATED {supported} OPTIONAL, + maxNumberMIMO-LayersPDSCH MIMO-LayersDL OPTIONAL, + supportedModulationOrderDL ModulationOrder OPTIONAL +} + +FeatureSetDownlinkPerCC-v1620 ::= SEQUENCE { + -- R1 16-2a: Mulit-DCI based multi-TRP + multiDCI-MultiTRP-r16 MultiDCI-MultiTRP-r16 OPTIONAL, + -- R1 16-2b-3: Support of single-DCI based FDMSchemeB + supportFDM-SchemeB-r16 ENUMERATED {supported} OPTIONAL +} + +MultiDCI-MultiTRP-r16 ::= SEQUENCE { + maxNumberCORESET-r16 ENUMERATED {n2, n3, n4, n5}, + maxNumberCORESETPerPoolIndex-r16 INTEGER (1..3), + maxNumberUnicastPDSCH-PerPool-r16 ENUMERATED {n1, n2, n3, n4, n7} +} + +-- TAG-FEATURESETDOWNLINKPERCC-STOP +-- TAG-FEATURESETDOWNLINKPERCC-ID-START + +FeatureSetDownlinkPerCC-Id ::= INTEGER (1..maxPerCC-FeatureSets) + +-- TAG-FEATURESETDOWNLINKPERCC-ID-STOP +-- TAG-FEATURESETEUTRADOWNLINKID-START + +FeatureSetEUTRA-DownlinkId ::= INTEGER (0..maxEUTRA-DL-FeatureSets) + +-- TAG-FEATURESETEUTRADOWNLINKID-STOP +-- TAG-FEATURESETEUTRAUPLINKID-START + +FeatureSetEUTRA-UplinkId ::= INTEGER (0..maxEUTRA-UL-FeatureSets) + +-- TAG-FEATURESETEUTRAUPLINKID-STOP +-- TAG-FEATURESETS-START + +FeatureSets ::= SEQUENCE { + featureSetsDownlink SEQUENCE (SIZE (1..maxDownlinkFeatureSets)) OF FeatureSetDownlink OPTIONAL, + featureSetsDownlinkPerCC SEQUENCE (SIZE (1..maxPerCC-FeatureSets)) OF FeatureSetDownlinkPerCC OPTIONAL, + featureSetsUplink SEQUENCE (SIZE (1..maxUplinkFeatureSets)) OF FeatureSetUplink OPTIONAL, + featureSetsUplinkPerCC SEQUENCE (SIZE (1..maxPerCC-FeatureSets)) OF FeatureSetUplinkPerCC OPTIONAL, + ..., + [[ + featureSetsDownlink-v1540 SEQUENCE (SIZE (1..maxDownlinkFeatureSets)) OF FeatureSetDownlink-v1540 OPTIONAL, + featureSetsUplink-v1540 SEQUENCE (SIZE (1..maxUplinkFeatureSets)) OF FeatureSetUplink-v1540 OPTIONAL, + featureSetsUplinkPerCC-v1540 SEQUENCE (SIZE (1..maxPerCC-FeatureSets)) OF FeatureSetUplinkPerCC-v1540 OPTIONAL + ]], + [[ + featureSetsDownlink-v15a0 SEQUENCE (SIZE (1..maxDownlinkFeatureSets)) OF FeatureSetDownlink-v15a0 OPTIONAL + ]], + [[ + featureSetsDownlink-v1610 SEQUENCE (SIZE (1..maxDownlinkFeatureSets)) OF FeatureSetDownlink-v1610 OPTIONAL, + featureSetsUplink-v1610 SEQUENCE (SIZE (1..maxUplinkFeatureSets)) OF FeatureSetUplink-v1610 OPTIONAL, + featureSetDownlinkPerCC-v1620 SEQUENCE (SIZE (1..maxPerCC-FeatureSets)) OF FeatureSetDownlinkPerCC-v1620 OPTIONAL + ]], + [[ + featureSetsUplink-v1630 SEQUENCE (SIZE (1..maxUplinkFeatureSets)) OF FeatureSetUplink-v1630 OPTIONAL + ]], + [[ + featureSetsUplink-v1640 SEQUENCE (SIZE (1..maxUplinkFeatureSets)) OF FeatureSetUplink-v1640 OPTIONAL + ]] +} + +-- TAG-FEATURESETS-STOP +-- TAG-FEATURESETUPLINK-START + +FeatureSetUplink ::= SEQUENCE { + featureSetListPerUplinkCC SEQUENCE (SIZE (1.. maxNrofServingCells)) OF FeatureSetUplinkPerCC-Id, + scalingFactor ENUMERATED {f0p4, f0p75, f0p8} OPTIONAL, + dummy3 ENUMERATED {supported} OPTIONAL, + intraBandFreqSeparationUL FreqSeparationClass OPTIONAL, + searchSpaceSharingCA-UL ENUMERATED {supported} OPTIONAL, + dummy1 DummyI OPTIONAL, + supportedSRS-Resources SRS-Resources OPTIONAL, + twoPUCCH-Group ENUMERATED {supported} OPTIONAL, + dynamicSwitchSUL ENUMERATED {supported} OPTIONAL, + simultaneousTxSUL-NonSUL ENUMERATED {supported} OPTIONAL, + pusch-ProcessingType1-DifferentTB-PerSlot SEQUENCE { + scs-15kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL, + scs-30kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL, + scs-60kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL, + scs-120kHz ENUMERATED {upto2, upto4, upto7} OPTIONAL + } OPTIONAL, + dummy2 DummyF OPTIONAL +} + +FeatureSetUplink-v1540 ::= SEQUENCE { + zeroSlotOffsetAperiodicSRS ENUMERATED {supported} OPTIONAL, + pa-PhaseDiscontinuityImpacts ENUMERATED {supported} OPTIONAL, + pusch-SeparationWithGap ENUMERATED {supported} OPTIONAL, + pusch-ProcessingType2 SEQUENCE { + scs-15kHz ProcessingParameters OPTIONAL, + scs-30kHz ProcessingParameters OPTIONAL, + scs-60kHz ProcessingParameters OPTIONAL + } OPTIONAL, + ul-MCS-TableAlt-DynamicIndication ENUMERATED {supported} OPTIONAL +} + +FeatureSetUplink-v1610 ::= SEQUENCE { + -- R1 11-5: PUsCH repetition Type B + pusch-RepetitionTypeB-r16 SEQUENCE { + maxNumberPUSCH-Tx-r16 ENUMERATED {n2, n3, n4, n7, n8, n12}, + hoppingScheme-r16 ENUMERATED {interSlotHopping, interRepetitionHopping, both} + } OPTIONAL, + -- R1 11-7: UL cancelation scheme for self-carrier + ul-CancellationSelfCarrier-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-7a: UL cancelation scheme for cross-carrier + ul-CancellationCrossCarrier-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-5c: The maximum number of SRS resources in one SRS resource set with usage set to 'codebook' for Mode 2 + ul-FullPwrMode2-MaxSRS-ResInSet-r16 ENUMERATED {n1, n2, n4} OPTIONAL, + + -- R1 22-4a/4b/4c/4d: CBG based transmission for UL with unicast PUSCH(s) per slot per CC with UE processing time Capability 1 + cbgPUSCH-ProcessingType1-DifferentTB-PerSlot-r16 SEQUENCE { + scs-15kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL, + scs-30kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL, + scs-60kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL, + scs-120kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL + } OPTIONAL, + + -- R1 22-3a/3b/3c/3d: CBG based transmission for UL with unicast PUSCH(s) per slot per CC with UE processing time Capability 2 + cbgPUSCH-ProcessingType2-DifferentTB-PerSlot-r16 SEQUENCE { + scs-15kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL, + scs-30kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL, + scs-60kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL, + scs-120kHz-r16 ENUMERATED {one-pusch, upto2, upto4, upto7} OPTIONAL + } OPTIONAL, + supportedSRS-PosResources-r16 SRS-AllPosResources-r16 OPTIONAL, + intraFreqDAPS-UL-r16 SEQUENCE { + dummy ENUMERATED {supported} OPTIONAL, + intraFreqTwoTAGs-DAPS-r16 ENUMERATED {supported} OPTIONAL, + dummy1 ENUMERATED {supported} OPTIONAL, + dummy2 ENUMERATED {supported} OPTIONAL, + dummy3 ENUMERATED {short, long} OPTIONAL + } OPTIONAL, + intraBandFreqSeparationUL-v1620 FreqSeparationClassUL-v1620 OPTIONAL, + + -- R1 11-3: More than one PUCCH for HARQ-ACK transmission within a slot + multiPUCCH-r16 SEQUENCE { + sub-SlotConfig-NCP-r16 ENUMERATED {set1, set2} OPTIONAL, + sub-SlotConfig-ECP-r16 ENUMERATED {set1, set2} OPTIONAL + } OPTIONAL, + -- R1 11-3c: 2 PUCCH of format 0 or 2 for a single 7*2-symbol subslot based HARQ-ACK codebook + twoPUCCH-Type1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-3d: 2 PUCCH of format 0 or 2 for a single 2*7-symbol subslot based HARQ-ACK codebook + twoPUCCH-Type2-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-3e: 1 PUCCH format 0 or 2 and 1 PUCCH format 1, 3 or 4 in the same subslot for a single 2*7-symbol HARQ-ACK codebooks + twoPUCCH-Type3-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-3f: 2 PUCCH transmissions in the same subslot for a single 2*7-symbol HARQ-ACK codebooks which are not covered by 11-3d and + -- 11-3e + twoPUCCH-Type4-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-3g: SR/HARQ-ACK multiplexing once per subslot using a PUCCH (or HARQ-ACK piggybacked on a PUSCH) when SR/HARQ-ACK + -- are supposed to be sent with different starting symbols in a subslot + mux-SR-HARQ-ACK-r16 ENUMERATED {supported} OPTIONAL, + dummy1 ENUMERATED {supported} OPTIONAL, + dummy2 ENUMERATED {supported} OPTIONAL, + -- R1 11-4c: 2 PUCCH of format 0 or 2 for two HARQ-ACK codebooks with one 7*2-symbol sub-slot based HARQ-ACK codebook + twoPUCCH-Type5-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-4d: 2 PUCCH of format 0 or 2 in consecutive symbols for two HARQ-ACK codebooks with one 2*7-symbol sub-slot based HARQ-ACK + -- codebook + twoPUCCH-Type6-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-4e: 2 PUCCH of format 0 or 2 for two subslot based HARQ-ACK codebooks + twoPUCCH-Type7-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-4f: 1 PUCCH format 0 or 2 and 1 PUCCH format 1, 3 or 4 in the same subslot for HARQ-ACK codebooks with one 2*7-symbol + -- subslot based HARQ-ACK codebook + twoPUCCH-Type8-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-4g: 1 PUCCH format 0 or 2 and 1 PUCCH format 1, 3 or 4 in the same subslot for two subslot based HARQ-ACK codebooks + twoPUCCH-Type9-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-4h: 2 PUCCH transmissions in the same subslot for two HARQ-ACK codebooks with one 2*7-symbol subslot which are not covered + -- by 11-4c and 11-4e + twoPUCCH-Type10-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-4i: 2 PUCCH transmissions in the same subslot for two subslot based HARQ-ACK codebooks which are not covered by 11-4d and + -- 11-4f + twoPUCCH-Type11-r16 ENUMERATED {supported} OPTIONAL, + -- R1 12-1: UL intra-UE multiplexing/prioritization of overlapping channel/signals with two priority levels in physical layer + ul-IntraUE-Mux-r16 SEQUENCE { + pusch-PreparationLowPriority-r16 ENUMERATED {sym0, sym1, sym2}, + pusch-PreparationHighPriority-r16 ENUMERATED {sym0, sym1, sym2} + } OPTIONAL, + -- R1 16-5a: Supported UL full power transmission mode of fullpower + ul-FullPwrMode-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-5d: Processing up to X unicast DCI scheduling for UL per scheduled CC + crossCarrierSchedulingProcessing-DiffSCS-r16 SEQUENCE { + scs-15kHz-120kHz-r16 ENUMERATED {n1,n2,n4} OPTIONAL, + scs-15kHz-60kHz-r16 ENUMERATED {n1,n2,n4} OPTIONAL, + scs-30kHz-120kHz-r16 ENUMERATED {n1,n2,n4} OPTIONAL, + scs-15kHz-30kHz-r16 ENUMERATED {n2} OPTIONAL, + scs-30kHz-60kHz-r16 ENUMERATED {n2} OPTIONAL, + scs-60kHz-120kHz-r16 ENUMERATED {n2} OPTIONAL + } OPTIONAL, + -- R1 16-5b: Supported UL full power transmission mode of fullpowerMode1 + ul-FullPwrMode1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-5c-2: Ports configuration for Mode 2 + ul-FullPwrMode2-SRSConfig-diffNumSRSPorts-r16 ENUMERATED {p1-2, p1-4, p1-2-4} OPTIONAL, + -- R1 16-5c-3: TPMI group for Mode 2 + ul-FullPwrMode2-TPMIGroup-r16 SEQUENCE { + twoPorts-r16 BIT STRING(SIZE(2)) OPTIONAL, + fourPortsNonCoherent-r16 ENUMERATED{g0, g1, g2, g3} OPTIONAL, + fourPortsPartialCoherent-r16 ENUMERATED{g0, g1, g2, g3, g4, g5, g6} OPTIONAL + } OPTIONAL +} + +FeatureSetUplink-v1630 ::= SEQUENCE { + -- R1 22-8: For SRS for CB PUSCH and antenna switching on FR1 with symbol level offset for aperiodic SRS transmission + offsetSRS-CB-PUSCH-Ant-Switch-fr1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 22-8a: PDCCH monitoring on any span of up to 3 consecutive OFDM symbols of a slot and constrained timeline for SRS for CB + -- PUSCH and antenna switching on FR1 + offsetSRS-CB-PUSCH-PDCCH-MonitorSingleOcc-fr1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 22-8b: For type 1 CSS with dedicated RRC configuration, type 3 CSS, and UE-SS, monitoring occasion can be any OFDM symbol(s) + -- of a slot for Case 2 and constrained timeline for SRS for CB PUSCH and antenna switching on FR1 + offsetSRS-CB-PUSCH-PDCCH-MonitorAnyOccWithoutGap-fr1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 22-8c: For type 1 CSS with dedicated RRC configuration, type 3 CSS, and UE-SS, monitoring occasion can be any OFDM symbol(s) + -- of a slot for Case 2 with a DCI gap and constrained timeline for SRS for CB PUSCH and antenna switching on FR1 + offsetSRS-CB-PUSCH-PDCCH-MonitorAnyOccWithGap-fr1-r16 ENUMERATED {supported} OPTIONAL, + dummy ENUMERATED {supported} OPTIONAL, + -- R1 22-9: Cancellation of PUCCH, PUSCH or PRACH with a DCI scheduling a PDSCH or CSI-RS or a DCI format 2_0 for SFI + partialCancellationPUCCH-PUSCH-PRACH-TX-r16 ENUMERATED {supported} OPTIONAL +} + +FeatureSetUplink-v1640 ::= SEQUENCE { + -- R1 11-4: Two HARQ-ACK codebooks with up to one sub-slot based HARQ-ACK codebook (i.e. slot-based + slot-based, or slot-based + + -- sub-slot based) simultaneously constructed for supporting HARQ-ACK codebooks with different priorities at a UE + twoHARQ-ACK-Codebook-type1-r16 SubSlot-Config-r16 OPTIONAL, + -- R1 11-4a: Two sub-slot based HARQ-ACK codebooks simultaneously constructed for supporting HARQ-ACK codebooks with different + -- priorities at a UE + twoHARQ-ACK-Codebook-type2-r16 SubSlot-Config-r16 OPTIONAL, + -- R1 22-8d: All PDCCH monitoring occasion can be any OFDM symbol(s) of a slot for Case 2 with a span gap and constrained timeline + -- for SRS for CB PUSCH and antenna switching on FR1 + offsetSRS-CB-PUSCH-PDCCH-MonitorAnyOccWithSpanGap-fr1-r16 SEQUENCE { + scs-15kHz-r16 ENUMERATED {set1, set2, set3} OPTIONAL, + scs-30kHz-r16 ENUMERATED {set1, set2, set3} OPTIONAL, + scs-60kHz-r16 ENUMERATED {set1, set2, set3} OPTIONAL + } OPTIONAL +} + +SubSlot-Config-r16 ::= SEQUENCE { + sub-SlotConfig-NCP-r16 ENUMERATED {n4,n5,n6,n7} OPTIONAL, + sub-SlotConfig-ECP-r16 ENUMERATED {n4,n5,n6} OPTIONAL +} + +SRS-AllPosResources-r16 ::= SEQUENCE { + srs-PosResources-r16 SRS-PosResources-r16, + srs-PosResourceAP-r16 SRS-PosResourceAP-r16 OPTIONAL, + srs-PosResourceSP-r16 SRS-PosResourceSP-r16 OPTIONAL +} + +SRS-PosResources-r16 ::= SEQUENCE { + maxNumberSRS-PosResourceSetPerBWP-r16 ENUMERATED {n1, n2, n4, n8, n12, n16}, + maxNumberSRS-PosResourcesPerBWP-r16 ENUMERATED {n1, n2, n4, n8, n16, n32, n64}, + maxNumberSRS-ResourcesPerBWP-PerSlot-r16 ENUMERATED {n1, n2, n3, n4, n5, n6, n8, n10, n12, n14}, + maxNumberPeriodicSRS-PosResourcesPerBWP-r16 ENUMERATED {n1, n2, n4, n8, n16, n32, n64}, + maxNumberPeriodicSRS-PosResourcesPerBWP-PerSlot-r16 ENUMERATED {n1, n2, n3, n4, n5, n6, n8, n10, n12, n14} +} + +SRS-PosResourceAP-r16 ::= SEQUENCE { + maxNumberAP-SRS-PosResourcesPerBWP-r16 ENUMERATED {n1, n2, n4, n8, n16, n32, n64}, + maxNumberAP-SRS-PosResourcesPerBWP-PerSlot-r16 ENUMERATED {n1, n2, n3, n4, n5, n6, n8, n10, n12, n14} +} + +SRS-PosResourceSP-r16 ::= SEQUENCE { + maxNumberSP-SRS-PosResourcesPerBWP-r16 ENUMERATED {n1, n2, n4, n8, n16, n32, n64}, + maxNumberSP-SRS-PosResourcesPerBWP-PerSlot-r16 ENUMERATED {n1, n2, n3, n4, n5, n6, n8, n10, n12, n14} +} + +SRS-Resources ::= SEQUENCE { + maxNumberAperiodicSRS-PerBWP ENUMERATED {n1, n2, n4, n8, n16}, + maxNumberAperiodicSRS-PerBWP-PerSlot INTEGER (1..6), + maxNumberPeriodicSRS-PerBWP ENUMERATED {n1, n2, n4, n8, n16}, + maxNumberPeriodicSRS-PerBWP-PerSlot INTEGER (1..6), + maxNumberSemiPersistentSRS-PerBWP ENUMERATED {n1, n2, n4, n8, n16}, + maxNumberSemiPersistentSRS-PerBWP-PerSlot INTEGER (1..6), + maxNumberSRS-Ports-PerResource ENUMERATED {n1, n2, n4} +} + +DummyF ::= SEQUENCE { + maxNumberPeriodicCSI-ReportPerBWP INTEGER (1..4), + maxNumberAperiodicCSI-ReportPerBWP INTEGER (1..4), + maxNumberSemiPersistentCSI-ReportPerBWP INTEGER (0..4), + simultaneousCSI-ReportsAllCC INTEGER (5..32) +} + +-- TAG-FEATURESETUPLINK-STOP +-- TAG-FEATURESETUPLINKID-START + +FeatureSetUplinkId ::= INTEGER (0..maxUplinkFeatureSets) + +-- TAG-FEATURESETUPLINKID-STOP +-- TAG-FEATURESETUPLINKPERCC-START + +FeatureSetUplinkPerCC ::= SEQUENCE { + supportedSubcarrierSpacingUL SubcarrierSpacing, + supportedBandwidthUL SupportedBandwidth, + channelBW-90mhz ENUMERATED {supported} OPTIONAL, + mimo-CB-PUSCH SEQUENCE { + maxNumberMIMO-LayersCB-PUSCH MIMO-LayersUL OPTIONAL, + maxNumberSRS-ResourcePerSet INTEGER (1..2) + } OPTIONAL, + maxNumberMIMO-LayersNonCB-PUSCH MIMO-LayersUL OPTIONAL, + supportedModulationOrderUL ModulationOrder OPTIONAL +} +FeatureSetUplinkPerCC-v1540 ::= SEQUENCE { + mimo-NonCB-PUSCH SEQUENCE { + maxNumberSRS-ResourcePerSet INTEGER (1..4), + maxNumberSimultaneousSRS-ResourceTx INTEGER (1..4) + } OPTIONAL +} + +-- TAG-FEATURESETUPLINKPERCC-STOP +-- TAG-FEATURESETUPLINKPERCC-ID-START + +FeatureSetUplinkPerCC-Id ::= INTEGER (1..maxPerCC-FeatureSets) + +-- TAG-FEATURESETUPLINKPERCC-ID-STOP +-- TAG-FREQBANDINDICATOREUTRA-START + +FreqBandIndicatorEUTRA ::= INTEGER (1..maxBandsEUTRA) + +-- TAG-FREQBANDINDICATOREUTRA-STOP +-- TAG-FREQBANDLIST-START + +FreqBandList ::= SEQUENCE (SIZE (1..maxBandsMRDC)) OF FreqBandInformation + +FreqBandInformation ::= CHOICE { + bandInformationEUTRA FreqBandInformationEUTRA, + bandInformationNR FreqBandInformationNR +} + +FreqBandInformationEUTRA ::= SEQUENCE { + bandEUTRA FreqBandIndicatorEUTRA, + ca-BandwidthClassDL-EUTRA CA-BandwidthClassEUTRA OPTIONAL, -- Need N + ca-BandwidthClassUL-EUTRA CA-BandwidthClassEUTRA OPTIONAL -- Need N +} + +FreqBandInformationNR ::= SEQUENCE { + bandNR FreqBandIndicatorNR, + maxBandwidthRequestedDL AggregatedBandwidth OPTIONAL, -- Need N + maxBandwidthRequestedUL AggregatedBandwidth OPTIONAL, -- Need N + maxCarriersRequestedDL INTEGER (1..maxNrofServingCells) OPTIONAL, -- Need N + maxCarriersRequestedUL INTEGER (1..maxNrofServingCells) OPTIONAL -- Need N +} + +AggregatedBandwidth ::= ENUMERATED {mhz50, mhz100, mhz150, mhz200, mhz250, mhz300, mhz350, + mhz400, mhz450, mhz500, mhz550, mhz600, mhz650, mhz700, mhz750, mhz800} + +-- TAG-FREQBANDLIST-STOP +-- TAG-FREQSEPARATIONCLASS-START + +FreqSeparationClass ::= ENUMERATED { mhz800, mhz1200, mhz1400, ...} + +FreqSeparationClassDL-v1620 ::= ENUMERATED {mhz1000, mhz1600, mhz1800, mhz2000, mhz2200, mhz2400} + +FreqSeparationClassUL-v1620 ::= ENUMERATED {mhz1000} + +-- TAG-FREQSEPARATIONCLASS-STOP +-- TAG-FREQSEPARATIONCLASSDL-Only-START + +FreqSeparationClassDL-Only-r16 ::= ENUMERATED {mhz200, mhz400, mhz600, mhz800, mhz1000, mhz1200} + +-- TAG-FREQSEPARATIONCLASSDL-Only-STOP +-- TAG-HIGHSPEEDPARAMETERS-START + +HighSpeedParameters-r16 ::= SEQUENCE { + measurementEnhancement-r16 ENUMERATED {supported} OPTIONAL, + demodulationEnhancement-r16 ENUMERATED {supported} OPTIONAL +} + +-- TAG-HIGHSPEEDPARAMETERS-STOP +-- TAG-IMS-PARAMETERS-START + +IMS-Parameters ::= SEQUENCE { + ims-ParametersCommon IMS-ParametersCommon OPTIONAL, + ims-ParametersFRX-Diff IMS-ParametersFRX-Diff OPTIONAL, + ... +} + +IMS-ParametersCommon ::= SEQUENCE { + voiceOverEUTRA-5GC ENUMERATED {supported} OPTIONAL, + ..., + [[ + voiceOverSCG-BearerEUTRA-5GC ENUMERATED {supported} OPTIONAL + ]], + [[ + voiceFallbackIndicationEPS-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +IMS-ParametersFRX-Diff ::= SEQUENCE { + voiceOverNR ENUMERATED {supported} OPTIONAL, + ... +} + +-- TAG-IMS-PARAMETERS-STOP +-- TAG-INTERRAT-PARAMETERS-START + +InterRAT-Parameters ::= SEQUENCE { + eutra EUTRA-Parameters OPTIONAL, + ..., + [[ + utra-FDD-r16 UTRA-FDD-Parameters-r16 OPTIONAL + ]] + +} + +EUTRA-Parameters ::= SEQUENCE { + supportedBandListEUTRA SEQUENCE (SIZE (1..maxBandsEUTRA)) OF FreqBandIndicatorEUTRA, + eutra-ParametersCommon EUTRA-ParametersCommon OPTIONAL, + eutra-ParametersXDD-Diff EUTRA-ParametersXDD-Diff OPTIONAL, + ... +} + +EUTRA-ParametersCommon ::= SEQUENCE { + mfbi-EUTRA ENUMERATED {supported} OPTIONAL, + modifiedMPR-BehaviorEUTRA BIT STRING (SIZE (32)) OPTIONAL, + multiNS-Pmax-EUTRA ENUMERATED {supported} OPTIONAL, + rs-SINR-MeasEUTRA ENUMERATED {supported} OPTIONAL, + ..., + [[ + ne-DC ENUMERATED {supported} OPTIONAL + ]], + [[ + nr-HO-ToEN-DC-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +EUTRA-ParametersXDD-Diff ::= SEQUENCE { + rsrqMeasWidebandEUTRA ENUMERATED {supported} OPTIONAL, + ... +} + +UTRA-FDD-Parameters-r16 ::= SEQUENCE { + supportedBandListUTRA-FDD-r16 SEQUENCE (SIZE (1..maxBandsUTRA-FDD-r16)) OF SupportedBandUTRA-FDD-r16, + ... +} + +SupportedBandUTRA-FDD-r16 ::= ENUMERATED { + bandI, bandII, bandIII, bandIV, bandV, bandVI, + bandVII, bandVIII, bandIX, bandX, bandXI, + bandXII, bandXIII, bandXIV, bandXV, bandXVI, + bandXVII, bandXVIII, bandXIX, bandXX, + bandXXI, bandXXII, bandXXIII, bandXXIV, + bandXXV, bandXXVI, bandXXVII, bandXXVIII, + bandXXIX, bandXXX, bandXXXI, bandXXXII} + +-- TAG-INTERRAT-PARAMETERS-STOP +-- TAG-MAC-PARAMETERS-START + +MAC-Parameters ::= SEQUENCE { + mac-ParametersCommon MAC-ParametersCommon OPTIONAL, + mac-ParametersXDD-Diff MAC-ParametersXDD-Diff OPTIONAL +} + +MAC-Parameters-v1610 ::= SEQUENCE { + mac-ParametersFRX-Diff-r16 MAC-ParametersFRX-Diff-r16 OPTIONAL +} + +MAC-ParametersCommon ::= SEQUENCE { + lcp-Restriction ENUMERATED {supported} OPTIONAL, + dummy ENUMERATED {supported} OPTIONAL, + lch-ToSCellRestriction ENUMERATED {supported} OPTIONAL, + ..., + [[ + recommendedBitRate ENUMERATED {supported} OPTIONAL, + recommendedBitRateQuery ENUMERATED {supported} OPTIONAL + ]], + [[ + recommendedBitRateMultiplier-r16 ENUMERATED {supported} OPTIONAL, + preEmptiveBSR-r16 ENUMERATED {supported} OPTIONAL, + autonomousTransmission-r16 ENUMERATED {supported} OPTIONAL, + lch-PriorityBasedPrioritization-r16 ENUMERATED {supported} OPTIONAL, + lch-ToConfiguredGrantMapping-r16 ENUMERATED {supported} OPTIONAL, + lch-ToGrantPriorityRestriction-r16 ENUMERATED {supported} OPTIONAL, + singlePHR-P-r16 ENUMERATED {supported} OPTIONAL, + ul-LBT-FailureDetectionRecovery-r16 ENUMERATED {supported} OPTIONAL, + -- R4 8-1: MPE + tdd-MPE-P-MPR-Reporting-r16 ENUMERATED {supported} OPTIONAL, + lcid-ExtensionIAB-r16 ENUMERATED {supported} OPTIONAL + ]], + [[ + spCell-BFR-CBRA-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +MAC-ParametersFRX-Diff-r16 ::= SEQUENCE { + directMCG-SCellActivation-r16 ENUMERATED {supported} OPTIONAL, + directMCG-SCellActivationResume-r16 ENUMERATED {supported} OPTIONAL, + directSCG-SCellActivation-r16 ENUMERATED {supported} OPTIONAL, + directSCG-SCellActivationResume-r16 ENUMERATED {supported} OPTIONAL, + -- R1 19-1: DRX Adaptation + drx-Adaptation-r16 SEQUENCE { + non-SharedSpectrumChAccess-r16 MinTimeGap-r16 OPTIONAL, + sharedSpectrumChAccess-r16 MinTimeGap-r16 OPTIONAL + } OPTIONAL, + ... +} + +MAC-ParametersXDD-Diff ::= SEQUENCE { + skipUplinkTxDynamic ENUMERATED {supported} OPTIONAL, + logicalChannelSR-DelayTimer ENUMERATED {supported} OPTIONAL, + longDRX-Cycle ENUMERATED {supported} OPTIONAL, + shortDRX-Cycle ENUMERATED {supported} OPTIONAL, + multipleSR-Configurations ENUMERATED {supported} OPTIONAL, + multipleConfiguredGrants ENUMERATED {supported} OPTIONAL, + ..., + [[ + secondaryDRX-Group-r16 ENUMERATED {supported} OPTIONAL + ]], + [[ + enhancedSkipUplinkTxDynamic-r16 ENUMERATED {supported} OPTIONAL, + enhancedSkipUplinkTxConfigured-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +MinTimeGap-r16 ::= SEQUENCE { + scs-15kHz-r16 ENUMERATED {sl1, sl3} OPTIONAL, + scs-30kHz-r16 ENUMERATED {sl1, sl6} OPTIONAL, + scs-60kHz-r16 ENUMERATED {sl1, sl12} OPTIONAL, + scs-120kHz-r16 ENUMERATED {sl2, sl24} OPTIONAL +} + +-- TAG-MAC-PARAMETERS-STOP +-- TAG-MEASANDMOBPARAMETERS-START + +MeasAndMobParameters ::= SEQUENCE { + measAndMobParametersCommon MeasAndMobParametersCommon OPTIONAL, + measAndMobParametersXDD-Diff MeasAndMobParametersXDD-Diff OPTIONAL, + measAndMobParametersFRX-Diff MeasAndMobParametersFRX-Diff OPTIONAL +} + +MeasAndMobParametersCommon ::= SEQUENCE { + supportedGapPattern BIT STRING (SIZE (22)) OPTIONAL, + ssb-RLM ENUMERATED {supported} OPTIONAL, + ssb-AndCSI-RS-RLM ENUMERATED {supported} OPTIONAL, + ..., + [[ + eventB-MeasAndReport ENUMERATED {supported} OPTIONAL, + handoverFDD-TDD ENUMERATED {supported} OPTIONAL, + eutra-CGI-Reporting ENUMERATED {supported} OPTIONAL, + nr-CGI-Reporting ENUMERATED {supported} OPTIONAL + ]], + [[ + independentGapConfig ENUMERATED {supported} OPTIONAL, + periodicEUTRA-MeasAndReport ENUMERATED {supported} OPTIONAL, + handoverFR1-FR2 ENUMERATED {supported} OPTIONAL, + maxNumberCSI-RS-RRM-RS-SINR ENUMERATED {n4, n8, n16, n32, n64, n96} OPTIONAL + ]], + [[ + nr-CGI-Reporting-ENDC ENUMERATED {supported} OPTIONAL + ]], + [[ + eutra-CGI-Reporting-NEDC ENUMERATED {supported} OPTIONAL, + eutra-CGI-Reporting-NRDC ENUMERATED {supported} OPTIONAL, + nr-CGI-Reporting-NEDC ENUMERATED {supported} OPTIONAL, + nr-CGI-Reporting-NRDC ENUMERATED {supported} OPTIONAL + ]], + [[ + reportAddNeighMeasForPeriodic-r16 ENUMERATED {supported} OPTIONAL, + condHandoverParametersCommon-r16 SEQUENCE { + condHandoverFDD-TDD-r16 ENUMERATED {supported} OPTIONAL, + condHandoverFR1-FR2-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + nr-NeedForGap-Reporting-r16 ENUMERATED {supported} OPTIONAL, + supportedGapPattern-NRonly-r16 BIT STRING (SIZE (10)) OPTIONAL, + supportedGapPattern-NRonly-NEDC-r16 ENUMERATED {supported} OPTIONAL, + maxNumberCLI-RSSI-r16 ENUMERATED {n8, n16, n32, n64} OPTIONAL, + maxNumberCLI-SRS-RSRP-r16 ENUMERATED {n4, n8, n16, n32} OPTIONAL, + maxNumberPerSlotCLI-SRS-RSRP-r16 ENUMERATED {n2, n4, n8} OPTIONAL, + mfbi-IAB-r16 ENUMERATED {supported} OPTIONAL, + dummy ENUMERATED {supported} OPTIONAL, + nr-CGI-Reporting-NPN-r16 ENUMERATED {supported} OPTIONAL, + idleInactiveEUTRA-MeasReport-r16 ENUMERATED {supported} OPTIONAL, + idleInactive-ValidityArea-r16 ENUMERATED {supported} OPTIONAL, + eutra-AutonomousGaps-r16 ENUMERATED {supported} OPTIONAL, + eutra-AutonomousGaps-NEDC-r16 ENUMERATED {supported} OPTIONAL, + eutra-AutonomousGaps-NRDC-r16 ENUMERATED {supported} OPTIONAL, + pcellT312-r16 ENUMERATED {supported} OPTIONAL, + supportedGapPattern-r16 BIT STRING (SIZE (2)) OPTIONAL + ]] +} + +MeasAndMobParametersXDD-Diff ::= SEQUENCE { + intraAndInterF-MeasAndReport ENUMERATED {supported} OPTIONAL, + eventA-MeasAndReport ENUMERATED {supported} OPTIONAL, + ..., + [[ + handoverInterF ENUMERATED {supported} OPTIONAL, + handoverLTE-EPC ENUMERATED {supported} OPTIONAL, + handoverLTE-5GC ENUMERATED {supported} OPTIONAL + ]], + [[ + sftd-MeasNR-Neigh ENUMERATED {supported} OPTIONAL, + sftd-MeasNR-Neigh-DRX ENUMERATED {supported} OPTIONAL + ]], + [[ + dummy ENUMERATED {supported} OPTIONAL + ]] +} + +MeasAndMobParametersFRX-Diff ::= SEQUENCE { + ss-SINR-Meas ENUMERATED {supported} OPTIONAL, + csi-RSRP-AndRSRQ-MeasWithSSB ENUMERATED {supported} OPTIONAL, + csi-RSRP-AndRSRQ-MeasWithoutSSB ENUMERATED {supported} OPTIONAL, + csi-SINR-Meas ENUMERATED {supported} OPTIONAL, + csi-RS-RLM ENUMERATED {supported} OPTIONAL, + ..., + [[ + handoverInterF ENUMERATED {supported} OPTIONAL, + handoverLTE-EPC ENUMERATED {supported} OPTIONAL, + handoverLTE-5GC ENUMERATED {supported} OPTIONAL + ]], + [[ + maxNumberResource-CSI-RS-RLM ENUMERATED {n2, n4, n6, n8} OPTIONAL + ]], + [[ + simultaneousRxDataSSB-DiffNumerology ENUMERATED {supported} OPTIONAL + ]], + [[ + nr-AutonomousGaps-r16 ENUMERATED {supported} OPTIONAL, + nr-AutonomousGaps-ENDC-r16 ENUMERATED {supported} OPTIONAL, + nr-AutonomousGaps-NEDC-r16 ENUMERATED {supported} OPTIONAL, + nr-AutonomousGaps-NRDC-r16 ENUMERATED {supported} OPTIONAL, + dummy ENUMERATED {supported} OPTIONAL, + cli-RSSI-Meas-r16 ENUMERATED {supported} OPTIONAL, + cli-SRS-RSRP-Meas-r16 ENUMERATED {supported} OPTIONAL, + interFrequencyMeas-NoGap-r16 ENUMERATED {supported} OPTIONAL, + simultaneousRxDataSSB-DiffNumerology-Inter-r16 ENUMERATED {supported} OPTIONAL, + idleInactiveNR-MeasReport-r16 ENUMERATED {supported} OPTIONAL, + -- R4 6-2: Support of beam level Early Measurement Reporting + idleInactiveNR-MeasBeamReport-r16 ENUMERATED {supported} OPTIONAL + ]], + [[ + increasedNumberofCSIRSPerMO-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +-- TAG-MEASANDMOBPARAMETERS-STOP +-- TAG-MEASANDMOBPARAMETERSMRDC-START + +MeasAndMobParametersMRDC ::= SEQUENCE { + measAndMobParametersMRDC-Common MeasAndMobParametersMRDC-Common OPTIONAL, + measAndMobParametersMRDC-XDD-Diff MeasAndMobParametersMRDC-XDD-Diff OPTIONAL, + measAndMobParametersMRDC-FRX-Diff MeasAndMobParametersMRDC-FRX-Diff OPTIONAL +} + +MeasAndMobParametersMRDC-v1560 ::= SEQUENCE { + measAndMobParametersMRDC-XDD-Diff-v1560 MeasAndMobParametersMRDC-XDD-Diff-v1560 OPTIONAL +} + +MeasAndMobParametersMRDC-v1610 ::= SEQUENCE { + measAndMobParametersMRDC-Common-v1610 MeasAndMobParametersMRDC-Common-v1610 OPTIONAL, + interNR-MeasEUTRA-IAB-r16 ENUMERATED {supported} OPTIONAL +} + +MeasAndMobParametersMRDC-Common ::= SEQUENCE { + independentGapConfig ENUMERATED {supported} OPTIONAL +} + +MeasAndMobParametersMRDC-Common-v1610 ::= SEQUENCE { + condPSCellChangeParametersCommon-r16 SEQUENCE { + condPSCellChangeFDD-TDD-r16 ENUMERATED {supported} OPTIONAL, + condPSCellChangeFR1-FR2-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + pscellT312-r16 ENUMERATED {supported} OPTIONAL +} + +MeasAndMobParametersMRDC-XDD-Diff ::= SEQUENCE { + sftd-MeasPSCell ENUMERATED {supported} OPTIONAL, + sftd-MeasNR-Cell ENUMERATED {supported} OPTIONAL +} + +MeasAndMobParametersMRDC-XDD-Diff-v1560 ::= SEQUENCE { + sftd-MeasPSCell-NEDC ENUMERATED {supported} OPTIONAL +} + +MeasAndMobParametersMRDC-FRX-Diff ::= SEQUENCE { + simultaneousRxDataSSB-DiffNumerology ENUMERATED {supported} OPTIONAL +} + +-- TAG-MEASANDMOBPARAMETERSMRDC-STOP +-- TAG-MIMO-LAYERS-START + +MIMO-LayersDL ::= ENUMERATED {twoLayers, fourLayers, eightLayers} + +MIMO-LayersUL ::= ENUMERATED {oneLayer, twoLayers, fourLayers} + +-- TAG-MIMO-LAYERS-STOP +-- TAG-MIMO-PARAMETERSPERBAND-START + +MIMO-ParametersPerBand ::= SEQUENCE { + tci-StatePDSCH SEQUENCE { + maxNumberConfiguredTCIstatesPerCC ENUMERATED {n4, n8, n16, n32, n64, n128} OPTIONAL, + maxNumberActiveTCI-PerBWP ENUMERATED {n1, n2, n4, n8} OPTIONAL + } OPTIONAL, + additionalActiveTCI-StatePDCCH ENUMERATED {supported} OPTIONAL, + pusch-TransCoherence ENUMERATED {nonCoherent, partialCoherent, fullCoherent} OPTIONAL, + beamCorrespondenceWithoutUL-BeamSweeping ENUMERATED {supported} OPTIONAL, + periodicBeamReport ENUMERATED {supported} OPTIONAL, + aperiodicBeamReport ENUMERATED {supported} OPTIONAL, + sp-BeamReportPUCCH ENUMERATED {supported} OPTIONAL, + sp-BeamReportPUSCH ENUMERATED {supported} OPTIONAL, + dummy1 DummyG OPTIONAL, + maxNumberRxBeam INTEGER (2..8) OPTIONAL, + maxNumberRxTxBeamSwitchDL SEQUENCE { + scs-15kHz ENUMERATED {n4, n7, n14} OPTIONAL, + scs-30kHz ENUMERATED {n4, n7, n14} OPTIONAL, + scs-60kHz ENUMERATED {n4, n7, n14} OPTIONAL, + scs-120kHz ENUMERATED {n4, n7, n14} OPTIONAL, + scs-240kHz ENUMERATED {n4, n7, n14} OPTIONAL + } OPTIONAL, + maxNumberNonGroupBeamReporting ENUMERATED {n1, n2, n4} OPTIONAL, + groupBeamReporting ENUMERATED {supported} OPTIONAL, + uplinkBeamManagement SEQUENCE { + maxNumberSRS-ResourcePerSet-BM ENUMERATED {n2, n4, n8, n16}, + maxNumberSRS-ResourceSet INTEGER (1..8) + } OPTIONAL, + maxNumberCSI-RS-BFD INTEGER (1..64) OPTIONAL, + maxNumberSSB-BFD INTEGER (1..64) OPTIONAL, + maxNumberCSI-RS-SSB-CBD INTEGER (1..256) OPTIONAL, + dummy2 ENUMERATED {supported} OPTIONAL, + twoPortsPTRS-UL ENUMERATED {supported} OPTIONAL, + dummy5 SRS-Resources OPTIONAL, + dummy3 INTEGER (1..4) OPTIONAL, + beamReportTiming SEQUENCE { + scs-15kHz ENUMERATED {sym2, sym4, sym8} OPTIONAL, + scs-30kHz ENUMERATED {sym4, sym8, sym14, sym28} OPTIONAL, + scs-60kHz ENUMERATED {sym8, sym14, sym28} OPTIONAL, + scs-120kHz ENUMERATED {sym14, sym28, sym56} OPTIONAL + } OPTIONAL, + ptrs-DensityRecommendationSetDL SEQUENCE { + scs-15kHz PTRS-DensityRecommendationDL OPTIONAL, + scs-30kHz PTRS-DensityRecommendationDL OPTIONAL, + scs-60kHz PTRS-DensityRecommendationDL OPTIONAL, + scs-120kHz PTRS-DensityRecommendationDL OPTIONAL + } OPTIONAL, + ptrs-DensityRecommendationSetUL SEQUENCE { + scs-15kHz PTRS-DensityRecommendationUL OPTIONAL, + scs-30kHz PTRS-DensityRecommendationUL OPTIONAL, + scs-60kHz PTRS-DensityRecommendationUL OPTIONAL, + scs-120kHz PTRS-DensityRecommendationUL OPTIONAL + } OPTIONAL, + dummy4 DummyH OPTIONAL, + aperiodicTRS ENUMERATED {supported} OPTIONAL, + ..., + [[ + dummy6 ENUMERATED {true} OPTIONAL, + beamManagementSSB-CSI-RS BeamManagementSSB-CSI-RS OPTIONAL, + beamSwitchTiming SEQUENCE { + scs-60kHz ENUMERATED {sym14, sym28, sym48, sym224, sym336} OPTIONAL, + scs-120kHz ENUMERATED {sym14, sym28, sym48, sym224, sym336} OPTIONAL + } OPTIONAL, + codebookParameters CodebookParameters OPTIONAL, + csi-RS-IM-ReceptionForFeedback CSI-RS-IM-ReceptionForFeedback OPTIONAL, + csi-RS-ProcFrameworkForSRS CSI-RS-ProcFrameworkForSRS OPTIONAL, + csi-ReportFramework CSI-ReportFramework OPTIONAL, + csi-RS-ForTracking CSI-RS-ForTracking OPTIONAL, + srs-AssocCSI-RS SEQUENCE (SIZE (1.. maxNrofCSI-RS-Resources)) OF SupportedCSI-RS-Resource OPTIONAL, + spatialRelations SpatialRelations OPTIONAL + ]], + [[ + -- R1 16-2b-0: Support of default QCL assumption with two TCI states + defaultQCL-TwoTCI-r16 ENUMERATED {supported} OPTIONAL, + codebookParametersPerBand-r16 CodebookParameters-v1610 OPTIONAL, + -- R1 16-1b-3: Support of PUCCH resource groups per BWP for simultaneous spatial relation update + simul-SpatialRelationUpdatePUCCHResGroup-r16 ENUMERATED {supported} OPTIONAL, + + -- R1 16-1f: Maximum number of SCells configured for SCell beam failure recovery simultaneously + maxNumberSCellBFR-r16 ENUMERATED {n1,n2,n4,n8} OPTIONAL, + + -- R1 16-2c: Supports simultaneous reception with different Type-D for FR2 only + simultaneousReceptionDiffTypeD-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-1a-1: SSB/CSI-RS for L1-SINR measurement + ssb-csirs-SINR-measurement-r16 SEQUENCE { + maxNumberSSB-CSIRS-OneTx-CMR-r16 ENUMERATED {n8, n16, n32, n64}, + maxNumberCSI-IM-NZP-IMR-res-r16 ENUMERATED {n8, n16, n32, n64}, + maxNumberCSIRS-2Tx-res-r16 ENUMERATED {n0, n4, n8, n16, n32, n64}, + maxNumberSSB-CSIRS-res-r16 ENUMERATED {n8, n16, n32, n64, n128}, + maxNumberCSI-IM-NZP-IMR-res-mem-r16 ENUMERATED {n8, n16, n32, n64, n128}, + supportedCSI-RS-Density-CMR-r16 ENUMERATED {one, three, oneAndThree}, + maxNumberAperiodicCSI-RS-Res-r16 ENUMERATED {n2, n4, n8, n16, n32, n64}, + supportedSINR-meas-r16 ENUMERATED {ssbWithCSI-IM, ssbWithNZP-IMR, csirsWithNZP-IMR, csi-RSWithoutIMR} OPTIONAL + } OPTIONAL, + -- R1 16-1a-2: Non-group based L1-SINR reporting + nonGroupSINR-reporting-r16 ENUMERATED {n1, n2, n4} OPTIONAL, + -- R1 16-1a-3: Non-group based L1-SINR reporting + groupSINR-reporting-r16 ENUMERATED {supported} OPTIONAL, + + multiDCI-multiTRP-Parameters-r16 SEQUENCE { + -- R1 16-2a-0: Overlapping PDSCHs in time and fully overlapping in frequency and time + overlapPDSCHsFullyFreqTime-r16 INTEGER (1..2) OPTIONAL, + -- R1 16-2a-1: Overlapping PDSCHs in time and partially overlapping in frequency and time + overlapPDSCHsInTimePartiallyFreq-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-2a-2: Out of order operation for DL + outOfOrderOperationDL-r16 SEQUENCE { + supportPDCCH-ToPDSCH-r16 ENUMERATED {supported} OPTIONAL, + supportPDSCH-ToHARQ-ACK-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + -- R1 16-2a-3: Out of order operation for UL + outOfOrderOperationUL-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-2a-5: Separate CRS rate matching + separateCRS-RateMatching-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-2a-6: Default QCL enhancement for multi-DCI based multi-TRP + defaultQCL-PerCORESETPoolIndex-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-2a-7: Maximum number of activated TCI states + maxNumberActivatedTCI-States-r16 SEQUENCE { + maxNumberPerCORESET-Pool-r16 ENUMERATED {n1, n2, n4, n8}, + maxTotalNumberAcrossCORESET-Pool-r16 ENUMERATED {n2, n4, n8, n16} + } OPTIONAL + } OPTIONAL, + singleDCI-SDM-scheme-Parameters-r16 SEQUENCE { + -- R1 16-2b-1b: Single-DCI based SDM scheme - Support of new DMRS port entry + supportNewDMRS-Port-r16 ENUMERATED {n0, n2, n3} OPTIONAL, + -- R1 16-2b-1a: Support of s-port DL PTRS + supportTwoPortDL-PTRS-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + -- R1 16-2b-2: Support of single-DCI based FDMSchemeA + supportFDM-SchemeA-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-2b-3a: Single-DCI based FDMSchemeB CW soft combining + supportCodeWordSoftCombining-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-2b-4: Single-DCI based TDMSchemeA + supportTDM-SchemeA-r16 ENUMERATED {kb3, kb5, kb10, kb20, noRestriction} OPTIONAL, + -- R1 16-2b-5: Single-DCI based inter-slot TDM + supportInter-slotTDM-r16 SEQUENCE { + supportRepNumPDSCH-TDRA-r16 ENUMERATED {n2, n3, n4, n5, n6, n7, n8, n16}, + maxTBS-Size-r16 ENUMERATED {kb3, kb5, kb10, kb20, noRestriction}, + maxNumberTCI-states-r16 INTEGER (1..2) + } OPTIONAL, + -- R1 16-4: Low PAPR DMRS for PDSCH + lowPAPR-DMRS-PDSCH-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-6a: Low PAPR DMRS for PUSCH without transform precoding + lowPAPR-DMRS-PUSCHwithoutPrecoding-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-6b: Low PAPR DMRS for PUCCH + lowPAPR-DMRS-PUCCH-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-6c: Low PAPR DMRS for PUSCH with transform precoding & pi/2 BPSK + lowPAPR-DMRS-PUSCHwithPrecoding-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-7: Extension of the maximum number of configured aperiodic CSI report settings + csi-ReportFrameworkExt-r16 CSI-ReportFrameworkExt-r16 OPTIONAL, + -- R1 16-3a, 16-3a-1, 16-3b, 16-3b-1, 16-8: Individual new codebook types + codebookParametersAddition-r16 CodebookParametersAddition-r16 OPTIONAL, + -- R1 16-8: Mixed codebook types + codebookComboParametersAddition-r16 CodebookComboParametersAddition-r16 OPTIONAL, + -- R4 8-2: SSB based beam correspondence + beamCorrespondenceSSB-based-r16 ENUMERATED {supported} OPTIONAL, + -- R4 8-3: CSI-RS based beam correspondence + beamCorrespondenceCSI-RS-based-r16 ENUMERATED {supported} OPTIONAL, + beamSwitchTiming-r16 SEQUENCE { + scs-60kHz-r16 ENUMERATED {sym224, sym336} OPTIONAL, + scs-120kHz-r16 ENUMERATED {sym224, sym336} OPTIONAL + } OPTIONAL + ]], + [[ + -- R1 16-1a-4: Semi-persistent L1-SINR report on PUCCH + semi-PersistentL1-SINR-Report-PUCCH-r16 SEQUENCE { + supportReportFormat1-2OFDM-syms-r16 ENUMERATED {supported} OPTIONAL, + supportReportFormat4-14OFDM-syms-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + -- R1 16-1a-5: Semi-persistent L1-SINR report on PUSCH + semi-PersistentL1-SINR-Report-PUSCH-r16 ENUMERATED {supported} OPTIONAL + ]], + [[ + -- R1 16-1h: Support of 64 configured PUCCH spatial relations + spatialRelations-v1640 SEQUENCE { + maxNumberConfiguredSpatialRelations-v1640 ENUMERATED {n96, n128, n160, n192, n224, n256, n288, n320} + } OPTIONAL, + -- R1 16-1i: Support of 64 configured candidate beam RSs for BFR + support64CandidateBeamRS-BFR-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +DummyG ::= SEQUENCE { + maxNumberSSB-CSI-RS-ResourceOneTx ENUMERATED {n8, n16, n32, n64}, + maxNumberSSB-CSI-RS-ResourceTwoTx ENUMERATED {n0, n4, n8, n16, n32, n64}, + supportedCSI-RS-Density ENUMERATED {one, three, oneAndThree} +} + +BeamManagementSSB-CSI-RS ::= SEQUENCE { + maxNumberSSB-CSI-RS-ResourceOneTx ENUMERATED {n0, n8, n16, n32, n64}, + maxNumberCSI-RS-Resource ENUMERATED {n0, n4, n8, n16, n32, n64}, + maxNumberCSI-RS-ResourceTwoTx ENUMERATED {n0, n4, n8, n16, n32, n64}, + supportedCSI-RS-Density ENUMERATED {one, three, oneAndThree} OPTIONAL, + maxNumberAperiodicCSI-RS-Resource ENUMERATED {n0, n1, n4, n8, n16, n32, n64} +} + +DummyH ::= SEQUENCE { + burstLength INTEGER (1..2), + maxSimultaneousResourceSetsPerCC INTEGER (1..8), + maxConfiguredResourceSetsPerCC INTEGER (1..64), + maxConfiguredResourceSetsAllCC INTEGER (1..128) +} + +CSI-RS-ForTracking ::= SEQUENCE { + maxBurstLength INTEGER (1..2), + maxSimultaneousResourceSetsPerCC INTEGER (1..8), + maxConfiguredResourceSetsPerCC INTEGER (1..64), + maxConfiguredResourceSetsAllCC INTEGER (1..256) +} + +CSI-RS-IM-ReceptionForFeedback ::= SEQUENCE { + maxConfigNumberNZP-CSI-RS-PerCC INTEGER (1..64), + maxConfigNumberPortsAcrossNZP-CSI-RS-PerCC INTEGER (2..256), + maxConfigNumberCSI-IM-PerCC ENUMERATED {n1, n2, n4, n8, n16, n32}, + maxNumberSimultaneousNZP-CSI-RS-PerCC INTEGER (1..64), + totalNumberPortsSimultaneousNZP-CSI-RS-PerCC INTEGER (2..256) +} + +CSI-RS-ProcFrameworkForSRS ::= SEQUENCE { + maxNumberPeriodicSRS-AssocCSI-RS-PerBWP INTEGER (1..4), + maxNumberAperiodicSRS-AssocCSI-RS-PerBWP INTEGER (1..4), + maxNumberSP-SRS-AssocCSI-RS-PerBWP INTEGER (0..4), + simultaneousSRS-AssocCSI-RS-PerCC INTEGER (1..8) +} + +CSI-ReportFramework ::= SEQUENCE { + maxNumberPeriodicCSI-PerBWP-ForCSI-Report INTEGER (1..4), + maxNumberAperiodicCSI-PerBWP-ForCSI-Report INTEGER (1..4), + maxNumberSemiPersistentCSI-PerBWP-ForCSI-Report INTEGER (0..4), + maxNumberPeriodicCSI-PerBWP-ForBeamReport INTEGER (1..4), + maxNumberAperiodicCSI-PerBWP-ForBeamReport INTEGER (1..4), + maxNumberAperiodicCSI-triggeringStatePerCC ENUMERATED {n3, n7, n15, n31, n63, n128}, + maxNumberSemiPersistentCSI-PerBWP-ForBeamReport INTEGER (0..4), + simultaneousCSI-ReportsPerCC INTEGER (1..8) +} + +CSI-ReportFrameworkExt-r16 ::= SEQUENCE { + maxNumberAperiodicCSI-PerBWP-ForCSI-ReportExt-r16 INTEGER (5..8) +} + +PTRS-DensityRecommendationDL ::= SEQUENCE { + frequencyDensity1 INTEGER (1..276), + frequencyDensity2 INTEGER (1..276), + timeDensity1 INTEGER (0..29), + timeDensity2 INTEGER (0..29), + timeDensity3 INTEGER (0..29) +} + +PTRS-DensityRecommendationUL ::= SEQUENCE { + frequencyDensity1 INTEGER (1..276), + frequencyDensity2 INTEGER (1..276), + timeDensity1 INTEGER (0..29), + timeDensity2 INTEGER (0..29), + timeDensity3 INTEGER (0..29), + sampleDensity1 INTEGER (1..276), + sampleDensity2 INTEGER (1..276), + sampleDensity3 INTEGER (1..276), + sampleDensity4 INTEGER (1..276), + sampleDensity5 INTEGER (1..276) +} + +SpatialRelations ::= SEQUENCE { + maxNumberConfiguredSpatialRelations ENUMERATED {n4, n8, n16, n32, n64, n96}, + maxNumberActiveSpatialRelations ENUMERATED {n1, n2, n4, n8, n14}, + additionalActiveSpatialRelationPUCCH ENUMERATED {supported} OPTIONAL, + maxNumberDL-RS-QCL-TypeD ENUMERATED {n1, n2, n4, n8, n14} +} + +DummyI ::= SEQUENCE { + supportedSRS-TxPortSwitch ENUMERATED {t1r2, t1r4, t2r4, t1r4-t2r4, tr-equal}, + txSwitchImpactToRx ENUMERATED {true} OPTIONAL +} + +-- TAG-MIMO-PARAMETERSPERBAND-STOP +-- TAG-MODULATIONORDER-START + +ModulationOrder ::= ENUMERATED {bpsk-halfpi, bpsk, qpsk, qam16, qam64, qam256} + +-- TAG-MODULATIONORDER-STOP +-- TAG-MRDC-PARAMETERS-START + +MRDC-Parameters ::= SEQUENCE { + singleUL-Transmission ENUMERATED {supported} OPTIONAL, + dynamicPowerSharingENDC ENUMERATED {supported} OPTIONAL, + tdm-Pattern ENUMERATED {supported} OPTIONAL, + ul-SharingEUTRA-NR ENUMERATED {tdm, fdm, both} OPTIONAL, + ul-SwitchingTimeEUTRA-NR ENUMERATED {type1, type2} OPTIONAL, + simultaneousRxTxInterBandENDC ENUMERATED {supported} OPTIONAL, + asyncIntraBandENDC ENUMERATED {supported} OPTIONAL, + ..., + [[ + dualPA-Architecture ENUMERATED {supported} OPTIONAL, + intraBandENDC-Support ENUMERATED {non-contiguous, both} OPTIONAL, + ul-TimingAlignmentEUTRA-NR ENUMERATED {required} OPTIONAL + ]] +} + +MRDC-Parameters-v1580 ::= SEQUENCE { + dynamicPowerSharingNEDC ENUMERATED {supported} OPTIONAL +} + +MRDC-Parameters-v1590 ::= SEQUENCE { + interBandContiguousMRDC ENUMERATED {supported} OPTIONAL +} + +MRDC-Parameters-v1620 ::= SEQUENCE { + maxUplinkDutyCycle-interBandENDC-TDD-PC2-r16 SEQUENCE{ + eutra-TDD-Config0-r16 ENUMERATED {n20, n40, n50, n60, n70, n80, n90, n100} OPTIONAL, + eutra-TDD-Config1-r16 ENUMERATED {n20, n40, n50, n60, n70, n80, n90, n100} OPTIONAL, + eutra-TDD-Config2-r16 ENUMERATED {n20, n40, n50, n60, n70, n80, n90, n100} OPTIONAL, + eutra-TDD-Config3-r16 ENUMERATED {n20, n40, n50, n60, n70, n80, n90, n100} OPTIONAL, + eutra-TDD-Config4-r16 ENUMERATED {n20, n40, n50, n60, n70, n80, n90, n100} OPTIONAL, + eutra-TDD-Config5-r16 ENUMERATED {n20, n40, n50, n60, n70, n80, n90, n100} OPTIONAL, + eutra-TDD-Config6-r16 ENUMERATED {n20, n40, n50, n60, n70, n80, n90, n100} OPTIONAL + } OPTIONAL, + -- R1 18-2 Single UL TX operation for TDD PCell in EN-DC + tdm-restrictionTDD-endc-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-2a Single UL TX operation for FDD PCell in EN-DC + tdm-restrictionFDD-endc-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-2b Support of HARQ-offset for SUO case1 in EN-DC with LTE TDD PCell for type 1 UE + singleUL-HARQ-offsetTDD-PCell-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-3 Dual Tx transmission for EN-DC with FDD PCell(TDM pattern for dual Tx UE) + tdm-restrictionDualTX-FDD-endc-r16 ENUMERATED {supported} OPTIONAL +} + +MRDC-Parameters-v1630 ::= SEQUENCE { + -- R4 2-20 Maximum uplink duty cycle for FDD+TDD EN-DC power class 2 + maxUplinkDutyCycle-interBandENDC-FDD-TDD-PC2-r16 SEQUENCE { + maxUplinkDutyCycle-FDD-TDD-EN-DC1-r16 ENUMERATED {n30, n40, n50, n60, n70, n80, n90, n100} OPTIONAL, + maxUplinkDutyCycle-FDD-TDD-EN-DC2-r16 ENUMERATED {n30, n40, n50, n60, n70, n80, n90, n100} OPTIONAL + } OPTIONAL, + + -- R4 2-19 FDD-FDD or TDD-TDD inter-band MR-DC with overlapping or partially overlapping DL spectrum + interBandMRDC-WithOverlapDL-Bands-r16 ENUMERATED {supported} OPTIONAL +} + +-- TAG-MRDC-PARAMETERS-STOP +-- TAG-NRDC-PARAMETERS-START + +NRDC-Parameters ::= SEQUENCE { + measAndMobParametersNRDC MeasAndMobParametersMRDC OPTIONAL, + generalParametersNRDC GeneralParametersMRDC-XDD-Diff OPTIONAL, + fdd-Add-UE-NRDC-Capabilities UE-MRDC-CapabilityAddXDD-Mode OPTIONAL, + tdd-Add-UE-NRDC-Capabilities UE-MRDC-CapabilityAddXDD-Mode OPTIONAL, + fr1-Add-UE-NRDC-Capabilities UE-MRDC-CapabilityAddFRX-Mode OPTIONAL, + fr2-Add-UE-NRDC-Capabilities UE-MRDC-CapabilityAddFRX-Mode OPTIONAL, + dummy2 OCTET STRING OPTIONAL, + dummy SEQUENCE {} OPTIONAL +} + +NRDC-Parameters-v1570 ::= SEQUENCE { + sfn-SyncNRDC ENUMERATED {supported} OPTIONAL +} + +NRDC-Parameters-v15c0 ::= SEQUENCE { + pdcp-DuplicationSplitSRB ENUMERATED {supported} OPTIONAL, + pdcp-DuplicationSplitDRB ENUMERATED {supported} OPTIONAL +} + +NRDC-Parameters-v1610 ::= SEQUENCE { + measAndMobParametersNRDC-v1610 MeasAndMobParametersMRDC-v1610 OPTIONAL +} + + +-- TAG-NRDC-PARAMETERS-STOP +-- TAG-OLPC-SRS-POS-START + +OLPC-SRS-Pos-r16 ::= SEQUENCE { + olpc-SRS-PosBasedOnPRS-Serving-r16 ENUMERATED {supported} OPTIONAL, + olpc-SRS-PosBasedOnSSB-Neigh-r16 ENUMERATED {supported} OPTIONAL, + olpc-SRS-PosBasedOnPRS-Neigh-r16 ENUMERATED {supported} OPTIONAL, + maxNumberPathLossEstimatePerServing-r16 ENUMERATED {n1, n4, n8, n16} OPTIONAL +} + +--TAG-OLPC-SRS-POS-STOP +-- TAG-PDCP-PARAMETERS-START + +PDCP-Parameters ::= SEQUENCE { + supportedROHC-Profiles SEQUENCE { + profile0x0000 BOOLEAN, + profile0x0001 BOOLEAN, + profile0x0002 BOOLEAN, + profile0x0003 BOOLEAN, + profile0x0004 BOOLEAN, + profile0x0006 BOOLEAN, + profile0x0101 BOOLEAN, + profile0x0102 BOOLEAN, + profile0x0103 BOOLEAN, + profile0x0104 BOOLEAN + }, + maxNumberROHC-ContextSessions ENUMERATED {cs2, cs4, cs8, cs12, cs16, cs24, cs32, cs48, cs64, + cs128, cs256, cs512, cs1024, cs16384, spare2, spare1}, + uplinkOnlyROHC-Profiles ENUMERATED {supported} OPTIONAL, + continueROHC-Context ENUMERATED {supported} OPTIONAL, + outOfOrderDelivery ENUMERATED {supported} OPTIONAL, + shortSN ENUMERATED {supported} OPTIONAL, + pdcp-DuplicationSRB ENUMERATED {supported} OPTIONAL, + pdcp-DuplicationMCG-OrSCG-DRB ENUMERATED {supported} OPTIONAL, + ..., + [[ + drb-IAB-r16 ENUMERATED {supported} OPTIONAL, + non-DRB-IAB-r16 ENUMERATED {supported} OPTIONAL, + extendedDiscardTimer-r16 ENUMERATED {supported} OPTIONAL, + continueEHC-Context-r16 ENUMERATED {supported} OPTIONAL, + ehc-r16 ENUMERATED {supported} OPTIONAL, + maxNumberEHC-Contexts-r16 ENUMERATED {cs2, cs4, cs8, cs16, cs32, cs64, cs128, cs256, cs512, + cs1024, cs2048, cs4096, cs8192, cs16384, cs32768, cs65536} OPTIONAL, + jointEHC-ROHC-Config-r16 ENUMERATED {supported} OPTIONAL, + pdcp-DuplicationMoreThanTwoRLC-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +-- TAG-PDCP-PARAMETERS-STOP +-- TAG-PDCP-PARAMETERSMRDC-START + +PDCP-ParametersMRDC ::= SEQUENCE { + pdcp-DuplicationSplitSRB ENUMERATED {supported} OPTIONAL, + pdcp-DuplicationSplitDRB ENUMERATED {supported} OPTIONAL +} + +PDCP-ParametersMRDC-v1610 ::= SEQUENCE { + scg-DRB-NR-IAB-r16 ENUMERATED {supported} OPTIONAL +} + +-- TAG-PDCP-PARAMETERSMRDC-STOP +-- TAG-PHY-PARAMETERS-START + +Phy-Parameters ::= SEQUENCE { + phy-ParametersCommon Phy-ParametersCommon OPTIONAL, + phy-ParametersXDD-Diff Phy-ParametersXDD-Diff OPTIONAL, + phy-ParametersFRX-Diff Phy-ParametersFRX-Diff OPTIONAL, + phy-ParametersFR1 Phy-ParametersFR1 OPTIONAL, + phy-ParametersFR2 Phy-ParametersFR2 OPTIONAL +} + +Phy-ParametersCommon ::= SEQUENCE { + csi-RS-CFRA-ForHO ENUMERATED {supported} OPTIONAL, + dynamicPRB-BundlingDL ENUMERATED {supported} OPTIONAL, + sp-CSI-ReportPUCCH ENUMERATED {supported} OPTIONAL, + sp-CSI-ReportPUSCH ENUMERATED {supported} OPTIONAL, + nzp-CSI-RS-IntefMgmt ENUMERATED {supported} OPTIONAL, + type2-SP-CSI-Feedback-LongPUCCH ENUMERATED {supported} OPTIONAL, + precoderGranularityCORESET ENUMERATED {supported} OPTIONAL, + dynamicHARQ-ACK-Codebook ENUMERATED {supported} OPTIONAL, + semiStaticHARQ-ACK-Codebook ENUMERATED {supported} OPTIONAL, + spatialBundlingHARQ-ACK ENUMERATED {supported} OPTIONAL, + dynamicBetaOffsetInd-HARQ-ACK-CSI ENUMERATED {supported} OPTIONAL, + pucch-Repetition-F1-3-4 ENUMERATED {supported} OPTIONAL, + ra-Type0-PUSCH ENUMERATED {supported} OPTIONAL, + dynamicSwitchRA-Type0-1-PDSCH ENUMERATED {supported} OPTIONAL, + dynamicSwitchRA-Type0-1-PUSCH ENUMERATED {supported} OPTIONAL, + pdsch-MappingTypeA ENUMERATED {supported} OPTIONAL, + pdsch-MappingTypeB ENUMERATED {supported} OPTIONAL, + interleavingVRB-ToPRB-PDSCH ENUMERATED {supported} OPTIONAL, + interSlotFreqHopping-PUSCH ENUMERATED {supported} OPTIONAL, + type1-PUSCH-RepetitionMultiSlots ENUMERATED {supported} OPTIONAL, + type2-PUSCH-RepetitionMultiSlots ENUMERATED {supported} OPTIONAL, + pusch-RepetitionMultiSlots ENUMERATED {supported} OPTIONAL, + pdsch-RepetitionMultiSlots ENUMERATED {supported} OPTIONAL, + downlinkSPS ENUMERATED {supported} OPTIONAL, + configuredUL-GrantType1 ENUMERATED {supported} OPTIONAL, + configuredUL-GrantType2 ENUMERATED {supported} OPTIONAL, + pre-EmptIndication-DL ENUMERATED {supported} OPTIONAL, + cbg-TransIndication-DL ENUMERATED {supported} OPTIONAL, + cbg-TransIndication-UL ENUMERATED {supported} OPTIONAL, + cbg-FlushIndication-DL ENUMERATED {supported} OPTIONAL, + dynamicHARQ-ACK-CodeB-CBG-Retx-DL ENUMERATED {supported} OPTIONAL, + rateMatchingResrcSetSemi-Static ENUMERATED {supported} OPTIONAL, + rateMatchingResrcSetDynamic ENUMERATED {supported} OPTIONAL, + bwp-SwitchingDelay ENUMERATED {type1, type2} OPTIONAL, + ..., + [[ + dummy ENUMERATED {supported} OPTIONAL + ]], + [[ + maxNumberSearchSpaces ENUMERATED {n10} OPTIONAL, + rateMatchingCtrlResrcSetDynamic ENUMERATED {supported} OPTIONAL, + maxLayersMIMO-Indication ENUMERATED {supported} OPTIONAL + ]], + [[ + spCellPlacement CarrierAggregationVariant OPTIONAL + ]], + [[ + -- R1 9-1: Basic channel structure and procedure of 2-step RACH + twoStepRACH-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-1: Monitoring DCI format 1_2 and DCI format 0_2 + dci-Format1-2And0-2-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-1a: Monitoring both DCI format 0_1/1_1 and DCI format 0_2/1_2 in the same search space + monitoringDCI-SameSearchSpace-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-10: Type 2 configured grant release by DCI format 0_1 + type2-CG-ReleaseDCI-0-1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-11: Type 2 configured grant release by DCI format 0_2 + type2-CG-ReleaseDCI-0-2-r16 ENUMERATED {supported} OPTIONAL, + -- R1 12-3: SPS release by DCI format 1_1 + sps-ReleaseDCI-1-1-r16 ENUMERATED {supported} OPTIONAL, + -- R1 12-3a: SPS release by DCI format 1_2 + sps-ReleaseDCI-1-2-r16 ENUMERATED {supported} OPTIONAL, + -- R1 14-8: CSI trigger states containing non-active BWP + csi-TriggerStateNon-ActiveBWP-r16 ENUMERATED {supported} OPTIONAL, + -- R1 20-2: Support up to 4 SMTCs configured for an IAB node MT per frequency location, including IAB-specific SMTC window periodicities + seperateSMTC-InterIAB-Support-r16 ENUMERATED {supported} OPTIONAL, + -- R1 20-3: Support RACH configuration separately from the RACH configuration for UE access, including new IAB-specific offset and scaling factors + seperateRACH-IAB-Support-r16 ENUMERATED {supported} OPTIONAL, + -- R1 20-5a: Support semi-static configuration/indication of UL-Flexible-DL slot formats for IAB-MT resources + ul-flexibleDL-SlotFormatSemiStatic-IAB-r16 ENUMERATED {supported} OPTIONAL, + -- R1 20-5b: Support dynamic indication of UL-Flexible-DL slot formats for IAB-MT resources + ul-flexibleDL-SlotFormatDynamics-IAB-r16 ENUMERATED {supported} OPTIONAL, + dft-S-OFDM-WaveformUL-IAB-r16 ENUMERATED {supported} OPTIONAL, + -- R1 20-6: Support DCI Format 2_5 based indication of soft resource availability to an IAB node + dci-25-AI-RNTI-Support-IAB-r16 ENUMERATED {supported} OPTIONAL, + -- R1 20-7: Support T_delta reception. + t-DeltaReceptionSupport-IAB-r16 ENUMERATED {supported} OPTIONAL, + -- R1 20-8: Support of Desired guard symbol reporting and provided guard symbok reception. + guardSymbolReportReception-IAB-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-8 HARQ-ACK codebook type and spatial bundling per PUCCH group + harqACK-CB-SpatialBundlingPUCCH-Group-r16 ENUMERATED {supported} OPTIONAL, + -- R1 19-2: Cross Slot Scheduling + crossSlotScheduling-r16 SEQUENCE { + non-SharedSpectrumChAccess-r16 ENUMERATED {supported} OPTIONAL, + sharedSpectrumChAccess-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + maxNumberSRS-PosPathLossEstimateAllServingCells-r16 ENUMERATED {n1, n4, n8, n16} OPTIONAL, + extendedCG-Periodicities-r16 ENUMERATED {supported} OPTIONAL, + extendedSPS-Periodicities-r16 ENUMERATED {supported} OPTIONAL, + codebookVariantsList-r16 CodebookVariantsList-r16 OPTIONAL, + -- R1 11-6: PUSCH repetition Type A + pusch-RepetitionTypeA-r16 SEQUENCE { + sharedSpectrumChAccess-r16 ENUMERATED {supported} OPTIONAL, + non-SharedSpectrumChAccess-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + -- R1 11-4b: DL priority indication in DCI with mixed DCI formats + dci-DL-PriorityIndicator-r16 ENUMERATED {supported} OPTIONAL, + -- R1 12-1a: UL priority indication in DCI with mixed DCI formats + dci-UL-PriorityIndicator-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-1e: Maximum number of configured pathloss reference RSs for PUSCH/PUCCH/SRS by RRC for MAC-CE based pathloss reference RS update + maxNumberPathlossRS-Update-r16 ENUMERATED {n4, n8, n16, n32, n64} OPTIONAL, + + -- R1 18-9: Usage of the PDSCH starting time for HARQ-ACK type 2 codebook + type2-HARQ-ACK-Codebook-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-1g-1: Resources for beam management, pathloss measurement, BFD, RLM and new beam identification across frequency ranges + maxTotalResourcesForAcrossFreqRanges-r16 SEQUENCE { + maxNumberResWithinSlotAcrossCC-AcrossFR-r16 ENUMERATED {n2, n4, n8, n12, n16, n32, n64, n128} OPTIONAL, + maxNumberResAcrossCC-AcrossFR-r16 ENUMERATED {n2, n4, n8, n12, n16, n32, n40, n48, n64, n72, n80, n96, n128, n256} + OPTIONAL + } OPTIONAL, + -- R1 16-2a-4: HARQ-ACK for multi-DCI based multi-TRP - separate + harqACK-separateMultiDCI-MultiTRP-r16 SEQUENCE { + maxNumberLongPUCCHs-r16 ENUMERATED {longAndLong, longAndShort, shortAndShort} OPTIONAL + } OPTIONAL, + -- R1 16-2a-4: HARQ-ACK for multi-DCI based multi-TRP - joint + harqACK-jointMultiDCI-MultiTRP-r16 ENUMERATED {supported} OPTIONAL, + -- R4 9-1: BWP switching on multiple CCs RRM requirements + bwp-SwitchingMultiCCs-r16 CHOICE { + type1-r16 ENUMERATED {us100, us200}, + type2-r16 ENUMERATED {us200, us400, us800, us1000} + } OPTIONAL + ]], + [[ + targetSMTC-SCG-r16 ENUMERATED {supported} OPTIONAL, + supportRepetitionZeroOffsetRV-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-12: in-order CBG-based re-transmission + cbg-TransInOrderPUSCH-UL-r16 ENUMERATED {supported} OPTIONAL + ]], + [[ + -- R4 6-3: Dormant BWP switching on multiple CCs RRM requirements + bwp-SwitchingMultiDormancyCCs-r16 CHOICE { + type1-r16 ENUMERATED {us100, us200}, + type2-r16 ENUMERATED {us200, us400, us800, us1000} + } OPTIONAL, + -- R1 16-2a-8: Indicates that retransmission scheduled by a different CORESETPoolIndex for multi-DCI multi-TRP is not supported. + supportRetx-Diff-CoresetPool-Multi-DCI-TRP-r16 ENUMERATED {notSupported} OPTIONAL, + -- R1 22-10: Support of pdcch-MonitoringAnyOccasionsWithSpanGap in case of cross-carrier scheduling with different SCSs + pdcch-MonitoringAnyOccasionsWithSpanGapCrossCarrierSch-r16 ENUMERATED {mode2, mode3} OPTIONAL + ]] +} + +Phy-ParametersXDD-Diff ::= SEQUENCE { + dynamicSFI ENUMERATED {supported} OPTIONAL, + twoPUCCH-F0-2-ConsecSymbols ENUMERATED {supported} OPTIONAL, + twoDifferentTPC-Loop-PUSCH ENUMERATED {supported} OPTIONAL, + twoDifferentTPC-Loop-PUCCH ENUMERATED {supported} OPTIONAL, + ..., + [[ + dl-SchedulingOffset-PDSCH-TypeA ENUMERATED {supported} OPTIONAL, + dl-SchedulingOffset-PDSCH-TypeB ENUMERATED {supported} OPTIONAL, + ul-SchedulingOffset ENUMERATED {supported} OPTIONAL + ]] +} + +Phy-ParametersFRX-Diff ::= SEQUENCE { + dynamicSFI ENUMERATED {supported} OPTIONAL, + dummy1 BIT STRING (SIZE (2)) OPTIONAL, + twoFL-DMRS BIT STRING (SIZE (2)) OPTIONAL, + dummy2 BIT STRING (SIZE (2)) OPTIONAL, + dummy3 BIT STRING (SIZE (2)) OPTIONAL, + supportedDMRS-TypeDL ENUMERATED {type1, type1And2} OPTIONAL, + supportedDMRS-TypeUL ENUMERATED {type1, type1And2} OPTIONAL, + semiOpenLoopCSI ENUMERATED {supported} OPTIONAL, + csi-ReportWithoutPMI ENUMERATED {supported} OPTIONAL, + csi-ReportWithoutCQI ENUMERATED {supported} OPTIONAL, + onePortsPTRS BIT STRING (SIZE (2)) OPTIONAL, + twoPUCCH-F0-2-ConsecSymbols ENUMERATED {supported} OPTIONAL, + pucch-F2-WithFH ENUMERATED {supported} OPTIONAL, + pucch-F3-WithFH ENUMERATED {supported} OPTIONAL, + pucch-F4-WithFH ENUMERATED {supported} OPTIONAL, + pucch-F0-2WithoutFH ENUMERATED {notSupported} OPTIONAL, + pucch-F1-3-4WithoutFH ENUMERATED {notSupported} OPTIONAL, + mux-SR-HARQ-ACK-CSI-PUCCH-MultiPerSlot ENUMERATED {supported} OPTIONAL, + uci-CodeBlockSegmentation ENUMERATED {supported} OPTIONAL, + onePUCCH-LongAndShortFormat ENUMERATED {supported} OPTIONAL, + twoPUCCH-AnyOthersInSlot ENUMERATED {supported} OPTIONAL, + intraSlotFreqHopping-PUSCH ENUMERATED {supported} OPTIONAL, + pusch-LBRM ENUMERATED {supported} OPTIONAL, + pdcch-BlindDetectionCA INTEGER (4..16) OPTIONAL, + tpc-PUSCH-RNTI ENUMERATED {supported} OPTIONAL, + tpc-PUCCH-RNTI ENUMERATED {supported} OPTIONAL, + tpc-SRS-RNTI ENUMERATED {supported} OPTIONAL, + absoluteTPC-Command ENUMERATED {supported} OPTIONAL, + twoDifferentTPC-Loop-PUSCH ENUMERATED {supported} OPTIONAL, + twoDifferentTPC-Loop-PUCCH ENUMERATED {supported} OPTIONAL, + pusch-HalfPi-BPSK ENUMERATED {supported} OPTIONAL, + pucch-F3-4-HalfPi-BPSK ENUMERATED {supported} OPTIONAL, + almostContiguousCP-OFDM-UL ENUMERATED {supported} OPTIONAL, + sp-CSI-RS ENUMERATED {supported} OPTIONAL, + sp-CSI-IM ENUMERATED {supported} OPTIONAL, + tdd-MultiDL-UL-SwitchPerSlot ENUMERATED {supported} OPTIONAL, + multipleCORESET ENUMERATED {supported} OPTIONAL, + ..., + [[ + csi-RS-IM-ReceptionForFeedback CSI-RS-IM-ReceptionForFeedback OPTIONAL, + csi-RS-ProcFrameworkForSRS CSI-RS-ProcFrameworkForSRS OPTIONAL, + csi-ReportFramework CSI-ReportFramework OPTIONAL, + mux-SR-HARQ-ACK-CSI-PUCCH-OncePerSlot SEQUENCE { + sameSymbol ENUMERATED {supported} OPTIONAL, + diffSymbol ENUMERATED {supported} OPTIONAL + } OPTIONAL, + mux-SR-HARQ-ACK-PUCCH ENUMERATED {supported} OPTIONAL, + mux-MultipleGroupCtrlCH-Overlap ENUMERATED {supported} OPTIONAL, + dl-SchedulingOffset-PDSCH-TypeA ENUMERATED {supported} OPTIONAL, + dl-SchedulingOffset-PDSCH-TypeB ENUMERATED {supported} OPTIONAL, + ul-SchedulingOffset ENUMERATED {supported} OPTIONAL, + dl-64QAM-MCS-TableAlt ENUMERATED {supported} OPTIONAL, + ul-64QAM-MCS-TableAlt ENUMERATED {supported} OPTIONAL, + cqi-TableAlt ENUMERATED {supported} OPTIONAL, + oneFL-DMRS-TwoAdditionalDMRS-UL ENUMERATED {supported} OPTIONAL, + twoFL-DMRS-TwoAdditionalDMRS-UL ENUMERATED {supported} OPTIONAL, + oneFL-DMRS-ThreeAdditionalDMRS-UL ENUMERATED {supported} OPTIONAL + ]], + [[ + pdcch-BlindDetectionNRDC SEQUENCE { + pdcch-BlindDetectionMCG-UE INTEGER (1..15), + pdcch-BlindDetectionSCG-UE INTEGER (1..15) + } OPTIONAL, + mux-HARQ-ACK-PUSCH-DiffSymbol ENUMERATED {supported} OPTIONAL + ]], + [[ + -- R1 11-1b: Type 1 HARQ-ACK codebook support for relative TDRA for DL + type1-HARQ-ACK-Codebook-r16 ENUMERATED {supported} OPTIONAL, + -- R1 11-8: Enhanced UL power control scheme + enhancedPowerControl-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-1b-1: TCI state activation across multiple CCs + simultaneousTCI-ActMultipleCC-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-1b-2: Spatial relation update across multiple CCs + simultaneousSpatialRelationMultipleCC-r16 ENUMERATED {supported} OPTIONAL, + cli-RSSI-FDM-DL-r16 ENUMERATED {supported} OPTIONAL, + cli-SRS-RSRP-FDM-DL-r16 ENUMERATED {supported} OPTIONAL, + -- R1 19-3: Maximum MIMO Layer Adaptation + maxLayersMIMO-Adaptation-r16 ENUMERATED {supported} OPTIONAL, + -- R1 12-5: Configuration of aggregation factor per SPS configuration + aggregationFactorSPS-DL-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-1g: Resources for beam management, pathloss measurement, BFD, RLM and new beam identification + maxTotalResourcesForOneFreqRange-r16 SEQUENCE { + maxNumberResWithinSlotAcrossCC-OneFR-r16 ENUMERATED {n2, n4, n8, n12, n16, n32, n64, n128} OPTIONAL, + maxNumberResAcrossCC-OneFR-r16 ENUMERATED {n2, n4, n8, n12, n16, n32, n40, n48, n64, n72, n80, n96, n128, n256} + OPTIONAL + } OPTIONAL, + -- R1 16-7: Extension of the maximum number of configured aperiodic CSI report settings + csi-ReportFrameworkExt-r16 CSI-ReportFrameworkExt-r16 OPTIONAL + ]], + [[ + twoTCI-Act-servingCellInCC-List-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +Phy-ParametersFR1 ::= SEQUENCE { + pdcch-MonitoringSingleOccasion ENUMERATED {supported} OPTIONAL, + scs-60kHz ENUMERATED {supported} OPTIONAL, + pdsch-256QAM-FR1 ENUMERATED {supported} OPTIONAL, + pdsch-RE-MappingFR1-PerSymbol ENUMERATED {n10, n20} OPTIONAL, + ..., + [[ + pdsch-RE-MappingFR1-PerSlot ENUMERATED {n16, n32, n48, n64, n80, n96, n112, n128, + n144, n160, n176, n192, n208, n224, n240, n256} OPTIONAL + ]] +} + +Phy-ParametersFR2 ::= SEQUENCE { + dummy ENUMERATED {supported} OPTIONAL, + pdsch-RE-MappingFR2-PerSymbol ENUMERATED {n6, n20} OPTIONAL, + ..., + [[ + pCell-FR2 ENUMERATED {supported} OPTIONAL, + pdsch-RE-MappingFR2-PerSlot ENUMERATED {n16, n32, n48, n64, n80, n96, n112, n128, + n144, n160, n176, n192, n208, n224, n240, n256} OPTIONAL + ]], + [[ + -- R1 16-1c: Support of default spatial relation and pathloss reference RS for dedicated-PUCCH/SRS and PUSCH + defaultSpatialRelationPathlossRS-r16 ENUMERATED {supported} OPTIONAL, + -- R1 16-1d: Support of spatial relation update for AP-SRS via MAC CE + spatialRelationUpdateAP-SRS-r16 ENUMERATED {supported} OPTIONAL, + maxNumberSRS-PosSpatialRelationsAllServingCells-r16 ENUMERATED {n0, n1, n2, n4, n8, n16} OPTIONAL + ]] +} + +-- TAG-PHY-PARAMETERS-STOP +-- TAG-PHY-PARAMETERSMRDC-START + +Phy-ParametersMRDC ::= SEQUENCE { + naics-Capability-List SEQUENCE (SIZE (1..maxNrofNAICS-Entries)) OF NAICS-Capability-Entry OPTIONAL, + ..., + [[ + spCellPlacement CarrierAggregationVariant OPTIONAL + ]], + [[ + -- R1 18-3b: Semi-statically configured LTE UL transmissions in all UL subframes not limited to tdm-pattern in case of TDD PCell + tdd-PCellUL-TX-AllUL-Subframe-r16 ENUMERATED {supported} OPTIONAL, + -- R1 18-3a: Semi-statically configured LTE UL transmissions in all UL subframes not limited to tdm-pattern in case of FDD PCell + fdd-PCellUL-TX-AllUL-Subframe-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +NAICS-Capability-Entry ::= SEQUENCE { + numberOfNAICS-CapableCC INTEGER(1..5), + numberOfAggregatedPRB ENUMERATED {n50, n75, n100, n125, n150, n175, n200, n225, + n250, n275, n300, n350, n400, n450, n500, spare}, + ... +} + +-- TAG-PHY-PARAMETERSMRDC-STOP +-- TAG-PHY-PARAMETERSSHAREDSPECTRUMCHACCESS-START + +Phy-ParametersSharedSpectrumChAccess-r16 ::= SEQUENCE { + -- 10-32 (1-2): SS block based SINR measurement (SS-SINR) for unlicensed spectrum + ss-SINR-Meas-r16 ENUMERATED {supported} OPTIONAL, + -- 10-33 (2-32a): Semi-persistent CSI report on PUCCH for unlicensed spectrum + sp-CSI-ReportPUCCH-r16 ENUMERATED {supported} OPTIONAL, + -- 10-33a (2-32b): Semi-persistent CSI report on PUSCH for unlicensed spectrum + sp-CSI-ReportPUSCH-r16 ENUMERATED {supported} OPTIONAL, + -- 10-34 (3-6): Dynamic SFI monitoring for unlicensed spectrum + dynamicSFI-r16 ENUMERATED {supported} OPTIONAL, + -- 10-35c (4-19c): SR/HARQ-ACK/CSI multiplexing once per slot using a PUCCH (or HARQ-ACK/CSI piggybacked on a PUSCH) when SR/HARQ- + -- ACK/CSI are supposed to be sent with different starting symbols in a slot for unlicensed spectrum + -- 10-35 (4-19): SR/HARQ-ACK/CSI multiplexing once per slot using a PUCCH (or HARQ-ACK/CSI piggybacked on a PUSCH) when SR/HARQ- + -- ACK/CSI are supposed to be sent with the same starting symbol on the PUCCH resources in a slot for unlicensed spectrum + mux-SR-HARQ-ACK-CSI-PUCCH-OncePerSlot-r16 SEQUENCE { + sameSymbol-r16 ENUMERATED {supported} OPTIONAL, + diffSymbol-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + -- 10-35a (4-19a): Overlapping PUCCH resources have different starting symbols in a slot for unlicensed spectrum + mux-SR-HARQ-ACK-PUCCH-r16 ENUMERATED {supported} OPTIONAL, + -- 10-35b (4-19b): SR/HARQ-ACK/CSI multiplexing more than once per slot using a PUCCH (or HARQ-ACK/CSI piggybacked on a PUSCH) when + -- SR/HARQ ACK/CSI are supposed to be sent with the same or different starting symbol in a slot for unlicensed spectrum + mux-SR-HARQ-ACK-CSI-PUCCH-MultiPerSlot-r16 ENUMERATED {supported} OPTIONAL, + -- 10-36 (4-28): HARQ-ACK multiplexing on PUSCH with different PUCCH/PUSCH starting OFDM symbols for unlicensed spectrum + mux-HARQ-ACK-PUSCH-DiffSymbol-r16 ENUMERATED {supported} OPTIONAL, + -- 10-37 (4-23): Repetitions for PUCCH format 1, 3, and 4 over multiple slots with K = 2, 4, 8 for unlicensed spectrum + pucch-Repetition-F1-3-4-r16 ENUMERATED {supported} OPTIONAL, + -- 10-38 (5-14): Type 1 configured PUSCH repetitions over multiple slots for unlicensed spectrum + type1-PUSCH-RepetitionMultiSlots-r16 ENUMERATED {supported} OPTIONAL, + -- 10-39 (5-16): Type 2 configured PUSCH repetitions over multiple slots for unlicensed spectrum + type2-PUSCH-RepetitionMultiSlots-r16 ENUMERATED {supported} OPTIONAL, + -- 10-40 (5-17): PUSCH repetitions over multiple slots for unlicensed spectrum + pusch-RepetitionMultiSlots-r16 ENUMERATED {supported} OPTIONAL, + -- 10-40a (5-17a): PDSCH repetitions over multiple slots for unlicensed spectrum + pdsch-RepetitionMultiSlots-r16 ENUMERATED {supported} OPTIONAL, + -- 10-41 (5-18): DL SPS + downlinkSPS-r16 ENUMERATED {supported} OPTIONAL, + -- 10-42 (5-19): Type 1 Configured UL grant + configuredUL-GrantType1-r16 ENUMERATED {supported} OPTIONAL, + -- 10-43 (5-20): Type 2 Configured UL grant + configuredUL-GrantType2-r16 ENUMERATED {supported} OPTIONAL, + -- 10-44 (5-21): Pre-emption indication for DL + pre-EmptIndication-DL-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +-- TAG-PHY-PARAMETERSSHAREDSPECTRUMCHACCESS-STOP +-- TAG-POWSAV-PARAMETERS-START + +PowSav-Parameters-r16 ::= SEQUENCE { + powSav-ParametersCommon-r16 PowSav-ParametersCommon-r16 OPTIONAL, + powSav-ParametersFRX-Diff-r16 PowSav-ParametersFRX-Diff-r16 OPTIONAL, + ... +} + +PowSav-ParametersCommon-r16 ::= SEQUENCE { + drx-Preference-r16 ENUMERATED {supported} OPTIONAL, + maxCC-Preference-r16 ENUMERATED {supported} OPTIONAL, + releasePreference-r16 ENUMERATED {supported} OPTIONAL, + -- R1 19-4a: UE assistance information + minSchedulingOffsetPreference-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +PowSav-ParametersFRX-Diff-r16 ::= SEQUENCE { + maxBW-Preference-r16 ENUMERATED {supported} OPTIONAL, + maxMIMO-LayerPreference-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +-- TAG-POWSAV-PARAMETERS-STOP +-- TAG-PROCESSINGPARAMETERS-START + +ProcessingParameters ::= SEQUENCE { + fallback ENUMERATED {sc, cap1-only}, + differentTB-PerSlot SEQUENCE { + upto1 NumberOfCarriers OPTIONAL, + upto2 NumberOfCarriers OPTIONAL, + upto4 NumberOfCarriers OPTIONAL, + upto7 NumberOfCarriers OPTIONAL + } OPTIONAL +} + +NumberOfCarriers ::= INTEGER (1..16) + +-- TAG-PROCESSINGPARAMETERS-STOP +-- TAG-RAT-TYPE-START + +RAT-Type ::= ENUMERATED {nr, eutra-nr, eutra, utra-fdd-v1610, ...} + +-- TAG-RAT-TYPE-STOP +-- TAG-RF-PARAMETERS-START + +RF-Parameters ::= SEQUENCE { + supportedBandListNR SEQUENCE (SIZE (1..maxBands)) OF BandNR, + supportedBandCombinationList BandCombinationList OPTIONAL, + appliedFreqBandListFilter FreqBandList OPTIONAL, + ..., + [[ + supportedBandCombinationList-v1540 BandCombinationList-v1540 OPTIONAL, + srs-SwitchingTimeRequested ENUMERATED {true} OPTIONAL + ]], + [[ + supportedBandCombinationList-v1550 BandCombinationList-v1550 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1560 BandCombinationList-v1560 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1610 BandCombinationList-v1610 OPTIONAL, + supportedBandCombinationListSidelinkEUTRA-NR-r16 BandCombinationListSidelinkEUTRA-NR-r16 OPTIONAL, + supportedBandCombinationList-UplinkTxSwitch-r16 BandCombinationList-UplinkTxSwitch-r16 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1630 BandCombinationList-v1630 OPTIONAL, + supportedBandCombinationListSidelinkEUTRA-NR-v1630 BandCombinationListSidelinkEUTRA-NR-v1630 OPTIONAL, + supportedBandCombinationList-UplinkTxSwitch-v1630 BandCombinationList-UplinkTxSwitch-v1630 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1640 BandCombinationList-v1640 OPTIONAL, + supportedBandCombinationList-UplinkTxSwitch-v1640 BandCombinationList-UplinkTxSwitch-v1640 OPTIONAL + ]] + +} + +BandNR ::= SEQUENCE { + bandNR FreqBandIndicatorNR, + modifiedMPR-Behaviour BIT STRING (SIZE (8)) OPTIONAL, + mimo-ParametersPerBand MIMO-ParametersPerBand OPTIONAL, + extendedCP ENUMERATED {supported} OPTIONAL, + multipleTCI ENUMERATED {supported} OPTIONAL, + bwp-WithoutRestriction ENUMERATED {supported} OPTIONAL, + bwp-SameNumerology ENUMERATED {upto2, upto4} OPTIONAL, + bwp-DiffNumerology ENUMERATED {upto4} OPTIONAL, + crossCarrierScheduling-SameSCS ENUMERATED {supported} OPTIONAL, + pdsch-256QAM-FR2 ENUMERATED {supported} OPTIONAL, + pusch-256QAM ENUMERATED {supported} OPTIONAL, + ue-PowerClass ENUMERATED {pc1, pc2, pc3, pc4} OPTIONAL, + rateMatchingLTE-CRS ENUMERATED {supported} OPTIONAL, + channelBWs-DL CHOICE { + fr1 SEQUENCE { + scs-15kHz BIT STRING (SIZE (10)) OPTIONAL, + scs-30kHz BIT STRING (SIZE (10)) OPTIONAL, + scs-60kHz BIT STRING (SIZE (10)) OPTIONAL + }, + fr2 SEQUENCE { + scs-60kHz BIT STRING (SIZE (3)) OPTIONAL, + scs-120kHz BIT STRING (SIZE (3)) OPTIONAL + } + } OPTIONAL, + channelBWs-UL CHOICE { + fr1 SEQUENCE { + scs-15kHz BIT STRING (SIZE (10)) OPTIONAL, + scs-30kHz BIT STRING (SIZE (10)) OPTIONAL, + scs-60kHz BIT STRING (SIZE (10)) OPTIONAL + }, + fr2 SEQUENCE { + scs-60kHz BIT STRING (SIZE (3)) OPTIONAL, + scs-120kHz BIT STRING (SIZE (3)) OPTIONAL + } + } OPTIONAL, + ..., + [[ + maxUplinkDutyCycle-PC2-FR1 ENUMERATED {n60, n70, n80, n90, n100} OPTIONAL + ]], + [[ + pucch-SpatialRelInfoMAC-CE ENUMERATED {supported} OPTIONAL, + powerBoosting-pi2BPSK ENUMERATED {supported} OPTIONAL + ]], + [[ + maxUplinkDutyCycle-FR2 ENUMERATED {n15, n20, n25, n30, n40, n50, n60, n70, n80, n90, n100} OPTIONAL + ]], + [[ + channelBWs-DL-v1590 CHOICE { + fr1 SEQUENCE { + scs-15kHz BIT STRING (SIZE (16)) OPTIONAL, + scs-30kHz BIT STRING (SIZE (16)) OPTIONAL, + scs-60kHz BIT STRING (SIZE (16)) OPTIONAL + }, + fr2 SEQUENCE { + scs-60kHz BIT STRING (SIZE (8)) OPTIONAL, + scs-120kHz BIT STRING (SIZE (8)) OPTIONAL + } + } OPTIONAL, + channelBWs-UL-v1590 CHOICE { + fr1 SEQUENCE { + scs-15kHz BIT STRING (SIZE (16)) OPTIONAL, + scs-30kHz BIT STRING (SIZE (16)) OPTIONAL, + scs-60kHz BIT STRING (SIZE (16)) OPTIONAL + }, + fr2 SEQUENCE { + scs-60kHz BIT STRING (SIZE (8)) OPTIONAL, + scs-120kHz BIT STRING (SIZE (8)) OPTIONAL + } + } OPTIONAL + ]], + [[ + asymmetricBandwidthCombinationSet BIT STRING (SIZE (1..32)) OPTIONAL + ]], + [[ + -- R1 10: NR-unlicensed + sharedSpectrumChAccessParamsPerBand-r16 SharedSpectrumChAccessParamsPerBand-r16 OPTIONAL, + -- R1 11-7b: Independent cancellation of the overlapping PUSCHs in an intra-band UL CA + cancelOverlappingPUSCH-r16 ENUMERATED {supported} OPTIONAL, + -- R1 14-1: Multiple LTE-CRS rate matching patterns + multipleRateMatchingEUTRA-CRS-r16 SEQUENCE { + maxNumberPatterns-r16 INTEGER (2..6), + maxNumberNon-OverlapPatterns-r16 INTEGER (1..3) + } OPTIONAL, + -- R1 14-1a: Two LTE-CRS overlapping rate matching patterns within a part of NR carrier using 15 kHz overlapping with a LTE carrier + overlapRateMatchingEUTRA-CRS-r16 ENUMERATED {supported} OPTIONAL, + -- R1 14-2: PDSCH Type B mapping of length 9 and 10 OFDM symbols + pdsch-MappingTypeB-Alt-r16 ENUMERATED {supported} OPTIONAL, + -- R1 14-3: One slot periodic TRS configuration for FR1 + oneSlotPeriodicTRS-r16 ENUMERATED {supported} OPTIONAL, + olpc-SRS-Pos-r16 OLPC-SRS-Pos-r16 OPTIONAL, + spatialRelationsSRS-Pos-r16 SpatialRelationsSRS-Pos-r16 OPTIONAL, + simulSRS-MIMO-TransWithinBand-r16 ENUMERATED {n2} OPTIONAL, + channelBW-DL-IAB-r16 CHOICE { + fr1-100mhz SEQUENCE { + scs-15kHz ENUMERATED {supported} OPTIONAL, + scs-30kHz ENUMERATED {supported} OPTIONAL, + scs-60kHz ENUMERATED {supported} OPTIONAL + }, + fr2-200mhz SEQUENCE { + scs-60kHz ENUMERATED {supported} OPTIONAL, + scs-120kHz ENUMERATED {supported} OPTIONAL + } + } OPTIONAL, + channelBW-UL-IAB-r16 CHOICE { + fr1-100mhz SEQUENCE { + scs-15kHz ENUMERATED {supported} OPTIONAL, + scs-30kHz ENUMERATED {supported} OPTIONAL, + scs-60kHz ENUMERATED {supported} OPTIONAL + }, + fr2-200mhz SEQUENCE { + scs-60kHz ENUMERATED {supported} OPTIONAL, + scs-120kHz ENUMERATED {supported} OPTIONAL + } + } OPTIONAL, + rasterShift7dot5-IAB-r16 ENUMERATED {supported} OPTIONAL, + ue-PowerClass-v1610 ENUMERATED {pc1dot5} OPTIONAL, + condHandover-r16 ENUMERATED {supported} OPTIONAL, + condHandoverFailure-r16 ENUMERATED {supported} OPTIONAL, + condHandoverTwoTriggerEvents-r16 ENUMERATED {supported} OPTIONAL, + condPSCellChange-r16 ENUMERATED {supported} OPTIONAL, + condPSCellChangeTwoTriggerEvents-r16 ENUMERATED {supported} OPTIONAL, + mpr-PowerBoost-FR2-r16 ENUMERATED {supported} OPTIONAL, + + -- R1 11-9: Multiple active configured grant configurations for a BWP of a serving cell + activeConfiguredGrant-r16 SEQUENCE { + maxNumberConfigsPerBWP-r16 ENUMERATED {n1, n2, n4, n8, n12}, + maxNumberConfigsAllCC-r16 INTEGER (2..32) + } OPTIONAL, + -- R1 11-9a: Joint release in a DCI for two or more configured grant Type 2 configurations for a given BWP of a serving cell + jointReleaseConfiguredGrantType2-r16 ENUMERATED {supported} OPTIONAL, + -- R1 12-2: Multiple SPS configurations + sps-r16 SEQUENCE { + maxNumberConfigsPerBWP-r16 INTEGER (1..8), + maxNumberConfigsAllCC-r16 INTEGER (2..32) + } OPTIONAL, + -- R1 12-2a: Joint release in a DCI for two or more SPS configurations for a given BWP of a serving cell + jointReleaseSPS-r16 ENUMERATED {supported} OPTIONAL, + -- R1 13-19: Simultaneous positioning SRS and MIMO SRS transmission within a band across multiple CCs + simulSRS-TransWithinBand-r16 ENUMERATED {n2} OPTIONAL, + trs-AdditionalBandwidth-r16 ENUMERATED {trs-AddBW-Set1, trs-AddBW-Set2} OPTIONAL, + handoverIntraF-IAB-r16 ENUMERATED {supported} OPTIONAL + ]], + [[ + -- R1 22-5a: Simultaneous transmission of SRS for antenna switching and SRS for CB/NCB /BM for intra-band UL CA + -- R1 22-5c: Simultaneous transmission of SRS for antenna switching and SRS for antenna switching for intra-band UL CA + simulTX-SRS-AntSwitchingIntraBandUL-CA-r16 SimulSRS-ForAntennaSwitching-r16 OPTIONAL, + -- R1 10: NR-unlicensed + sharedSpectrumChAccessParamsPerBand-v1630 SharedSpectrumChAccessParamsPerBand-v1630 OPTIONAL + ]], + [[ + handoverUTRA-FDD-r16 ENUMERATED {supported} OPTIONAL, + -- R4 7-4: Report the shorter transient capability supported by the UE: 2, 4 or 7us + enhancedUL-TransientPeriod-r16 ENUMERATED {us2, us4, us7} OPTIONAL, + sharedSpectrumChAccessParamsPerBand-v1640 SharedSpectrumChAccessParamsPerBand-v1640 OPTIONAL + ]] +} + +-- TAG-RF-PARAMETERS-STOP +-- TAG-RF-PARAMETERSMRDC-START + +RF-ParametersMRDC ::= SEQUENCE { + supportedBandCombinationList BandCombinationList OPTIONAL, + appliedFreqBandListFilter FreqBandList OPTIONAL, + ..., + [[ + srs-SwitchingTimeRequested ENUMERATED {true} OPTIONAL, + supportedBandCombinationList-v1540 BandCombinationList-v1540 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1550 BandCombinationList-v1550 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1560 BandCombinationList-v1560 OPTIONAL, + supportedBandCombinationListNEDC-Only BandCombinationList OPTIONAL + ]], + [[ + supportedBandCombinationList-v1570 BandCombinationList-v1570 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1580 BandCombinationList-v1580 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1590 BandCombinationList-v1590 OPTIONAL + ]], + [[ + supportedBandCombinationListNEDC-Only-v15a0 SEQUENCE { + supportedBandCombinationList-v1540 BandCombinationList-v1540 OPTIONAL, + supportedBandCombinationList-v1560 BandCombinationList-v1560 OPTIONAL, + supportedBandCombinationList-v1570 BandCombinationList-v1570 OPTIONAL, + supportedBandCombinationList-v1580 BandCombinationList-v1580 OPTIONAL, + supportedBandCombinationList-v1590 BandCombinationList-v1590 OPTIONAL + } OPTIONAL + ]], + [[ + supportedBandCombinationList-v1610 BandCombinationList-v1610 OPTIONAL, + supportedBandCombinationListNEDC-Only-v1610 BandCombinationList-v1610 OPTIONAL, + supportedBandCombinationList-UplinkTxSwitch-r16 BandCombinationList-UplinkTxSwitch-r16 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1630 BandCombinationList-v1630 OPTIONAL, + supportedBandCombinationListNEDC-Only-v1630 BandCombinationList-v1630 OPTIONAL, + supportedBandCombinationList-UplinkTxSwitch-v1630 BandCombinationList-UplinkTxSwitch-v1630 OPTIONAL + ]], + [[ + supportedBandCombinationList-v1640 BandCombinationList-v1640 OPTIONAL, + supportedBandCombinationListNEDC-Only-v1640 BandCombinationList-v1640 OPTIONAL, + supportedBandCombinationList-UplinkTxSwitch-v1640 BandCombinationList-UplinkTxSwitch-v1640 OPTIONAL + ]] +} + +-- TAG-RF-PARAMETERSMRDC-STOP +-- TAG-RLC-PARAMETERS-START + +RLC-Parameters ::= SEQUENCE { + am-WithShortSN ENUMERATED {supported} OPTIONAL, + um-WithShortSN ENUMERATED {supported} OPTIONAL, + um-WithLongSN ENUMERATED {supported} OPTIONAL, + ..., + [[ + extendedT-PollRetransmit-r16 ENUMERATED {supported} OPTIONAL, + extendedT-StatusProhibit-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +-- TAG-RLC-PARAMETERS-STOP +-- TAG-SDAP-PARAMETERS-START + +SDAP-Parameters ::= SEQUENCE { + as-ReflectiveQoS ENUMERATED {true} OPTIONAL, + ..., + [[ + sdap-QOS-IAB-r16 ENUMERATED {supported} OPTIONAL, + sdapHeaderIAB-r16 ENUMERATED {supported} OPTIONAL + ]] + +} + +-- TAG-SDAP-PARAMETERS-STOP +-- TAG-SIDELINKPARAMETERS-START + +SidelinkParameters-r16 ::= SEQUENCE { + sidelinkParametersNR-r16 SidelinkParametersNR-r16 OPTIONAL, + sidelinkParametersEUTRA-r16 SidelinkParametersEUTRA-r16 OPTIONAL +} + +SidelinkParametersNR-r16 ::= SEQUENCE { + rlc-ParametersSidelink-r16 RLC-ParametersSidelink-r16 OPTIONAL, + mac-ParametersSidelink-r16 MAC-ParametersSidelink-r16 OPTIONAL, + fdd-Add-UE-Sidelink-Capabilities-r16 UE-SidelinkCapabilityAddXDD-Mode-r16 OPTIONAL, + tdd-Add-UE-Sidelink-Capabilities-r16 UE-SidelinkCapabilityAddXDD-Mode-r16 OPTIONAL, + supportedBandListSidelink-r16 SEQUENCE (SIZE (1..maxBands)) OF BandSidelink-r16 OPTIONAL, + ... +} + +SidelinkParametersEUTRA-r16 ::= SEQUENCE { + sl-ParametersEUTRA1-r16 OCTET STRING OPTIONAL, + sl-ParametersEUTRA2-r16 OCTET STRING OPTIONAL, + sl-ParametersEUTRA3-r16 OCTET STRING OPTIONAL, + supportedBandListSidelinkEUTRA-r16 SEQUENCE (SIZE (1..maxBandsEUTRA)) OF BandSidelinkEUTRA-r16 OPTIONAL, + ... +} + +RLC-ParametersSidelink-r16 ::= SEQUENCE { + am-WithLongSN-Sidelink-r16 ENUMERATED {supported} OPTIONAL, + um-WithLongSN-Sidelink-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +MAC-ParametersSidelink-r16 ::= SEQUENCE { + mac-ParametersSidelinkCommon-r16 MAC-ParametersSidelinkCommon-r16 OPTIONAL, + mac-ParametersSidelinkXDD-Diff-r16 MAC-ParametersSidelinkXDD-Diff-r16 OPTIONAL, + ... +} + +UE-SidelinkCapabilityAddXDD-Mode-r16 ::= SEQUENCE { + mac-ParametersSidelinkXDD-Diff-r16 MAC-ParametersSidelinkXDD-Diff-r16 OPTIONAL +} + +MAC-ParametersSidelinkCommon-r16 ::= SEQUENCE { + lcp-RestrictionSidelink-r16 ENUMERATED {supported} OPTIONAL, + multipleConfiguredGrantsSidelink-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +MAC-ParametersSidelinkXDD-Diff-r16 ::= SEQUENCE { + multipleSR-ConfigurationsSidelink-r16 ENUMERATED {supported} OPTIONAL, + logicalChannelSR-DelayTimerSidelink-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +BandSidelinkEUTRA-r16 ::= SEQUENCE { + freqBandSidelinkEUTRA-r16 FreqBandIndicatorEUTRA, + -- R1 15-7: Transmitting LTE sidelink mode 3 scheduled by NR Uu + gnb-ScheduledMode3SidelinkEUTRA-r16 SEQUENCE { + gnb-ScheduledMode3DelaySidelinkEUTRA-r16 ENUMERATED {ms0, ms0dot25, ms0dot5, ms0dot625, ms0dot75, ms1, + ms1dot25, ms1dot5, ms1dot75, ms2, ms2dot5, ms3, ms4, + ms5, ms6, ms8, ms10, ms20} + } OPTIONAL, + -- R1 15-9: Transmitting LTE sidelink mode 4 configured by NR Uu + gnb-ScheduledMode4SidelinkEUTRA-r16 ENUMERATED {supported} OPTIONAL +} + +BandSidelink-r16 ::= SEQUENCE { + freqBandSidelink-r16 FreqBandIndicatorNR, + --15-1 + sl-Reception-r16 SEQUENCE { + harq-RxProcessSidelink-r16 ENUMERATED {n16, n24, n32, n48, n64}, + pscch-RxSidelink-r16 ENUMERATED {value1, value2}, + scs-CP-PatternRxSidelink-r16 CHOICE { + fr1-r16 SEQUENCE { + scs-15kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-30kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-60kHz-r16 BIT STRING (SIZE (16)) OPTIONAL + }, + fr2-r16 SEQUENCE { + scs-60kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-120kHz-r16 BIT STRING (SIZE (16)) OPTIONAL + } + } OPTIONAL, + extendedCP-RxSidelink-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + --15-2 + sl-TransmissionMode1-r16 SEQUENCE { + harq-TxProcessModeOneSidelink-r16 ENUMERATED {n8, n16}, + scs-CP-PatternTxSidelinkModeOne-r16 CHOICE { + fr1-r16 SEQUENCE { + scs-15kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-30kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-60kHz-r16 BIT STRING (SIZE (16)) OPTIONAL + }, + fr2-r16 SEQUENCE { + scs-60kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-120kHz-r16 BIT STRING (SIZE (16)) OPTIONAL + } + }, + extendedCP-TxSidelink-r16 ENUMERATED {supported} OPTIONAL, + harq-ReportOnPUCCH-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + --15-4 + sync-Sidelink-r16 SEQUENCE { + gNB-Sync-r16 ENUMERATED {supported} OPTIONAL, + gNB-GNSS-UE-SyncWithPriorityOnGNB-ENB-r16 ENUMERATED {supported} OPTIONAL, + gNB-GNSS-UE-SyncWithPriorityOnGNSS-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + --15-10 + sl-Tx-256QAM-r16 ENUMERATED {supported} OPTIONAL, + --15-11 + psfch-FormatZeroSidelink-r16 SEQUENCE { + psfch-RxNumber ENUMERATED {n5, n15, n25, n32, n35, n45, n50, n64}, + psfch-TxNumber ENUMERATED {n4, n8, n16} + } OPTIONAL, + --15-12 + lowSE-64QAM-MCS-TableSidelink-r16 ENUMERATED {supported} OPTIONAL, + --15-15 + enb-sync-Sidelink-r16 ENUMERATED {supported} OPTIONAL, + ..., + [[ + --15-3 + sl-TransmissionMode2-r16 SEQUENCE { + harq-TxProcessModeTwoSidelink-r16 ENUMERATED {n8, n16}, + scs-CP-PatternTxSidelinkModeTwo-r16 ENUMERATED {supported} OPTIONAL, + dl-openLoopPC-Sidelink-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + --15-5 + congestionControlSidelink-r16 SEQUENCE { + cbr-ReportSidelink-r16 ENUMERATED {supported} OPTIONAL, + cbr-CR-TimeLimitSidelink-r16 ENUMERATED {time1, time2} + } OPTIONAL, + --15-22 + fewerSymbolSlotSidelink-r16 ENUMERATED {supported} OPTIONAL, + --15-23 + sl-openLoopPC-RSRP-ReportSidelink-r16 ENUMERATED {supported} OPTIONAL, + --13-1 + sl-Rx-256QAM-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +-- TAG-SIDELINKPARAMETERS-STOP +-- TAG-SON-PARAMETERS-START + +SON-Parameters-r16 ::= SEQUENCE { + rach-Report-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +-- TAG-SON-PARAMETERS-STOP +-- TAG-SPATIALRELATIONSSRS-POS-START + +SpatialRelationsSRS-Pos-r16 ::= SEQUENCE { + spatialRelation-SRS-PosBasedOnSSB-Serving-r16 ENUMERATED {supported} OPTIONAL, + spatialRelation-SRS-PosBasedOnCSI-RS-Serving-r16 ENUMERATED {supported} OPTIONAL, + spatialRelation-SRS-PosBasedOnPRS-Serving-r16 ENUMERATED {supported} OPTIONAL, + spatialRelation-SRS-PosBasedOnSRS-r16 ENUMERATED {supported} OPTIONAL, + spatialRelation-SRS-PosBasedOnSSB-Neigh-r16 ENUMERATED {supported} OPTIONAL, + spatialRelation-SRS-PosBasedOnPRS-Neigh-r16 ENUMERATED {supported} OPTIONAL +} + +--TAG-SPATIALRELATIONSSRS-POS-STOP +-- TAG-SRS-SWITCHINGTIMENR-START + +SRS-SwitchingTimeNR ::= SEQUENCE { + switchingTimeDL ENUMERATED {n0us, n30us, n100us, n140us, n200us, n300us, n500us, n900us} OPTIONAL, + switchingTimeUL ENUMERATED {n0us, n30us, n100us, n140us, n200us, n300us, n500us, n900us} OPTIONAL +} + +-- TAG-SRS-SWITCHINGTIMENR-STOP +-- TAG-SRS-SWITCHINGTIMEEUTRA-START + +SRS-SwitchingTimeEUTRA ::= SEQUENCE { + switchingTimeDL ENUMERATED {n0, n0dot5, n1, n1dot5, n2, n2dot5, n3, n3dot5, n4, n4dot5, n5, n5dot5, n6, n6dot5, n7} + OPTIONAL, + switchingTimeUL ENUMERATED {n0, n0dot5, n1, n1dot5, n2, n2dot5, n3, n3dot5, n4, n4dot5, n5, n5dot5, n6, n6dot5, n7} + OPTIONAL +} +-- TAG-SRS-SWITCHINGTIMEEUTRA-STOP +-- TAG-SUPPORTEDBANDWIDTH-START + +SupportedBandwidth ::= CHOICE { + fr1 ENUMERATED {mhz5, mhz10, mhz15, mhz20, mhz25, mhz30, mhz40, mhz50, mhz60, mhz80, mhz100}, + fr2 ENUMERATED {mhz50, mhz100, mhz200, mhz400} +} + +-- TAG-SUPPORTEDBANDWIDTH-STOP +-- TAG-UE-BASEDPERFMEAS-PARAMETERS-START + +UE-BasedPerfMeas-Parameters-r16 ::= SEQUENCE { + barometerMeasReport-r16 ENUMERATED {supported} OPTIONAL, + immMeasBT-r16 ENUMERATED {supported} OPTIONAL, + immMeasWLAN-r16 ENUMERATED {supported} OPTIONAL, + loggedMeasBT-r16 ENUMERATED {supported} OPTIONAL, + loggedMeasurements-r16 ENUMERATED {supported} OPTIONAL, + loggedMeasWLAN-r16 ENUMERATED {supported} OPTIONAL, + orientationMeasReport-r16 ENUMERATED {supported} OPTIONAL, + speedMeasReport-r16 ENUMERATED {supported} OPTIONAL, + gnss-Location-r16 ENUMERATED {supported} OPTIONAL, + ulPDCP-Delay-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +-- TAG-UE-BASEDPERFMEAS-PARAMETERS-STOP +-- TAG-UE-CAPABILITYRAT-CONTAINERLIST-START + +UE-CapabilityRAT-ContainerList ::= SEQUENCE (SIZE (0..maxRAT-CapabilityContainers)) OF UE-CapabilityRAT-Container + +UE-CapabilityRAT-Container ::= SEQUENCE { + rat-Type RAT-Type, + ue-CapabilityRAT-Container OCTET STRING +} + +-- TAG-UE-CAPABILITYRAT-CONTAINERLIST-STOP +-- TAG-UE-CAPABILITYRAT-REQUESTLIST-START + +UE-CapabilityRAT-RequestList ::= SEQUENCE (SIZE (1..maxRAT-CapabilityContainers)) OF UE-CapabilityRAT-Request + +UE-CapabilityRAT-Request ::= SEQUENCE { + rat-Type RAT-Type, + capabilityRequestFilter OCTET STRING OPTIONAL, -- Need N + ... +} + +-- TAG-UE-CAPABILITYRAT-REQUESTLIST-STOP +-- TAG-UE-CAPABILITYREQUESTFILTERCOMMON-START + +UE-CapabilityRequestFilterCommon ::= SEQUENCE { + mrdc-Request SEQUENCE { + omitEN-DC ENUMERATED {true} OPTIONAL, -- Need N + includeNR-DC ENUMERATED {true} OPTIONAL, -- Need N + includeNE-DC ENUMERATED {true} OPTIONAL -- Need N + } OPTIONAL, -- Need N + ..., + [[ + codebookTypeRequest-r16 SEQUENCE { + type1-SinglePanel-r16 ENUMERATED {true} OPTIONAL, -- Need N + type1-MultiPanel-r16 ENUMERATED {true} OPTIONAL, -- Need N + type2-r16 ENUMERATED {true} OPTIONAL, -- Need N + type2-PortSelection-r16 ENUMERATED {true} OPTIONAL -- Need N + } OPTIONAL, -- Need N + uplinkTxSwitchRequest-r16 ENUMERATED {true} OPTIONAL -- Need N + ]] +} + +-- TAG-UE-CAPABILITYREQUESTFILTERCOMMON-STOP +-- TAG-UE-CAPABILITYREQUESTFILTERNR-START + +UE-CapabilityRequestFilterNR ::= SEQUENCE { + frequencyBandListFilter FreqBandList OPTIONAL, -- Need N + nonCriticalExtension UE-CapabilityRequestFilterNR-v1540 OPTIONAL +} + +UE-CapabilityRequestFilterNR-v1540 ::= SEQUENCE { + srs-SwitchingTimeRequest ENUMERATED {true} OPTIONAL, -- Need N + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-UE-CAPABILITYREQUESTFILTERNR-STOP +-- TAG-UE-MRDC-CAPABILITY-START + +UE-MRDC-Capability ::= SEQUENCE { + measAndMobParametersMRDC MeasAndMobParametersMRDC OPTIONAL, + phy-ParametersMRDC-v1530 Phy-ParametersMRDC OPTIONAL, + rf-ParametersMRDC RF-ParametersMRDC, + generalParametersMRDC GeneralParametersMRDC-XDD-Diff OPTIONAL, + fdd-Add-UE-MRDC-Capabilities UE-MRDC-CapabilityAddXDD-Mode OPTIONAL, + tdd-Add-UE-MRDC-Capabilities UE-MRDC-CapabilityAddXDD-Mode OPTIONAL, + fr1-Add-UE-MRDC-Capabilities UE-MRDC-CapabilityAddFRX-Mode OPTIONAL, + fr2-Add-UE-MRDC-Capabilities UE-MRDC-CapabilityAddFRX-Mode OPTIONAL, + featureSetCombinations SEQUENCE (SIZE (1..maxFeatureSetCombinations)) OF FeatureSetCombination OPTIONAL, + pdcp-ParametersMRDC-v1530 PDCP-ParametersMRDC OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension UE-MRDC-Capability-v1560 OPTIONAL +} + +UE-MRDC-Capability-v1560 ::= SEQUENCE { + receivedFilters OCTET STRING (CONTAINING UECapabilityEnquiry-v1560-IEs) OPTIONAL, + measAndMobParametersMRDC-v1560 MeasAndMobParametersMRDC-v1560 OPTIONAL, + fdd-Add-UE-MRDC-Capabilities-v1560 UE-MRDC-CapabilityAddXDD-Mode-v1560 OPTIONAL, + tdd-Add-UE-MRDC-Capabilities-v1560 UE-MRDC-CapabilityAddXDD-Mode-v1560 OPTIONAL, + nonCriticalExtension UE-MRDC-Capability-v1610 OPTIONAL +} + +UE-MRDC-Capability-v1610 ::= SEQUENCE { + measAndMobParametersMRDC-v1610 MeasAndMobParametersMRDC-v1610 OPTIONAL, + generalParametersMRDC-v1610 GeneralParametersMRDC-v1610 OPTIONAL, + pdcp-ParametersMRDC-v1610 PDCP-ParametersMRDC-v1610 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +UE-MRDC-CapabilityAddXDD-Mode ::= SEQUENCE { + measAndMobParametersMRDC-XDD-Diff MeasAndMobParametersMRDC-XDD-Diff OPTIONAL, + generalParametersMRDC-XDD-Diff GeneralParametersMRDC-XDD-Diff OPTIONAL +} + +UE-MRDC-CapabilityAddXDD-Mode-v1560 ::= SEQUENCE { + measAndMobParametersMRDC-XDD-Diff-v1560 MeasAndMobParametersMRDC-XDD-Diff-v1560 OPTIONAL +} + +UE-MRDC-CapabilityAddFRX-Mode ::= SEQUENCE { + measAndMobParametersMRDC-FRX-Diff MeasAndMobParametersMRDC-FRX-Diff +} + + +GeneralParametersMRDC-XDD-Diff ::= SEQUENCE { + splitSRB-WithOneUL-Path ENUMERATED {supported} OPTIONAL, + splitDRB-withUL-Both-MCG-SCG ENUMERATED {supported} OPTIONAL, + srb3 ENUMERATED {supported} OPTIONAL, + dummy ENUMERATED {supported} OPTIONAL, + ... +} + +GeneralParametersMRDC-v1610 ::= SEQUENCE { + f1c-OverEUTRA-r16 ENUMERATED {supported} OPTIONAL +} + +-- TAG-UE-MRDC-CAPABILITY-STOP +-- TAG-UE-NR-CAPABILITY-START + +UE-NR-Capability ::= SEQUENCE { + accessStratumRelease AccessStratumRelease, + pdcp-Parameters PDCP-Parameters, + rlc-Parameters RLC-Parameters OPTIONAL, + mac-Parameters MAC-Parameters OPTIONAL, + phy-Parameters Phy-Parameters, + rf-Parameters RF-Parameters, + measAndMobParameters MeasAndMobParameters OPTIONAL, + fdd-Add-UE-NR-Capabilities UE-NR-CapabilityAddXDD-Mode OPTIONAL, + tdd-Add-UE-NR-Capabilities UE-NR-CapabilityAddXDD-Mode OPTIONAL, + fr1-Add-UE-NR-Capabilities UE-NR-CapabilityAddFRX-Mode OPTIONAL, + fr2-Add-UE-NR-Capabilities UE-NR-CapabilityAddFRX-Mode OPTIONAL, + featureSets FeatureSets OPTIONAL, + featureSetCombinations SEQUENCE (SIZE (1..maxFeatureSetCombinations)) OF FeatureSetCombination OPTIONAL, + lateNonCriticalExtension OCTET STRING (CONTAINING UE-NR-Capability-v15c0) OPTIONAL, + nonCriticalExtension UE-NR-Capability-v1530 OPTIONAL +} + +-- Regular non-critical extensions: +UE-NR-Capability-v1530 ::= SEQUENCE { + fdd-Add-UE-NR-Capabilities-v1530 UE-NR-CapabilityAddXDD-Mode-v1530 OPTIONAL, + tdd-Add-UE-NR-Capabilities-v1530 UE-NR-CapabilityAddXDD-Mode-v1530 OPTIONAL, + dummy ENUMERATED {supported} OPTIONAL, + interRAT-Parameters InterRAT-Parameters OPTIONAL, + inactiveState ENUMERATED {supported} OPTIONAL, + delayBudgetReporting ENUMERATED {supported} OPTIONAL, + nonCriticalExtension UE-NR-Capability-v1540 OPTIONAL +} + +UE-NR-Capability-v1540 ::= SEQUENCE { + sdap-Parameters SDAP-Parameters OPTIONAL, + overheatingInd ENUMERATED {supported} OPTIONAL, + ims-Parameters IMS-Parameters OPTIONAL, + fr1-Add-UE-NR-Capabilities-v1540 UE-NR-CapabilityAddFRX-Mode-v1540 OPTIONAL, + fr2-Add-UE-NR-Capabilities-v1540 UE-NR-CapabilityAddFRX-Mode-v1540 OPTIONAL, + fr1-fr2-Add-UE-NR-Capabilities UE-NR-CapabilityAddFRX-Mode OPTIONAL, + nonCriticalExtension UE-NR-Capability-v1550 OPTIONAL +} + +UE-NR-Capability-v1550 ::= SEQUENCE { + reducedCP-Latency ENUMERATED {supported} OPTIONAL, + nonCriticalExtension UE-NR-Capability-v1560 OPTIONAL +} + +UE-NR-Capability-v1560 ::= SEQUENCE { + nrdc-Parameters NRDC-Parameters OPTIONAL, + receivedFilters OCTET STRING (CONTAINING UECapabilityEnquiry-v1560-IEs) OPTIONAL, + nonCriticalExtension UE-NR-Capability-v1570 OPTIONAL +} + +UE-NR-Capability-v1570 ::= SEQUENCE { + nrdc-Parameters-v1570 NRDC-Parameters-v1570 OPTIONAL, + nonCriticalExtension UE-NR-Capability-v1610 OPTIONAL +} + +-- Late non-critical extensions: +UE-NR-Capability-v15c0 ::= SEQUENCE { + nrdc-Parameters-v15c0 NRDC-Parameters-v15c0 OPTIONAL, + partialFR2-FallbackRX-Req ENUMERATED {true} OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- Regular non-critical extensions: +UE-NR-Capability-v1610 ::= SEQUENCE { + inDeviceCoexInd-r16 ENUMERATED {supported} OPTIONAL, + dl-DedicatedMessageSegmentation-r16 ENUMERATED {supported} OPTIONAL, + nrdc-Parameters-v1610 NRDC-Parameters-v1610 OPTIONAL, + powSav-Parameters-r16 PowSav-Parameters-r16 OPTIONAL, + fr1-Add-UE-NR-Capabilities-v1610 UE-NR-CapabilityAddFRX-Mode-v1610 OPTIONAL, + fr2-Add-UE-NR-Capabilities-v1610 UE-NR-CapabilityAddFRX-Mode-v1610 OPTIONAL, + bh-RLF-Indication-r16 ENUMERATED {supported} OPTIONAL, + directSN-AdditionFirstRRC-IAB-r16 ENUMERATED {supported} OPTIONAL, + bap-Parameters-r16 BAP-Parameters-r16 OPTIONAL, + referenceTimeProvision-r16 ENUMERATED {supported} OPTIONAL, + sidelinkParameters-r16 SidelinkParameters-r16 OPTIONAL, + highSpeedParameters-r16 HighSpeedParameters-r16 OPTIONAL, + mac-Parameters-v1610 MAC-Parameters-v1610 OPTIONAL, + mcgRLF-RecoveryViaSCG-r16 ENUMERATED {supported} OPTIONAL, + resumeWithStoredMCG-SCells-r16 ENUMERATED {supported} OPTIONAL, + resumeWithStoredSCG-r16 ENUMERATED {supported} OPTIONAL, + resumeWithSCG-Config-r16 ENUMERATED {supported} OPTIONAL, + ue-BasedPerfMeas-Parameters-r16 UE-BasedPerfMeas-Parameters-r16 OPTIONAL, + son-Parameters-r16 SON-Parameters-r16 OPTIONAL, + onDemandSIB-Connected-r16 ENUMERATED {supported} OPTIONAL, + nonCriticalExtension UE-NR-Capability-v1640 OPTIONAL +} + +UE-NR-Capability-v1640 ::= SEQUENCE { + redirectAtResumeByNAS-r16 ENUMERATED {supported} OPTIONAL, + phy-ParametersSharedSpectrumChAccess-r16 Phy-ParametersSharedSpectrumChAccess-r16 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +UE-NR-CapabilityAddXDD-Mode ::= SEQUENCE { + phy-ParametersXDD-Diff Phy-ParametersXDD-Diff OPTIONAL, + mac-ParametersXDD-Diff MAC-ParametersXDD-Diff OPTIONAL, + measAndMobParametersXDD-Diff MeasAndMobParametersXDD-Diff OPTIONAL +} + +UE-NR-CapabilityAddXDD-Mode-v1530 ::= SEQUENCE { + eutra-ParametersXDD-Diff EUTRA-ParametersXDD-Diff +} + +UE-NR-CapabilityAddFRX-Mode ::= SEQUENCE { + phy-ParametersFRX-Diff Phy-ParametersFRX-Diff OPTIONAL, + measAndMobParametersFRX-Diff MeasAndMobParametersFRX-Diff OPTIONAL +} + +UE-NR-CapabilityAddFRX-Mode-v1540 ::= SEQUENCE { + ims-ParametersFRX-Diff IMS-ParametersFRX-Diff OPTIONAL +} + +UE-NR-CapabilityAddFRX-Mode-v1610 ::= SEQUENCE { + powSav-ParametersFRX-Diff-r16 PowSav-ParametersFRX-Diff-r16 OPTIONAL, + mac-ParametersFRX-Diff-r16 MAC-ParametersFRX-Diff-r16 OPTIONAL +} + +BAP-Parameters-r16 ::= SEQUENCE { + flowControlBH-RLC-ChannelBased-r16 ENUMERATED {supported} OPTIONAL, + flowControlRouting-ID-Based-r16 ENUMERATED {supported} OPTIONAL +} + +-- TAG-UE-NR-CAPABILITY-STOP +-- TAG-SHAREDSPECTRUMCHACCESSPARAMSPERBAND-START + +SharedSpectrumChAccessParamsPerBand-r16 ::= SEQUENCE { + + -- R1 10-1: UL channel access for dynamic channel access mode + ul-DynamicChAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-1a: UL channel access for semi-static channel access mode + ul-Semi-StaticChAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2: SSB-based RRM for dynamic channel access mode + ssb-RRM-DynamicChAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2a: SSB-based RRM for semi-static channel access mode + ssb-RRM-Semi-StaticChAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2b: MIB reading on unlicensed cell + mib-Acquisition-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2c: SSB-based RLM for dynamic channel access mode + ssb-RLM-DynamicChAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2d: SSB-based RLM for semi-static channel access mode + ssb-RLM-Semi-StaticChAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2e: SIB1 reception on unlicensed cell + sib1-Acquisition-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2f: Support monitoring of extended RAR window + extRA-ResponseWindow-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2g: SSB-based BFD/CBD for dynamic channel access mode + ssb-BFD-CBD-dynamicChannelAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2h: SSB-based BFD/CBD for semi-static channel access mode + ssb-BFD-CBD-semi-staticChannelAccess-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-2i: CSI-RS-based BFD/CBD for NR-U + csi-RS-BFD-CBD-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-7: UL channel access for 10 MHz SCell + ul-ChannelBW-SCell-10mhz-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-10: RSSI and channel occupancy measurement and reporting + rssi-ChannelOccupancyReporting-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-11:SRS starting position at any OFDM symbol in a slot + srs-StartAnyOFDM-Symbol-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-20: Support search space set configuration with freqMonitorLocation-r16 + searchSpaceFreqMonitorLocation-r16 INTEGER (1..5) OPTIONAL, + -- R1 10-20a: Support coreset configuration with rb-Offset + coreset-RB-Offset-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-23:CGI reading on unlicensed cell for ANR functionality + cgi-Acquisition-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-25: Enable configured UL transmissions when DCI 2_0 is configured but not detected + configuredUL-Tx-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-27: Wideband PRACH + prach-Wideband-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-29: Support available RB set indicator field in DCI 2_0 + dci-AvailableRB-Set-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-30: Support channel occupancy duration indicator field in DCI 2_0 + dci-ChOccupancyDuration-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-8: Type B PDSCH length {3, 5, 6, 8, 9, 10, 11, 12, 13} without DMRS shift due to CRS collision + typeB-PDSCH-length-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-9: Search space set group switching with explicit DCI 2_0 bit field trigger or with implicit PDCCH decoding with DCI 2_0 monitoring + searchSpaceSetGroupSwitchingwithDCI-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-9b: Search space set group switching with implicit PDCCH decoding without DCI 2_0 monitoring FFS:per band or per UE + searchSpaceSetGroupSwitchingwithoutDCI-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-9d: Support Search space set group switching capability 2 + searchSpaceSetGroupSwitchingcapability2-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-14: Non-numerical PDSCH to HARQ-ACK timing + non-numericalPDSCH-HARQ-timing-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-15: Enhanced dynamic HARQ codebook + enhancedDynamicHARQ-codebook-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-16: One-shot HARQ ACK feedback + oneShotHARQ-feedback-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-17: Multi-PUSCH UL grant + multiPUSCH-UL-grant-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-26: CSI-RS based RLM for NR-U + csi-RS-RLM-r16 ENUMERATED {supported} OPTIONAL, + dummy ENUMERATED {supported} OPTIONAL, + -- R1 10-31: Support of P/SP-CSI-RS reception with CSI-RS-ValidationWith-DCI-r16 configured + periodicAndSemi-PersistentCSI-RS-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-3: PRB interlace mapping for PUSCH + pusch-PRB-interlace-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-3a: PRB interlace mapping for PUCCH + pucch-F0-F1-PRB-Interlace-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-12: OCC for PRB interlace mapping for PF2 and PF3 + occ-PRB-PF2-PF3-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-13a: Extended CP range of more than one symbol for CG-PUSCH + extCP-rangeCG-PUSCH-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-18: Configured grant with retransmission in CG resources + configuredGrantWithReTx-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-21a: Support using ED threshold given by gNB for UL to DL COT sharing + ed-Threshold-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-21b: Support UL to DL COT sharing + ul-DL-COT-Sharing-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-24: CG-UCI multiplexing with HARQ ACK + mux-CG-UCI-HARQ-ACK-r16 ENUMERATED {supported} OPTIONAL, + -- R1 10-28: Configured grant with Rel-16 enhanced resource configuration + cg-resourceConfig-r16 ENUMERATED {supported} OPTIONAL +} + +SharedSpectrumChAccessParamsPerBand-v1630 ::= SEQUENCE { + -- R4 4-1: DL reception in intra-carrier guardband + dl-ReceptionIntraCellGuardband-r16 ENUMERATED {supported} OPTIONAL, + -- R4 4-2: DL reception when gNB does not transmit on all RB sets of a carrier as a result of LBT + dl-ReceptionLBT-subsetRB-r16 ENUMERATED {supported} OPTIONAL +} + +SharedSpectrumChAccessParamsPerBand-v1640 ::= SEQUENCE { + -- 10-26b(1-4): CSI-RS based RRM measurement with associated SS-block + csi-RSRP-AndRSRQ-MeasWithSSB-r16 ENUMERATED {supported} OPTIONAL, + -- 10-26c(1-5): CSI-RS based RRM measurement without associated SS-block + csi-RSRP-AndRSRQ-MeasWithoutSSB-r16 ENUMERATED {supported} OPTIONAL, + -- 10-26d(1-6): CSI-RS based RS-SINR measurement + csi-SINR-Meas-r16 ENUMERATED {supported} OPTIONAL, + -- 10-26e(1-8): RLM based on a mix of SS block and CSI-RS signals within active BWP + ssb-AndCSI-RS-RLM-r16 ENUMERATED {supported} OPTIONAL, + -- 10-26f(1-9): CSI-RS based contention free RA for HO + csi-RS-CFRA-ForHO-r16 ENUMERATED {supported} OPTIONAL +} +-- TAG-SHAREDSPECTRUMCHACCESSPARAMSPERBAND-STOP +-- TAG-ABSOLUTETIMEINFO-START + +AbsoluteTimeInfo-r16 ::= BIT STRING (SIZE (48)) + +-- TAG-ABSOLUTETIMEINFO-STOP +-- TAG-AREACONFIGURATION-START + +AreaConfiguration-r16 ::= SEQUENCE { + areaConfig-r16 AreaConfig-r16, + interFreqTargetList-r16 SEQUENCE(SIZE (1..maxFreq)) OF InterFreqTargetInfo-r16 OPTIONAL -- Need R +} + +AreaConfig-r16 ::= CHOICE { + cellGlobalIdList-r16 CellGlobalIdList-r16, + trackingAreaCodeList-r16 TrackingAreaCodeList-r16, + trackingAreaIdentityList-r16 TrackingAreaIdentityList-r16 +} + +InterFreqTargetInfo-r16 ::= SEQUENCE { + dl-CarrierFreq ARFCN-ValueNR, + cellList SEQUENCE (SIZE (1..32)) OF PhysCellId OPTIONAL +} + +CellGlobalIdList-r16 ::= SEQUENCE (SIZE (1..32)) OF CGI-Info-Logging-r16 + +TrackingAreaCodeList-r16 ::= SEQUENCE (SIZE (1..8)) OF TrackingAreaCode + +TrackingAreaIdentityList-r16 ::= SEQUENCE (SIZE (1..8)) OF TrackingAreaIdentity-r16 + +TrackingAreaIdentity-r16 ::= SEQUENCE { + plmn-Identity-r16 PLMN-Identity, + trackingAreaCode-r16 TrackingAreaCode +} + +-- TAG-AREACONFIGURATION-STOP +-- TAG-BTNAMELIST-START + +BT-NameList-r16 ::= SEQUENCE (SIZE (1..maxBT-Name-r16)) OF BT-Name-r16 + +BT-Name-r16 ::= OCTET STRING (SIZE (1..248)) + +-- TAG-BTNAMELIST-STOP +-- TAG-EUTRA-ALLOWEDMEASBANDWIDTH-START + +EUTRA-AllowedMeasBandwidth ::= ENUMERATED {mbw6, mbw15, mbw25, mbw50, mbw75, mbw100} + +-- TAG-EUTRA-ALLOWEDMEASBANDWIDTH-STOP +-- TAG-EUTRA-MBSFN-SUBFRAMECONFIGLIST-START + +EUTRA-MBSFN-SubframeConfigList ::= SEQUENCE (SIZE (1..maxMBSFN-Allocations)) OF EUTRA-MBSFN-SubframeConfig + +EUTRA-MBSFN-SubframeConfig ::= SEQUENCE { + radioframeAllocationPeriod ENUMERATED {n1, n2, n4, n8, n16, n32}, + radioframeAllocationOffset INTEGER (0..7), + subframeAllocation1 CHOICE { + oneFrame BIT STRING (SIZE(6)), + fourFrames BIT STRING (SIZE(24)) + }, + subframeAllocation2 CHOICE { + oneFrame BIT STRING (SIZE(2)), + fourFrames BIT STRING (SIZE(8)) + } OPTIONAL, -- Need R + ... +} + +-- TAG-EUTRA-MBSFN-SUBFRAMECONFIGLIST-STOP +-- TAG-EUTRA-MULTIBANDINFOLIST-START + +EUTRA-MultiBandInfoList ::= SEQUENCE (SIZE (1..maxMultiBands)) OF EUTRA-MultiBandInfo + +EUTRA-MultiBandInfo ::= SEQUENCE { + eutra-FreqBandIndicator FreqBandIndicatorEUTRA, + eutra-NS-PmaxList EUTRA-NS-PmaxList OPTIONAL -- Need R +} + +-- TAG-EUTRA-MULTIBANDINFOLIST-STOP +-- TAG-EUTRA-NS-PMAXLIST-START + +EUTRA-NS-PmaxList ::= SEQUENCE (SIZE (1..maxEUTRA-NS-Pmax)) OF EUTRA-NS-PmaxValue + +EUTRA-NS-PmaxValue ::= SEQUENCE { + additionalPmax INTEGER (-30..33) OPTIONAL, -- Need R + additionalSpectrumEmission INTEGER (1..288) OPTIONAL -- Need R +} + +-- TAG-EUTRA-NS-PMAXLIST-STOP +-- TAG-EUTRA-PHYSCELLID-START + +EUTRA-PhysCellId ::= INTEGER (0..503) + +-- TAG-EUTRA-PHYSCELLID-STOP +-- TAG-EUTRA-PHYSCELLIDRANGE-START + +EUTRA-PhysCellIdRange ::= SEQUENCE { + start EUTRA-PhysCellId, + range ENUMERATED {n4, n8, n12, n16, n24, n32, n48, n64, n84, n96, + n128, n168, n252, n504, spare2, spare1} OPTIONAL -- Need N +} + +-- TAG-EUTRA-PHYSCELLIDRANGE-STOP +-- TAG-EUTRA-PRESENCEANTENNAPORT1-START + +EUTRA-PresenceAntennaPort1 ::= BOOLEAN + +-- TAG-EUTRA-PRESENCEANTENNAPORT1-STOP +-- TAG-EUTRA-Q-OFFSETRANGE-START + +EUTRA-Q-OffsetRange ::= ENUMERATED { + dB-24, dB-22, dB-20, dB-18, dB-16, dB-14, + dB-12, dB-10, dB-8, dB-6, dB-5, dB-4, dB-3, + dB-2, dB-1, dB0, dB1, dB2, dB3, dB4, dB5, + dB6, dB8, dB10, dB12, dB14, dB16, dB18, + dB20, dB22, dB24} + +-- TAG-EUTRA-Q-OFFSETRANGE-STOP +-- TAG-IABIPADDRESS-START + +IAB-IP-Address-r16 ::= CHOICE { + iPv4-Address-r16 BIT STRING (SIZE(32)), + iPv6-Address-r16 BIT STRING (SIZE(128)), + iPv6-Prefix-r16 BIT STRING (SIZE(64)), + ... +} + +-- TAG-IABIPADDRESS-STOP +-- TAG-IABIPADDRESSINDEX-START + +IAB-IP-AddressIndex-r16 ::= INTEGER (1..maxIAB-IP-Address-r16) + +-- TAG-IABIPADDRESSINDEX-STOP +-- TAG-IAB-IP-USAGE-START + +IAB-IP-Usage-r16 ::= ENUMERATED {f1-C, f1-U, non-F1, spare} + +-- TAG-IAB-IP-USAGE-STOP +-- TAG-LOGGINGDURATION-START + +LoggingDuration-r16 ::= ENUMERATED { + min10, min20, min40, min60, min90, min120, spare2, spare1} + +-- TAG-LOGGINGDURATION-STOP +-- TAG-LOGGINGINTERVAL-START + +LoggingInterval-r16 ::= ENUMERATED { + ms320, ms640, ms1280, ms2560, ms5120, ms10240, ms20480, + ms30720, ms40960, ms61440 , infinity} + +-- TAG-LOGGINGINTERVAL-STOP +-- TAG-LOGMEASRESULTLISTBT-START + +LogMeasResultListBT-r16 ::= SEQUENCE (SIZE (1..maxBT-IdReport-r16)) OF LogMeasResultBT-r16 + +LogMeasResultBT-r16 ::= SEQUENCE { + bt-Addr-r16 BIT STRING (SIZE (48)), + rssi-BT-r16 INTEGER (-128..127) OPTIONAL, + ... +} + +-- TAG-LOGMEASRESULTLISTBT-STOP +-- TAG-LOGMEASRESULTLISTWLAN-START + +LogMeasResultListWLAN-r16 ::= SEQUENCE (SIZE (1..maxWLAN-Id-Report-r16)) OF LogMeasResultWLAN-r16 + +LogMeasResultWLAN-r16 ::= SEQUENCE { + wlan-Identifiers-r16 WLAN-Identifiers-r16, + rssiWLAN-r16 WLAN-RSSI-Range-r16 OPTIONAL, + rtt-WLAN-r16 WLAN-RTT-r16 OPTIONAL, + ... +} + +WLAN-Identifiers-r16 ::= SEQUENCE { + ssid-r16 OCTET STRING (SIZE (1..32)) OPTIONAL, + bssid-r16 OCTET STRING (SIZE (6)) OPTIONAL, + hessid-r16 OCTET STRING (SIZE (6)) OPTIONAL, + ... +} + +WLAN-RSSI-Range-r16 ::= INTEGER(0..141) + +WLAN-RTT-r16 ::= SEQUENCE { + rttValue-r16 INTEGER (0..16777215), + rttUnits-r16 ENUMERATED { + microseconds, + hundredsofnanoseconds, + tensofnanoseconds, + nanoseconds, + tenthsofnanoseconds, + ...}, + rttAccuracy-r16 INTEGER (0..255) OPTIONAL, + ... +} + +-- TAG-OTHERCONFIG-START + +OtherConfig ::= SEQUENCE { + delayBudgetReportingConfig CHOICE{ + release NULL, + setup SEQUENCE{ + delayBudgetReportingProhibitTimer ENUMERATED {s0, s0dot4, s0dot8, s1dot6, s3, s6, s12, s30} + } + } OPTIONAL -- Need M +} + +OtherConfig-v1540 ::= SEQUENCE { + overheatingAssistanceConfig SetupRelease {OverheatingAssistanceConfig} OPTIONAL, -- Need M + ... +} +CandidateServingFreqListNR-r16 ::= SEQUENCE (SIZE (1..maxFreqIDC-r16)) OF ARFCN-ValueNR + +OtherConfig-v1610 ::= SEQUENCE { + idc-AssistanceConfig-r16 SetupRelease {IDC-AssistanceConfig-r16} OPTIONAL, -- Need M + drx-PreferenceConfig-r16 SetupRelease {DRX-PreferenceConfig-r16} OPTIONAL, -- Need M + maxBW-PreferenceConfig-r16 SetupRelease {MaxBW-PreferenceConfig-r16} OPTIONAL, -- Need M + maxCC-PreferenceConfig-r16 SetupRelease {MaxCC-PreferenceConfig-r16} OPTIONAL, -- Need M + maxMIMO-LayerPreferenceConfig-r16 SetupRelease {MaxMIMO-LayerPreferenceConfig-r16} OPTIONAL, -- Need M + minSchedulingOffsetPreferenceConfig-r16 SetupRelease {MinSchedulingOffsetPreferenceConfig-r16} OPTIONAL, -- Need M + releasePreferenceConfig-r16 SetupRelease {ReleasePreferenceConfig-r16} OPTIONAL, -- Need M + referenceTimePreferenceReporting-r16 ENUMERATED {true} OPTIONAL, -- Need R + btNameList-r16 SetupRelease {BT-NameList-r16} OPTIONAL, -- Need M + wlanNameList-r16 SetupRelease {WLAN-NameList-r16} OPTIONAL, -- Need M + sensorNameList-r16 SetupRelease {Sensor-NameList-r16} OPTIONAL, -- Need M + obtainCommonLocation-r16 ENUMERATED {true} OPTIONAL, -- Need R + sl-AssistanceConfigNR-r16 ENUMERATED{true} OPTIONAL -- Need R +} + +OverheatingAssistanceConfig ::= SEQUENCE { + overheatingIndicationProhibitTimer ENUMERATED {s0, s0dot5, s1, s2, s5, s10, s20, s30, + s60, s90, s120, s300, s600, spare3, spare2, spare1} +} + +IDC-AssistanceConfig-r16 ::= SEQUENCE { + candidateServingFreqListNR-r16 CandidateServingFreqListNR-r16 OPTIONAL, -- Need R + ... +} + +DRX-PreferenceConfig-r16 ::= SEQUENCE { + drx-PreferenceProhibitTimer-r16 ENUMERATED { + s0, s0dot5, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s20, s30, spare2, spare1} +} + +MaxBW-PreferenceConfig-r16 ::= SEQUENCE { + maxBW-PreferenceProhibitTimer-r16 ENUMERATED { + s0, s0dot5, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s20, s30, spare2, spare1} +} + +MaxCC-PreferenceConfig-r16 ::= SEQUENCE { + maxCC-PreferenceProhibitTimer-r16 ENUMERATED { + s0, s0dot5, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s20, s30, spare2, spare1} +} + +MaxMIMO-LayerPreferenceConfig-r16 ::= SEQUENCE { + maxMIMO-LayerPreferenceProhibitTimer-r16 ENUMERATED { + s0, s0dot5, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s20, s30, spare2, spare1} +} + +MinSchedulingOffsetPreferenceConfig-r16 ::= SEQUENCE { + minSchedulingOffsetPreferenceProhibitTimer-r16 ENUMERATED { + s0, s0dot5, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s20, s30, spare2, spare1} +} + +ReleasePreferenceConfig-r16 ::= SEQUENCE { + releasePreferenceProhibitTimer-r16 ENUMERATED { + s0, s0dot5, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s20, s30, infinity, spare1}, + connectedReporting ENUMERATED {true} OPTIONAL -- Need R +} + +-- TAG-OTHERCONFIG-STOP +-- TAG-PHYSCELLIDUTRA-FDD-START + +PhysCellIdUTRA-FDD-r16 ::= INTEGER (0..511) + +-- TAG-PHYSCELLIDUTRA-FDD-STOP +-- TAG-RRC-TRANSACTIONIDENTIFIER-START + +RRC-TransactionIdentifier ::= INTEGER (0..3) + +-- TAG-RRC-TRANSACTIONIDENTIFIER-STOP +-- TAG-SENSORNAMELIST-START + +Sensor-NameList-r16 ::= SEQUENCE { + measUncomBarPre-r16 ENUMERATED {true} OPTIONAL, -- Need R + measUeSpeed ENUMERATED {true} OPTIONAL, -- Need R + measUeOrientation ENUMERATED {true} OPTIONAL -- Need R +} + +-- TAG-SENSORNAMELIST-STOP +-- TAG-TRACEREFERENCE-START + +TraceReference-r16 ::= SEQUENCE { + plmn-Identity-r16 PLMN-Identity, + traceId-r16 OCTET STRING (SIZE (3)) +} + +-- TAG-TRACEREFERENCE-STOP +-- TAG-UE-MeasurementsAvailable-START + +UE-MeasurementsAvailable-r16 ::= SEQUENCE { + logMeasAvailable-r16 ENUMERATED {true} OPTIONAL, + logMeasAvailableBT-r16 ENUMERATED {true} OPTIONAL, + logMeasAvailableWLAN-r16 ENUMERATED {true} OPTIONAL, + connEstFailInfoAvailable-r16 ENUMERATED {true} OPTIONAL, + rlf-InfoAvailable-r16 ENUMERATED {true} OPTIONAL, + ... +} + +-- TAG-UE-MeasurementsAvailable-STOP +-- TAG-UTRA-FDD-Q-OFFSETRANGE-START + +UTRA-FDD-Q-OffsetRange-r16 ::= ENUMERATED { + dB-24, dB-22, dB-20, dB-18, dB-16, dB-14, + dB-12, dB-10, dB-8, dB-6, dB-5, dB-4, dB-3, + dB-2, dB-1, dB0, dB1, dB2, dB3, dB4, dB5, + dB6, dB8, dB10, dB12, dB14, dB16, dB18, + dB20, dB22, dB24} + +-- TAG-UTRA-FDD-Q-OFFSETRANGE-STOP +-- TAG-VISITEDCELLINFOLIST-START + +VisitedCellInfoList-r16 ::= SEQUENCE (SIZE (1..maxCellHistory-r16)) OF VisitedCellInfo-r16 + +VisitedCellInfo-r16 ::= SEQUENCE { + visitedCellId-r16 CHOICE { + nr-CellId-r16 CHOICE { + cgi-Info CGI-Info-Logging-r16, + pci-arfcn-r16 SEQUENCE { + physCellId-r16 PhysCellId, + carrierFreq-r16 ARFCN-ValueNR + } + }, + eutra-CellId-r16 CHOICE { + cellGlobalId-r16 CGI-InfoEUTRA, + pci-arfcn-r16 SEQUENCE { + physCellId-r16 EUTRA-PhysCellId, + carrierFreq-r16 ARFCN-ValueEUTRA + } + } + } OPTIONAL, + timeSpent-r16 INTEGER (0..4095), + ... +} + +-- TAG-VISITEDCELLINFOLIST-STOP +-- TAG-WLANNAMELIST-START + +WLAN-NameList-r16 ::= SEQUENCE (SIZE (1..maxWLAN-Name-r16)) OF WLAN-Name-r16 + +WLAN-Name-r16 ::= OCTET STRING (SIZE (1..32)) + +-- TAG-SL-BWP-CONFIG-START + +SL-BWP-Config-r16 ::= SEQUENCE { + sl-BWP-Id BWP-Id, + sl-BWP-Generic-r16 SL-BWP-Generic-r16 OPTIONAL, -- Need M + sl-BWP-PoolConfig-r16 SL-BWP-PoolConfig-r16 OPTIONAL, -- Need M + ... +} + +SL-BWP-Generic-r16 ::= SEQUENCE { + sl-BWP-r16 BWP OPTIONAL, -- Need M + sl-LengthSymbols-r16 ENUMERATED {sym7, sym8, sym9, sym10, sym11, sym12, sym13, sym14} OPTIONAL, -- Need M + sl-StartSymbol-r16 ENUMERATED {sym0, sym1, sym2, sym3, sym4, sym5, sym6, sym7} OPTIONAL, -- Need M + sl-PSBCH-Config-r16 SetupRelease {SL-PSBCH-Config-r16} OPTIONAL, -- Need M + sl-TxDirectCurrentLocation-r16 INTEGER (0..3301) OPTIONAL, -- Need M + ... +} + +-- TAG-SL-BWP-CONFIG-STOP +-- TAG-SL-BWP-CONFIGCOMMON-START + +SL-BWP-ConfigCommon-r16 ::= SEQUENCE { + sl-BWP-Generic-r16 SL-BWP-Generic-r16 OPTIONAL, -- Need R + sl-BWP-PoolConfigCommon-r16 SL-BWP-PoolConfigCommon-r16 OPTIONAL, -- Need R + ... +} + +-- TAG-SL-BWP-CONFIGCOMMON-STOP +-- TAG-SL-BWP-POOLCONFIG-START + +SL-BWP-PoolConfig-r16 ::= SEQUENCE { + sl-RxPool-r16 SEQUENCE (SIZE (1..maxNrofRXPool-r16)) OF SL-ResourcePool-r16 OPTIONAL, -- Cond HO + sl-TxPoolSelectedNormal-r16 SL-TxPoolDedicated-r16 OPTIONAL, -- Need M + sl-TxPoolScheduling-r16 SL-TxPoolDedicated-r16 OPTIONAL, -- Need N + sl-TxPoolExceptional-r16 SL-ResourcePoolConfig-r16 OPTIONAL -- Need M +} + +SL-TxPoolDedicated-r16 ::= SEQUENCE { + sl-PoolToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofTXPool-r16)) OF SL-ResourcePoolID-r16 OPTIONAL, -- Need N + sl-PoolToAddModList-r16 SEQUENCE (SIZE (1..maxNrofTXPool-r16)) OF SL-ResourcePoolConfig-r16 OPTIONAL -- Need N +} + +SL-ResourcePoolConfig-r16 ::= SEQUENCE { + sl-ResourcePoolID-r16 SL-ResourcePoolID-r16, + sl-ResourcePool-r16 SL-ResourcePool-r16 OPTIONAL -- Need M +} + +SL-ResourcePoolID-r16 ::= INTEGER (1..maxNrofPoolID-r16) + +-- TAG-SL-BWP-POOLCONFIG-STOP +-- TAG-SL-BWP-POOLCONFIGCOMMON-START + +SL-BWP-PoolConfigCommon-r16 ::= SEQUENCE { + sl-RxPool-r16 SEQUENCE (SIZE (1..maxNrofRXPool-r16)) OF SL-ResourcePool-r16 OPTIONAL, -- Need R + sl-TxPoolSelectedNormal-r16 SEQUENCE (SIZE (1..maxNrofTXPool-r16)) OF SL-ResourcePoolConfig-r16 OPTIONAL, -- Need R + sl-TxPoolExceptional-r16 SL-ResourcePoolConfig-r16 OPTIONAL -- Need R +} + +-- TAG-SL-BWP-POOLCONFIGCOMMON-STOP +-- TAG-SL-CBR-PRIORITYTXCONFIGLIST-START + +SL-CBR-PriorityTxConfigList-r16 ::= SEQUENCE (SIZE (1..8)) OF SL-PriorityTxConfigIndex-r16 + +SL-PriorityTxConfigIndex-r16 ::= SEQUENCE { + sl-PriorityThreshold-r16 INTEGER (1..8) OPTIONAL, -- Need M + sl-DefaultTxConfigIndex-r16 INTEGER (0..maxCBR-Level-1-r16) OPTIONAL, -- Need M + sl-CBR-ConfigIndex-r16 INTEGER (0..maxCBR-Config-1-r16) OPTIONAL, -- Need M + sl-Tx-ConfigIndexList-r16 SEQUENCE (SIZE (1.. maxCBR-Level-r16)) OF SL-TxConfigIndex-r16 OPTIONAL -- Need M +} + +SL-TxConfigIndex-r16 ::= INTEGER (0..maxTxConfig-1-r16) + +-- TAG-SL-CBR-PRIORITYTXCONFIGLIST-STOP +-- TAG-SL-CBR-COMMONTXCONFIGLIST-START + +SL-CBR-CommonTxConfigList-r16 ::= SEQUENCE { + sl-CBR-RangeConfigList-r16 SEQUENCE (SIZE (1..maxCBR-Config-r16)) OF SL-CBR-LevelsConfig-r16 OPTIONAL, -- Need M + sl-CBR-PSSCH-TxConfigList-r16 SEQUENCE (SIZE (1.. maxTxConfig-r16)) OF SL-CBR-PSSCH-TxConfig-r16 OPTIONAL -- Need M +} + +SL-CBR-LevelsConfig-r16 ::= SEQUENCE (SIZE (1..maxCBR-Level-r16)) OF SL-CBR-r16 + +SL-CBR-PSSCH-TxConfig-r16 ::= SEQUENCE { + sl-CR-Limit-r16 INTEGER(0..10000) OPTIONAL, -- Need M + sl-TxParameters-r16 SL-PSSCH-TxParameters-r16 OPTIONAL -- Need M +} + +SL-CBR-r16 ::= INTEGER (0..100) + +-- TAG-SL-CBR-COMMONTXCONFIGLIST-STOP +-- TAG-SL-CONFIGDEDICATEDNR-START + +SL-ConfigDedicatedNR-r16 ::= SEQUENCE { + sl-PHY-MAC-RLC-Config-r16 SL-PHY-MAC-RLC-Config-r16 OPTIONAL, -- Need M + sl-RadioBearerToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofSLRB-r16)) OF SLRB-Uu-ConfigIndex-r16 OPTIONAL, -- Need N + sl-RadioBearerToAddModList-r16 SEQUENCE (SIZE (1..maxNrofSLRB-r16)) OF SL-RadioBearerConfig-r16 OPTIONAL, -- Need N + sl-MeasConfigInfoToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofSL-Dest-r16)) OF SL-DestinationIndex-r16 OPTIONAL, -- Need N + sl-MeasConfigInfoToAddModList-r16 SEQUENCE (SIZE (1..maxNrofSL-Dest-r16)) OF SL-MeasConfigInfo-r16 OPTIONAL, -- Need M + t400-r16 ENUMERATED {ms100, ms200, ms300, ms400, ms600, ms1000, ms1500, ms2000} OPTIONAL, -- Need M + ... +} + +SL-DestinationIndex-r16 ::= INTEGER (0..maxNrofSL-Dest-1-r16) + +SL-PHY-MAC-RLC-Config-r16::= SEQUENCE { + sl-ScheduledConfig-r16 SetupRelease { SL-ScheduledConfig-r16 } OPTIONAL, -- Need M + sl-UE-SelectedConfig-r16 SetupRelease { SL-UE-SelectedConfig-r16 } OPTIONAL, -- Need M + sl-FreqInfoToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF SL-Freq-Id-r16 OPTIONAL, -- Need N + sl-FreqInfoToAddModList-r16 SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF SL-FreqConfig-r16 OPTIONAL, -- Need N + sl-RLC-BearerToReleaseList-r16 SEQUENCE (SIZE (1..maxSL-LCID-r16)) OF SL-RLC-BearerConfigIndex-r16 OPTIONAL, -- Need N + sl-RLC-BearerToAddModList-r16 SEQUENCE (SIZE (1..maxSL-LCID-r16)) OF SL-RLC-BearerConfig-r16 OPTIONAL, -- Need N + sl-MaxNumConsecutiveDTX-r16 ENUMERATED {n1, n2, n3, n4, n6, n8, n16, n32} OPTIONAL, -- Need M + sl-CSI-Acquisition-r16 ENUMERATED {enabled} OPTIONAL, -- Need R + sl-CSI-SchedulingRequestId-r16 SetupRelease {SchedulingRequestId} OPTIONAL, -- Need M + sl-SSB-PriorityNR-r16 INTEGER (1..8) OPTIONAL, -- Need R + networkControlledSyncTx-r16 ENUMERATED {on, off} OPTIONAL -- Need M +} + +-- TAG-SL-CONFIGDEDICATEDNR-STOP +-- TAG-SL-CONFIGUREDGRANTCONFIG-START + +SL-ConfiguredGrantConfig-r16 ::= SEQUENCE { + sl-ConfigIndexCG-r16 SL-ConfigIndexCG-r16, + sl-PeriodCG-r16 SL-PeriodCG-r16 OPTIONAL, -- Need M + sl-NrOfHARQ-Processes-r16 INTEGER (1..16) OPTIONAL, -- Need M + sl-HARQ-ProcID-offset-r16 INTEGER (0..15) OPTIONAL, -- Need M + sl-CG-MaxTransNumList-r16 SL-CG-MaxTransNumList-r16 OPTIONAL, -- Need M + rrc-ConfiguredSidelinkGrant-r16 SEQUENCE { + sl-TimeResourceCG-Type1-r16 INTEGER (0..496) OPTIONAL, -- Need M + sl-StartSubchannelCG-Type1-r16 INTEGER (0..26) OPTIONAL, -- Need M + sl-FreqResourceCG-Type1-r16 INTEGER (0..6929) OPTIONAL, -- Need M + sl-TimeOffsetCG-Type1-r16 INTEGER (0..7999) OPTIONAL, -- Need R + sl-N1PUCCH-AN-r16 PUCCH-ResourceId OPTIONAL, -- Need M + sl-PSFCH-ToPUCCH-CG-Type1-r16 INTEGER (0..15) OPTIONAL, -- Need M + sl-ResourcePoolID-r16 SL-ResourcePoolID-r16 OPTIONAL, -- Need M + sl-TimeReferenceSFN-Type1-r16 ENUMERATED {sfn512} OPTIONAL -- Need S + } OPTIONAL, -- Need M + ... +} + +SL-ConfigIndexCG-r16 ::= INTEGER (0..maxNrofCG-SL-r16-1) + +SL-CG-MaxTransNumList-r16 ::= SEQUENCE (SIZE (1..8)) OF SL-CG-MaxTransNum-r16 + +SL-CG-MaxTransNum-r16 ::= SEQUENCE { + sl-Priority-r16 INTEGER (1..8), + sl-MaxTransNum-r16 INTEGER (1..32) +} + +SL-PeriodCG-r16 ::= CHOICE{ + sl-PeriodCG1-r16 ENUMERATED {ms100, ms200, ms300, ms400, ms500, ms600, ms700, ms800, ms900, ms1000, spare6, + spare5, spare4, spare3, spare2, spare1}, + sl-PeriodCG2-r16 INTEGER (1..99) +} + +-- TAG-SL-CONFIGUREDGRANTCONFIG-STOP +-- TAG-SL-DESTINATIONIDENTITY-START + +SL-DestinationIdentity-r16 ::= BIT STRING (SIZE (24)) + +-- TAG-SL-DESTINATIONIDENTITY-STOP +-- TAG-SL-FREQCONFIG-START + +SL-FreqConfig-r16 ::= SEQUENCE { + sl-Freq-Id-r16 SL-Freq-Id-r16, + sl-SCS-SpecificCarrierList-r16 SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier, + sl-AbsoluteFrequencyPointA-r16 ARFCN-ValueNR OPTIONAL, -- Need M + sl-AbsoluteFrequencySSB-r16 ARFCN-ValueNR OPTIONAL, -- Need R + frequencyShift7p5khzSL-r16 ENUMERATED {true} OPTIONAL, -- Cond V2X-SL-Shared + valueN-r16 INTEGER (-1..1), + sl-BWP-ToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofSL-BWPs-r16)) OF BWP-Id OPTIONAL, -- Need N + sl-BWP-ToAddModList-r16 SEQUENCE (SIZE (1..maxNrofSL-BWPs-r16)) OF SL-BWP-Config-r16 OPTIONAL, -- Need N + sl-SyncConfigList-r16 SL-SyncConfigList-r16 OPTIONAL, -- Need M + sl-SyncPriority-r16 ENUMERATED {gnss, gnbEnb} OPTIONAL -- Need M +} + +SL-Freq-Id-r16 ::= INTEGER (1.. maxNrofFreqSL-r16) + +-- TAG-SL-FREQCONFIG-STOP +-- TAG-SL-FREQCONFIGCOMMON-START + +SL-FreqConfigCommon-r16 ::= SEQUENCE { + sl-SCS-SpecificCarrierList-r16 SEQUENCE (SIZE (1..maxSCSs)) OF SCS-SpecificCarrier, + sl-AbsoluteFrequencyPointA-r16 ARFCN-ValueNR, + sl-AbsoluteFrequencySSB-r16 ARFCN-ValueNR OPTIONAL, -- Need R + frequencyShift7p5khzSL-r16 ENUMERATED {true} OPTIONAL, -- Cond V2X-SL-Shared + valueN-r16 INTEGER (-1..1), + sl-BWP-List-r16 SEQUENCE (SIZE (1..maxNrofSL-BWPs-r16)) OF SL-BWP-ConfigCommon-r16 OPTIONAL, -- Need R + sl-SyncPriority-r16 ENUMERATED {gnss, gnbEnb} OPTIONAL, -- Need R + sl-NbAsSync-r16 BOOLEAN OPTIONAL, -- Need R + sl-SyncConfigList-r16 SL-SyncConfigList-r16 OPTIONAL, -- Need R + ... +} +-- TAG-SL-FREQCONFIGCOMMON-STOP +-- TAG-SL-LOGICALCHANNELCONFIG-START + +SL-LogicalChannelConfig-r16 ::= SEQUENCE { + sl-Priority-r16 INTEGER (1..8), + sl-PrioritisedBitRate-r16 ENUMERATED {kBps0, kBps8, kBps16, kBps32, kBps64, kBps128, kBps256, kBps512, + kBps1024, kBps2048, kBps4096, kBps8192, kBps16384, kBps32768, kBps65536, infinity}, + sl-BucketSizeDuration-r16 ENUMERATED {ms5, ms10, ms20, ms50, ms100, ms150, ms300, ms500, ms1000, + spare7, spare6, spare5, spare4, spare3,spare2, spare1}, + sl-ConfiguredGrantType1Allowed-r16 ENUMERATED {true} OPTIONAL, -- Need R + sl-HARQ-FeedbackEnabled-r16 ENUMERATED {enabled, disabled } OPTIONAL, -- Need R + sl-AllowedCG-List-r16 SEQUENCE (SIZE (0.. maxNrofCG-SL-r16-1)) OF SL-ConfigIndexCG-r16 + OPTIONAL, -- Need R + sl-AllowedSCS-List-r16 SEQUENCE (SIZE (1..maxSCSs)) OF SubcarrierSpacing OPTIONAL, -- Need R + sl-MaxPUSCH-Duration-r16 ENUMERATED {ms0p02, ms0p04, ms0p0625, ms0p125, ms0p25, ms0p5, spare2, spare1} + OPTIONAL, -- Need R + sl-LogicalChannelGroup-r16 INTEGER (0..maxLCG-ID) OPTIONAL, -- Need R + sl-SchedulingRequestId-r16 SchedulingRequestId OPTIONAL, -- Need R + sl-LogicalChannelSR-DelayTimerApplied-r16 BOOLEAN OPTIONAL, -- Need R + ... +} +-- TAG-SL-LOGICALCHANNELCONFIG-STOP +-- TAG-SL-MEASCONFIGCOMMON-START + +SL-MeasConfigCommon-r16 ::= SEQUENCE { + sl-MeasObjectListCommon-r16 SL-MeasObjectList-r16 OPTIONAL, -- Need R + sl-ReportConfigListCommon-r16 SL-ReportConfigList-r16 OPTIONAL, -- Need R + sl-MeasIdListCommon-r16 SL-MeasIdList-r16 OPTIONAL, -- Need R + sl-QuantityConfigCommon-r16 SL-QuantityConfig-r16 OPTIONAL, -- Need R + ... +} + +-- TAG-SL-MEASCONFIGCOMMON-STOP +-- TAG-SL-MEASCONFIGINFO-START + +SL-MeasConfigInfo-r16 ::= SEQUENCE { + sl-DestinationIndex-r16 SL-DestinationIndex-r16, + sl-MeasConfig-r16 SL-MeasConfig-r16, + ... +} + +SL-MeasConfig-r16 ::= SEQUENCE { + sl-MeasObjectToRemoveList-r16 SL-MeasObjectToRemoveList-r16 OPTIONAL, -- Need N + sl-MeasObjectToAddModList-r16 SL-MeasObjectList-r16 OPTIONAL, -- Need N + sl-ReportConfigToRemoveList-r16 SL-ReportConfigToRemoveList-r16 OPTIONAL, -- Need N + sl-ReportConfigToAddModList-r16 SL-ReportConfigList-r16 OPTIONAL, -- Need N + sl-MeasIdToRemoveList-r16 SL-MeasIdToRemoveList-r16 OPTIONAL, -- Need N + sl-MeasIdToAddModList-r16 SL-MeasIdList-r16 OPTIONAL, -- Need N + sl-QuantityConfig-r16 SL-QuantityConfig-r16 OPTIONAL, -- Need M + ... +} + +SL-MeasObjectToRemoveList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-ObjectId-r16)) OF SL-MeasObjectId-r16 + +SL-ReportConfigToRemoveList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-ReportConfigId-r16)) OF SL-ReportConfigId-r16 + +SL-MeasIdToRemoveList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-MeasId-r16)) OF SL-MeasId-r16 + +-- TAG-SL-MEASCONFIGINFO-STOP +-- TAG-SL-MEASIDLIST-START + +SL-MeasIdList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-MeasId-r16)) OF SL-MeasIdInfo-r16 + +SL-MeasIdInfo-r16 ::= SEQUENCE { + sl-MeasId-r16 SL-MeasId-r16, + sl-MeasObjectId-r16 SL-MeasObjectId-r16, + sl-ReportConfigId-r16 SL-ReportConfigId-r16, + ... +} + +SL-MeasId-r16 ::= INTEGER (1..maxNrofSL-MeasId-r16) + +-- TAG-SL-MEASIDLIST-STOP +-- TAG-SL-MEASOBJECTLIST-START + +SL-MeasObjectList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-ObjectId-r16)) OF SL-MeasObjectInfo-r16 + +SL-MeasObjectInfo-r16 ::= SEQUENCE { + sl-MeasObjectId-r16 SL-MeasObjectId-r16, + sl-MeasObject-r16 SL-MeasObject-r16, + ... +} + +SL-MeasObjectId-r16 ::= INTEGER (1..maxNrofSL-ObjectId-r16) + +SL-MeasObject-r16 ::= SEQUENCE { + frequencyInfoSL-r16 ARFCN-ValueNR, + ... +} + +-- TAG-SL-MEASOBJECTLIST-STOP +-- TAG-SL-PDCP-CONFIG-START + +SL-PDCP-Config-r16 ::= SEQUENCE { + sl-DiscardTimer-r16 ENUMERATED {ms3, ms10, ms20, ms25, ms30, ms40, ms50, ms60, ms75, ms100, ms150, ms200, + ms250, ms300, ms500, ms750, ms1500, infinity} OPTIONAL, -- Cond Setup + sl-PDCP-SN-Size-r16 ENUMERATED {len12bits, len18bits} OPTIONAL, -- Cond Setup2 + sl-OutOfOrderDelivery ENUMERATED { true } OPTIONAL, -- Need R + ... +} + +-- TAG-SL-PDCP-CONFIG-STOP +-- TAG-SL-PSBCH-CONFIG-START + +SL-PSBCH-Config-r16 ::= SEQUENCE { + dl-P0-PSBCH-r16 INTEGER (-16..15) OPTIONAL, -- Need M + dl-Alpha-PSBCH-r16 ENUMERATED {alpha0, alpha04, alpha05, alpha06, alpha07, alpha08, alpha09, alpha1} OPTIONAL, -- Need M + ... +} + +-- TAG-SL-PSBCH-CONFIG-STOP +-- TAG-SL-PSSCH-TXCONFIGLIST-START + +SL-PSSCH-TxConfigList-r16 ::= SEQUENCE (SIZE (1..maxPSSCH-TxConfig-r16)) OF SL-PSSCH-TxConfig-r16 + +SL-PSSCH-TxConfig-r16 ::= SEQUENCE { + sl-TypeTxSync-r16 SL-TypeTxSync-r16 OPTIONAL, -- Need R + sl-ThresUE-Speed-r16 ENUMERATED {kmph60, kmph80, kmph100, kmph120, + kmph140, kmph160, kmph180, kmph200}, + sl-ParametersAboveThres-r16 SL-PSSCH-TxParameters-r16, + sl-ParametersBelowThres-r16 SL-PSSCH-TxParameters-r16, + ... +} + + +SL-PSSCH-TxParameters-r16 ::= SEQUENCE { + sl-MinMCS-PSSCH-r16 INTEGER (0..27), + sl-MaxMCS-PSSCH-r16 INTEGER (0..31), + sl-MinSubChannelNumPSSCH-r16 INTEGER (1..27), + sl-MaxSubchannelNumPSSCH-r16 INTEGER (1..27), + sl-MaxTxTransNumPSSCH-r16 INTEGER (1..32), + sl-MaxTxPower-r16 SL-TxPower-r16 OPTIONAL -- Cond CBR +} + +-- TAG-SL-PSSCH-TXCONFIGLIST-STOP +-- TAG-SL-QOS-FLOWIDENTITY-START + +SL-QoS-FlowIdentity-r16 ::= INTEGER (1..maxNrofSL-QFIs-r16) + +-- TAG-SL-QOS-FLOWIDENTITY-STOP +-- TAG-SL-QOS-PROFILE-START + +SL-QoS-Profile-r16 ::= SEQUENCE { + sl-PQI-r16 SL-PQI-r16 OPTIONAL, -- Need R + sl-GFBR-r16 INTEGER (0..4000000000) OPTIONAL, -- Need R + sl-MFBR-r16 INTEGER (0..4000000000) OPTIONAL, -- Need R + sl-Range-r16 INTEGER (1..1000) OPTIONAL, -- Need R + ... +} + +SL-PQI-r16 ::= CHOICE { + sl-StandardizedPQI-r16 INTEGER (0..255), + sl-Non-StandardizedPQI-r16 SEQUENCE { + sl-ResourceType-r16 ENUMERATED {gbr, non-GBR, delayCriticalGBR, spare1} OPTIONAL, -- Need R + sl-PriorityLevel-r16 INTEGER (1..8) OPTIONAL, -- Need R + sl-PacketDelayBudget-r16 INTEGER (0..1023) OPTIONAL, -- Need R + sl-PacketErrorRate-r16 INTEGER (0..9) OPTIONAL, -- Need R + sl-AveragingWindow-r16 INTEGER (0..4095) OPTIONAL, -- Need R + sl-MaxDataBurstVolume-r16 INTEGER (0..4095) OPTIONAL, -- Need R + ... + } +} + +-- TAG-SL-QOS-PROFILE-STOP +-- TAG-SL-QUANTITYCONFIG-START + +SL-QuantityConfig-r16 ::= SEQUENCE { + sl-FilterCoefficientDMRS-r16 FilterCoefficient DEFAULT fc4, + ... +} + +-- TAG-SL-QuantityConfig-STOP +-- TAG-SL-RADIOBEARERCONFIG-START + +SL-RadioBearerConfig-r16 ::= SEQUENCE { + slrb-Uu-ConfigIndex-r16 SLRB-Uu-ConfigIndex-r16, + sl-SDAP-Config-r16 SL-SDAP-Config-r16 OPTIONAL, -- Cond SLRBSetup + sl-PDCP-Config-r16 SL-PDCP-Config-r16 OPTIONAL, -- Cond SLRBSetup + sl-TransRange-r16 ENUMERATED {m20, m50, m80, m100, m120, m150, m180, m200, m220, m250, m270, m300, m350, m370, + m400, m420, m450, m480, m500, m550, m600, m700, m1000, spare9, spare8, spare7, spare6, + spare5, spare4, spare3, spare2, spare1} OPTIONAL, -- Need R + ... +} + +-- TAG-SL-RADIOBEARERCONFIG-STOP +-- TAG-SL-REPORTCONFIGLIST-START + +SL-ReportConfigList-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-ReportConfigId-r16)) OF SL-ReportConfigInfo-r16 + +SL-ReportConfigInfo-r16 ::= SEQUENCE { + sl-ReportConfigId-r16 SL-ReportConfigId-r16, + sl-ReportConfig-r16 SL-ReportConfig-r16, + ... +} + +SL-ReportConfigId-r16 ::= INTEGER (1..maxNrofSL-ReportConfigId-r16) + +SL-ReportConfig-r16 ::= SEQUENCE { + sl-ReportType-r16 CHOICE { + sl-Periodical-r16 SL-PeriodicalReportConfig-r16, + sl-EventTriggered-r16 SL-EventTriggerConfig-r16, + ... + }, + ... +} + +SL-PeriodicalReportConfig-r16 ::= SEQUENCE { + sl-ReportInterval-r16 ReportInterval, + sl-ReportAmount-r16 ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + sl-ReportQuantity-r16 SL-MeasReportQuantity-r16, + sl-RS-Type-r16 SL-RS-Type-r16, + ... +} + +SL-EventTriggerConfig-r16 ::= SEQUENCE { + sl-EventId-r16 CHOICE { + eventS1-r16 SEQUENCE { + s1-Threshold-r16 SL-MeasTriggerQuantity-r16, + sl-ReportOnLeave-r16 BOOLEAN, + sl-Hysteresis-r16 Hysteresis, + sl-TimeToTrigger-r16 TimeToTrigger, + ... + }, + eventS2-r16 SEQUENCE { + s2-Threshold-r16 SL-MeasTriggerQuantity-r16, + sl-ReportOnLeave-r16 BOOLEAN, + sl-Hysteresis-r16 Hysteresis, + sl-TimeToTrigger-r16 TimeToTrigger, + ... + }, + ... + }, + sl-ReportInterval-r16 ReportInterval, + sl-ReportAmount-r16 ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity}, + sl-ReportQuantity-r16 SL-MeasReportQuantity-r16, + sl-RS-Type-r16 SL-RS-Type-r16, + ... +} + +SL-MeasReportQuantity-r16 ::= CHOICE { + sl-RSRP-r16 BOOLEAN, + ... +} + +SL-MeasTriggerQuantity-r16 ::= CHOICE { + sl-RSRP-r16 RSRP-Range, + ... +} + +SL-RS-Type-r16 ::= ENUMERATED {dmrs, spare3, spare2, spare1} + +-- TAG-SL-REPORTCONFIGLIST-STOP +-- TAG-SL-RESOURCEPOOL-START + +SL-ResourcePool-r16 ::= SEQUENCE { + sl-PSCCH-Config-r16 SetupRelease { SL-PSCCH-Config-r16 } OPTIONAL, -- Need M + sl-PSSCH-Config-r16 SetupRelease { SL-PSSCH-Config-r16 } OPTIONAL, -- Need M + sl-PSFCH-Config-r16 SetupRelease { SL-PSFCH-Config-r16 } OPTIONAL, -- Need M + sl-SyncAllowed-r16 SL-SyncAllowed-r16 OPTIONAL, -- Need M + sl-SubchannelSize-r16 ENUMERATED {n10, n12, n15, n20, n25, n50, n75, n100} OPTIONAL, -- Need M + dummy INTEGER (10..160) OPTIONAL, -- Need M + sl-StartRB-Subchannel-r16 INTEGER (0..265) OPTIONAL, -- Need M + sl-NumSubchannel-r16 INTEGER (1..27) OPTIONAL, -- Need M + sl-Additional-MCS-Table-r16 ENUMERATED {qam256, qam64LowSE, qam256-qam64LowSE } OPTIONAL, -- Need M + sl-ThreshS-RSSI-CBR-r16 INTEGER (0..45) OPTIONAL, -- Need M + sl-TimeWindowSizeCBR-r16 ENUMERATED {ms100, slot100} OPTIONAL, -- Need M + sl-TimeWindowSizeCR-r16 ENUMERATED {ms1000, slot1000} OPTIONAL, -- Need M + sl-PTRS-Config-r16 SL-PTRS-Config-r16 OPTIONAL, -- Need M + sl-UE-SelectedConfigRP-r16 SL-UE-SelectedConfigRP-r16 OPTIONAL, -- Need M + sl-RxParametersNcell-r16 SEQUENCE { + sl-TDD-Configuration-r16 TDD-UL-DL-ConfigCommon OPTIONAL, -- Need M + sl-SyncConfigIndex-r16 INTEGER (0..15) + } OPTIONAL, -- Need M + sl-ZoneConfigMCR-List-r16 SEQUENCE (SIZE (16)) OF SL-ZoneConfigMCR-r16 OPTIONAL, -- Need M + sl-FilterCoefficient-r16 FilterCoefficient OPTIONAL, -- Need M + sl-RB-Number-r16 INTEGER (10..275) OPTIONAL, -- Need M + sl-PreemptionEnable-r16 ENUMERATED {enabled, pl1, pl2, pl3, pl4, pl5, pl6, pl7, pl8} OPTIONAL, -- Need R + sl-PriorityThreshold-UL-URLLC-r16 INTEGER (1..9) OPTIONAL, -- Need M + sl-PriorityThreshold-r16 INTEGER (1..9) OPTIONAL, -- Need M + sl-X-Overhead-r16 ENUMERATED {n0,n3, n6, n9} OPTIONAL, -- Need S + sl-PowerControl-r16 SL-PowerControl-r16 OPTIONAL, -- Need M + sl-TxPercentageList-r16 SL-TxPercentageList-r16 OPTIONAL, -- Need M + sl-MinMaxMCS-List-r16 SL-MinMaxMCS-List-r16 OPTIONAL, -- Need M + ..., + [[ + sl-TimeResource-r16 BIT STRING (SIZE (10..160)) OPTIONAL -- Need M + ]] +} + +SL-ZoneConfigMCR-r16 ::= SEQUENCE { + sl-ZoneConfigMCR-Index-r16 INTEGER (0..15), + sl-TransRange-r16 ENUMERATED {m20, m50, m80, m100, m120, m150, m180, m200, m220, m250, m270, m300, m350, + m370, m400, m420, m450, m480, m500, m550, m600, m700, m1000, spare9, spare8, + spare7, spare6, spare5, spare4, spare3, spare2, spare1} + OPTIONAL, -- Need M + sl-ZoneConfig-r16 SL-ZoneConfig-r16 OPTIONAL, -- Need M + ... +} + +SL-SyncAllowed-r16 ::= SEQUENCE { + gnss-Sync-r16 ENUMERATED {true} OPTIONAL, -- Need R + gnbEnb-Sync-r16 ENUMERATED {true} OPTIONAL, -- Need R + ue-Sync-r16 ENUMERATED {true} OPTIONAL -- Need R +} + +SL-PSCCH-Config-r16 ::= SEQUENCE { + sl-TimeResourcePSCCH-r16 ENUMERATED {n2, n3} OPTIONAL, -- Need M + sl-FreqResourcePSCCH-r16 ENUMERATED {n10,n12, n15, n20, n25} OPTIONAL, -- Need M + sl-DMRS-ScrambleID-r16 INTEGER (0..65535) OPTIONAL, -- Need M + sl-NumReservedBits-r16 INTEGER (2..4) OPTIONAL, -- Need M + ... +} + +SL-PSSCH-Config-r16 ::= SEQUENCE { + sl-PSSCH-DMRS-TimePatternList-r16 SEQUENCE (SIZE (1..3)) OF INTEGER (2..4) OPTIONAL, -- Need M + sl-BetaOffsets2ndSCI-r16 SEQUENCE (SIZE (4)) OF SL-BetaOffsets-r16 OPTIONAL, -- Need M + sl-Scaling-r16 ENUMERATED {f0p5, f0p65, f0p8, f1} OPTIONAL, -- Need M + ... +} + +SL-PSFCH-Config-r16 ::= SEQUENCE { + sl-PSFCH-Period-r16 ENUMERATED {sl0, sl1, sl2, sl4} OPTIONAL, -- Need M + sl-PSFCH-RB-Set-r16 BIT STRING (SIZE (10..275)) OPTIONAL, -- Need M + sl-NumMuxCS-Pair-r16 ENUMERATED {n1, n2, n3, n6} OPTIONAL, -- Need M + sl-MinTimeGapPSFCH-r16 ENUMERATED {sl2, sl3} OPTIONAL, -- Need M + sl-PSFCH-HopID-r16 INTEGER (0..1023) OPTIONAL, -- Need M + sl-PSFCH-CandidateResourceType-r16 ENUMERATED {startSubCH, allocSubCH} OPTIONAL, -- Need M + ... +} +SL-PTRS-Config-r16 ::= SEQUENCE { + sl-PTRS-FreqDensity-r16 SEQUENCE (SIZE (2)) OF INTEGER (1..276) OPTIONAL, -- Need M + sl-PTRS-TimeDensity-r16 SEQUENCE (SIZE (3)) OF INTEGER (0..29) OPTIONAL, -- Need M + sl-PTRS-RE-Offset-r16 ENUMERATED {offset01, offset10, offset11} OPTIONAL, -- Need M + ... +} + +SL-UE-SelectedConfigRP-r16 ::= SEQUENCE { + sl-CBR-PriorityTxConfigList-r16 SL-CBR-PriorityTxConfigList-r16 OPTIONAL, -- Need M + sl-Thres-RSRP-List-r16 SL-Thres-RSRP-List-r16 OPTIONAL, -- Need M + sl-MultiReserveResource-r16 ENUMERATED {enabled} OPTIONAL, -- Need M + sl-MaxNumPerReserve-r16 ENUMERATED {n2, n3} OPTIONAL, -- Need M + sl-SensingWindow-r16 ENUMERATED {ms100, ms1100} OPTIONAL, -- Need M + sl-SelectionWindowList-r16 SL-SelectionWindowList-r16 OPTIONAL, -- Need M + sl-ResourceReservePeriodList-r16 SEQUENCE (SIZE (1..16)) OF SL-ResourceReservePeriod-r16 OPTIONAL, -- Need M + sl-RS-ForSensing-r16 ENUMERATED {pscch, pssch}, + ... +} + +SL-ResourceReservePeriod-r16 ::= CHOICE { + sl-ResourceReservePeriod1-r16 ENUMERATED {ms0, ms100, ms200, ms300, ms400, ms500, ms600, ms700, ms800, ms900, ms1000}, + sl-ResourceReservePeriod2-r16 INTEGER (1..99) +} + +SL-SelectionWindowList-r16 ::= SEQUENCE (SIZE (8)) OF SL-SelectionWindowConfig-r16 + +SL-SelectionWindowConfig-r16 ::= SEQUENCE { + sl-Priority-r16 INTEGER (1..8), + sl-SelectionWindow-r16 ENUMERATED {n1, n5, n10, n20} +} + +SL-TxPercentageList-r16 ::= SEQUENCE (SIZE (8)) OF SL-TxPercentageConfig-r16 + +SL-TxPercentageConfig-r16 ::= SEQUENCE { + sl-Priority-r16 INTEGER (1..8), + sl-TxPercentage-r16 ENUMERATED {p20, p35, p50} +} + +SL-MinMaxMCS-List-r16 ::= SEQUENCE (SIZE (1..3)) OF SL-MinMaxMCS-Config-r16 + +SL-MinMaxMCS-Config-r16 ::= SEQUENCE { + sl-MCS-Table-r16 ENUMERATED {qam64, qam256, qam64LowSE}, + sl-MinMCS-PSSCH-r16 INTEGER (0..27), + sl-MaxMCS-PSSCH-r16 INTEGER (0..31) +} + +SL-BetaOffsets-r16 ::= INTEGER (0..31) + +SL-PowerControl-r16 ::= SEQUENCE { + sl-MaxTransPower-r16 INTEGER (-30..33), + sl-Alpha-PSSCH-PSCCH-r16 ENUMERATED {alpha0, alpha04, alpha05, alpha06, alpha07, alpha08, alpha09, alpha1} OPTIONAL, -- Need M + dl-Alpha-PSSCH-PSCCH-r16 ENUMERATED {alpha0, alpha04, alpha05, alpha06, alpha07, alpha08, alpha09, alpha1} OPTIONAL, -- Need S + sl-P0-PSSCH-PSCCH-r16 INTEGER (-16..15) OPTIONAL, -- Need S + dl-P0-PSSCH-PSCCH-r16 INTEGER (-16..15) OPTIONAL, -- Need M + dl-Alpha-PSFCH-r16 ENUMERATED {alpha0, alpha04, alpha05, alpha06, alpha07, alpha08, alpha09, alpha1} OPTIONAL, -- Need S + dl-P0-PSFCH-r16 INTEGER (-16..15) OPTIONAL, -- Need M + ... +} + +-- TAG-SL-RESOURCEPOOL-STOP +-- TAG-SL-RLC-BEARERCONFIG-START + +SL-RLC-BearerConfig-r16 ::= SEQUENCE { + sl-RLC-BearerConfigIndex-r16 SL-RLC-BearerConfigIndex-r16, + sl-ServedRadioBearer-r16 SLRB-Uu-ConfigIndex-r16 OPTIONAL, -- Cond LCH-SetupOnly + sl-RLC-Config-r16 SL-RLC-Config-r16 OPTIONAL, -- Cond LCH-Setup + sl-MAC-LogicalChannelConfig-r16 SL-LogicalChannelConfig-r16 OPTIONAL, -- Cond LCH-Setup + ... +} + +-- TAG-SL-RLC-BEARERCONFIG-STOP +-- TAG-SL-RLC-BEARERCONFIGINDEX-START + +SL-RLC-BearerConfigIndex-r16 ::= INTEGER (1..maxSL-LCID-r16) + +-- TAG-RLC-BEARERCONFIGINDEX-STOP +-- TAG-SL-RLC-CONFIG-START + +SL-RLC-Config-r16 ::= CHOICE { + sl-AM-RLC-r16 SEQUENCE { + sl-SN-FieldLengthAM-r16 SN-FieldLengthAM OPTIONAL, -- Cond SLRBSetup + sl-T-PollRetransmit-r16 T-PollRetransmit, + sl-PollPDU-r16 PollPDU, + sl-PollByte-r16 PollByte, + sl-MaxRetxThreshold-r16 ENUMERATED { t1, t2, t3, t4, t6, t8, t16, t32 }, + ... + }, + sl-UM-RLC-r16 SEQUENCE { + sl-SN-FieldLengthUM-r16 SN-FieldLengthUM OPTIONAL, -- Cond SLRBSetup + ... + }, + ... +} + +-- TAG-SL-RLC-CONFIG-STOP +-- TAG-SL-SCHEDULEDCONFIG-START + +SL-ScheduledConfig-r16 ::= SEQUENCE { + sl-RNTI-r16 RNTI-Value, + mac-MainConfigSL-r16 MAC-MainConfigSL-r16 OPTIONAL, -- Need M + sl-CS-RNTI-r16 RNTI-Value OPTIONAL, -- Need M + sl-PSFCH-ToPUCCH-r16 SEQUENCE (SIZE (1..8)) OF INTEGER (0..15) OPTIONAL, -- Need M + sl-ConfiguredGrantConfigList-r16 SL-ConfiguredGrantConfigList-r16 OPTIONAL, -- Need M + ..., + [[ + sl-DCI-ToSL-Trans-r16 SEQUENCE (SIZE (1..8)) OF INTEGER (1..32) OPTIONAL -- Need M + ]] +} + +MAC-MainConfigSL-r16 ::= SEQUENCE { + sl-BSR-Config-r16 BSR-Config OPTIONAL, -- Need M + ul-PrioritizationThres-r16 INTEGER (1..16) OPTIONAL, -- Need M + sl-PrioritizationThres-r16 INTEGER (1..8) OPTIONAL, -- Need M + ... +} + +SL-ConfiguredGrantConfigList-r16 ::= SEQUENCE { + sl-ConfiguredGrantConfigToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofCG-SL-r16)) OF SL-ConfigIndexCG-r16 OPTIONAL, -- Need N + sl-ConfiguredGrantConfigToAddModList-r16 SEQUENCE (SIZE (1..maxNrofCG-SL-r16)) OF SL-ConfiguredGrantConfig-r16 OPTIONAL -- Need N +} + +-- TAG-SL-SCHEDULEDCONFIG-STOP +-- TAG-SL-SDAP-CONFIG-START + +SL-SDAP-Config-r16 ::= SEQUENCE { + sl-SDAP-Header-r16 ENUMERATED {present, absent}, + sl-DefaultRB-r16 BOOLEAN, + sl-MappedQoS-Flows-r16 CHOICE { + sl-MappedQoS-FlowsList-r16 SEQUENCE (SIZE (1..maxNrofSL-QFIs-r16)) OF SL-QoS-Profile-r16, + sl-MappedQoS-FlowsListDedicated-r16 SL-MappedQoS-FlowsListDedicated-r16 + } OPTIONAL, -- Need M + sl-CastType-r16 ENUMERATED {broadcast, groupcast, unicast, spare1} OPTIONAL, -- Need M + ... +} + +SL-MappedQoS-FlowsListDedicated-r16 ::= SEQUENCE { + sl-MappedQoS-FlowsToAddList-r16 SEQUENCE (SIZE (1..maxNrofSL-QFIs-r16)) OF SL-QoS-FlowIdentity-r16 OPTIONAL, -- Need N + sl-MappedQoS-FlowsToReleaseList-16 SEQUENCE (SIZE (1..maxNrofSL-QFIs-r16)) OF SL-QoS-FlowIdentity-r16 OPTIONAL -- Need N +} + +-- TAG-SL-SDAP-CONFIG-STOP +-- TAG-SL-SYNCCONFIG-START + +SL-SyncConfigList-r16 ::= SEQUENCE (SIZE (1..maxSL-SyncConfig-r16)) OF SL-SyncConfig-r16 + +SL-SyncConfig-r16 ::= SEQUENCE { + sl-SyncRefMinHyst-r16 ENUMERATED {dB0, dB3, dB6, dB9, dB12} OPTIONAL, -- Need R + sl-SyncRefDiffHyst-r16 ENUMERATED {dB0, dB3, dB6, dB9, dB12, dBinf} OPTIONAL, -- Need R + sl-filterCoefficient-r16 FilterCoefficient OPTIONAL, -- Need R + sl-SSB-TimeAllocation1-r16 SL-SSB-TimeAllocation-r16 OPTIONAL, -- Need R + sl-SSB-TimeAllocation2-r16 SL-SSB-TimeAllocation-r16 OPTIONAL, -- Need R + sl-SSB-TimeAllocation3-r16 SL-SSB-TimeAllocation-r16 OPTIONAL, -- Need R + sl-SSID-r16 INTEGER (0..671) OPTIONAL, -- Need R + txParameters-r16 SEQUENCE { + syncTxThreshIC-r16 SL-RSRP-Range-r16 OPTIONAL, -- Need R + syncTxThreshOoC-r16 SL-RSRP-Range-r16 OPTIONAL, -- Need R + syncInfoReserved-r16 BIT STRING (SIZE (2)) OPTIONAL -- Need R + }, + gnss-Sync-r16 ENUMERATED {true} OPTIONAL, -- Need R + ... +} + +SL-RSRP-Range-r16 ::= INTEGER (0..13) + +SL-SSB-TimeAllocation-r16 ::= SEQUENCE { + sl-NumSSB-WithinPeriod-r16 ENUMERATED {n1, n2, n4, n8, n16, n32, n64} OPTIONAL, -- Need R + sl-TimeOffsetSSB-r16 INTEGER (0..1279) OPTIONAL, -- Need R + sl-TimeInterval-r16 INTEGER (0..639) OPTIONAL -- Need R +} + +-- TAG-SL-SYNCCONFIG-STOP +-- TAG-SL-THRES-RSRP-LIST-START + +SL-Thres-RSRP-List-r16 ::= SEQUENCE (SIZE (64)) OF SL-Thres-RSRP-r16 + +SL-Thres-RSRP-r16 ::= INTEGER (0..66) + +-- TAG-SL-THRES-RSRP-LIST-STOP +-- TAG-SL-TXPOWER-START + +SL-TxPower-r16 ::= CHOICE{ + minusinfinity-r16 NULL, + txPower-r16 INTEGER (-30..33) +} + +-- TAG-SL-TXPOWER-STOP +-- TAG-SL-TYPETXSYNC-START + +SL-TypeTxSync-r16 ::= ENUMERATED {gnss, gnbEnb, ue} + +-- TAG-SL-TYPETXSYNC-STOP +-- TAG-SL-UE-SELECTEDCONFIG-START + +SL-UE-SelectedConfig-r16 ::= SEQUENCE { + sl-PSSCH-TxConfigList-r16 SL-PSSCH-TxConfigList-r16 OPTIONAL, -- Need R + sl-ProbResourceKeep-r16 ENUMERATED {v0, v0dot2, v0dot4, v0dot6, v0dot8} OPTIONAL, -- Need R + sl-ReselectAfter-r16 ENUMERATED {n1, n2, n3, n4, n5, n6, n7, n8, n9} OPTIONAL, -- Need R + sl-CBR-CommonTxConfigList-r16 SL-CBR-CommonTxConfigList-r16 OPTIONAL, -- Need R + ul-PrioritizationThres-r16 INTEGER (1..16) OPTIONAL, -- Need R + sl-PrioritizationThres-r16 INTEGER (1..8) OPTIONAL, -- Need R + ... +} + +-- TAG-SL-UE-SELECTEDCONFIG-STOP +-- TAG-SL-ZONECONFIG-START + +SL-ZoneConfig-r16 ::= SEQUENCE { + sl-ZoneLength-r16 ENUMERATED { m5, m10, m20, m30, m40, m50, spare2, spare1}, + ... +} + +-- TAG-SL-ZONECONFIG-STOP +-- TAG-SLRB-UU-CONFIGINDEX-START + +SLRB-Uu-ConfigIndex-r16 ::= INTEGER (1..maxNrofSLRB-r16) + +-- TAG-SLRB-UU-CONFIGINDEX-STOP +-- TAG-MULTIPLICITY-AND-TYPE-CONSTRAINT-DEFINITIONS-START + +maxAI-DCI-PayloadSize-r16 INTEGER ::= 128 --Maximum size of the DCI payload scrambled with ai-RNTI +maxAI-DCI-PayloadSize-r16-1 INTEGER ::= 127 --Maximum size of the DCI payload scrambled with ai-RNTI minus 1 +maxBandComb INTEGER ::= 65536 -- Maximum number of DL band combinations +maxBandsUTRA-FDD-r16 INTEGER ::= 64 -- Maximum number of bands listed in UTRA-FDD UE caps +maxBH-RLC-ChannelID-r16 INTEGER ::= 65536 -- Maximum value of BH RLC Channel ID +maxBT-IdReport-r16 INTEGER ::= 32 -- Maximum number of Bluetooth IDs to report +maxBT-Name-r16 INTEGER ::= 4 -- Maximum number of Bluetooth name +maxCAG-Cell-r16 INTEGER ::= 16 -- Maximum number of NR CAG cell ranges in SIB3, SIB4 +maxTwoPUCCH-Grp-ConfigList-r16 INTEGER ::= 32 -- Maximum number of supported configuration(s) of {primary PUCCH group + -- config, secondary PUCCH group config} +maxCBR-Config-r16 INTEGER ::= 8 -- Maximum number of CBR range configurations for sidelink communication + -- congestion control +maxCBR-Config-1-r16 INTEGER ::= 7 -- Maximum number of CBR range configurations for sidelink communication + -- congestion control minus 1 +maxCBR-Level-r16 INTEGER ::= 16 -- Maximum nuber of CBR levels +maxCBR-Level-1-r16 INTEGER ::= 15 -- Maximum number of CBR levels minus 1 +maxCellBlack INTEGER ::= 16 -- Maximum number of NR blacklisted cell ranges in SIB3, SIB4 +maxCellHistory-r16 INTEGER ::= 16 -- Maximum number of visited cells reported +maxCellInter INTEGER ::= 16 -- Maximum number of inter-Freq cells listed in SIB4 +maxCellIntra INTEGER ::= 16 -- Maximum number of intra-Freq cells listed in SIB3 +maxCellMeasEUTRA INTEGER ::= 32 -- Maximum number of cells in E-UTRAN +maxCellMeasIdle-r16 INTEGER ::= 8 -- Maximum number of cells per carrier for idle/inactive measurements +maxCellMeasUTRA-FDD-r16 INTEGER ::= 32 -- Maximum number of cells in FDD UTRAN +maxCellWhite INTEGER ::= 16 -- Maximum number of NR whitelisted cell ranges in SIB3, SIB4 +maxEARFCN INTEGER ::= 262143 -- Maximum value of E-UTRA carrier frequency +maxEUTRA-CellBlack INTEGER ::= 16 -- Maximum number of E-UTRA blacklisted physical cell identity ranges + -- in SIB5 +maxEUTRA-NS-Pmax INTEGER ::= 8 -- Maximum number of NS and P-Max values per band +maxLogMeasReport-r16 INTEGER ::= 520 -- Maximum number of entries for logged measurements +maxMultiBands INTEGER ::= 8 -- Maximum number of additional frequency bands that a cell belongs to +maxNARFCN INTEGER ::= 3279165 -- Maximum value of NR carrier frequency +maxNR-NS-Pmax INTEGER ::= 8 -- Maximum number of NS and P-Max values per band +maxFreqIdle-r16 INTEGER ::= 8 -- Maximum number of carrier frequencies for idle/inactive measurements +maxNrofServingCells INTEGER ::= 32 -- Max number of serving cells (SpCells + SCells) +maxNrofServingCells-1 INTEGER ::= 31 -- Max number of serving cells (SpCell + SCells) per cell group +maxNrofAggregatedCellsPerCellGroup INTEGER ::= 16 +maxNrofDUCells-r16 INTEGER ::= 512 -- Max number of cells configured on the collocated IAB-DU +maxNrofAssociatedDUCellsPerMT-r16 INTEGER ::= 65535 +maxNrofAvailabilityCombinationsPerSet-r16 INTEGER ::= 512 -- Max number of AvailabilityCombinationId used in the DCI format 2_5 +maxNrofAvailabilityCombinationsPerSet-r16-1 INTEGER ::= 511 -- Max number of AvailabilityCombinationId used in the DCI format 2_5 minus 1 +maxNrofSCells INTEGER ::= 31 -- Max number of secondary serving cells per cell group +maxNrofCellMeas INTEGER ::= 32 -- Maximum number of entries in each of the cell lists in a measurement object +maxNrofCG-SL-r16 INTEGER ::= 8 -- Max number of sidelink configured grant +maxNrofCG-SL-r16-1 INTEGER ::= 7 -- Max number of sidelink configured grant minus 1 +maxNrofSS-BlocksToAverage INTEGER ::= 16 -- Max number for the (max) number of SS blocks to average to determine cell measurement +maxNrofCondCells-r16 INTEGER ::= 8 -- Max number of conditional candidate SpCells +maxNrofCSI-RS-ResourcesToAverage INTEGER ::= 16 -- Max number for the (max) number of CSI-RS to average to determine cell measurement +maxNrofDL-Allocations INTEGER ::= 16 -- Maximum number of PDSCH time domain resource allocations +maxNrofSR-ConfigPerCellGroup INTEGER ::= 8 -- Maximum number of SR configurations per cell group +maxLCG-ID INTEGER ::= 7 -- Maximum value of LCG ID +maxLC-ID INTEGER ::= 32 -- Maximum value of Logical Channel ID +maxLC-ID-Iab-r16 INTEGER ::= 65855 -- Maximum value of BH Logical Channel ID extension +maxLTE-CRS-Patterns-r16 INTEGER ::= 3 -- Maximum number of additional LTE CRS rate matching patterns +maxNrofTAGs INTEGER ::= 4 -- Maximum number of Timing Advance Groups +maxNrofTAGs-1 INTEGER ::= 3 -- Maximum number of Timing Advance Groups minus 1 +maxNrofBWPs INTEGER ::= 4 -- Maximum number of BWPs per serving cell +maxNrofCombIDC INTEGER ::= 128 -- Maximum number of reported MR-DC combinations for IDC +maxNrofSymbols-1 INTEGER ::= 13 -- Maximum index identifying a symbol within a slot (14 symbols, indexed from 0..13) +maxNrofSlots INTEGER ::= 320 -- Maximum number of slots in a 10 ms period +maxNrofSlots-1 INTEGER ::= 319 -- Maximum number of slots in a 10 ms period minus 1 +maxNrofPhysicalResourceBlocks INTEGER ::= 275 -- Maximum number of PRBs +maxNrofPhysicalResourceBlocks-1 INTEGER ::= 274 -- Maximum number of PRBs minus 1 +maxNrofPhysicalResourceBlocksPlus1 INTEGER ::= 276 -- Maximum number of PRBs plus 1 +maxNrofControlResourceSets INTEGER ::= 12 -- Max number of CoReSets configurable on a serving cell +maxNrofControlResourceSets-1 INTEGER ::= 11 -- Max number of CoReSets configurable on a serving cell minus 1 +maxNrofControlResourceSets-1-r16 INTEGER ::= 15 -- Max number of CoReSets configurable on a serving cell extended in minus 1 +maxNrofCoresetPools-r16 INTEGER ::= 2 -- Maximum number of CORESET pools +maxCoReSetDuration INTEGER ::= 3 -- Max number of OFDM symbols in a control resource set +maxNrofSearchSpaces-1 INTEGER ::= 39 -- Max number of Search Spaces minus 1 +maxSFI-DCI-PayloadSize INTEGER ::= 128 -- Max number payload of a DCI scrambled with SFI-RNTI +maxSFI-DCI-PayloadSize-1 INTEGER ::= 127 -- Max number payload of a DCI scrambled with SFI-RNTI minus 1 +maxIAB-IP-Address-r16 INTEGER ::= 32 -- Max number of assigned IP addresses +maxINT-DCI-PayloadSize INTEGER ::= 126 -- Max number payload of a DCI scrambled with INT-RNTI +maxINT-DCI-PayloadSize-1 INTEGER ::= 125 -- Max number payload of a DCI scrambled with INT-RNTI minus 1 +maxNrofRateMatchPatterns INTEGER ::= 4 -- Max number of rate matching patterns that may be configured +maxNrofRateMatchPatterns-1 INTEGER ::= 3 -- Max number of rate matching patterns that may be configured minus 1 +maxNrofRateMatchPatternsPerGroup INTEGER ::= 8 -- Max number of rate matching patterns that may be configured in one group +maxNrofCSI-ReportConfigurations INTEGER ::= 48 -- Maximum number of report configurations +maxNrofCSI-ReportConfigurations-1 INTEGER ::= 47 -- Maximum number of report configurations minus 1 +maxNrofCSI-ResourceConfigurations INTEGER ::= 112 -- Maximum number of resource configurations +maxNrofCSI-ResourceConfigurations-1 INTEGER ::= 111 -- Maximum number of resource configurations minus 1 +maxNrofAP-CSI-RS-ResourcesPerSet INTEGER ::= 16 +maxNrOfCSI-AperiodicTriggers INTEGER ::= 128 -- Maximum number of triggers for aperiodic CSI reporting +maxNrofReportConfigPerAperiodicTrigger INTEGER ::= 16 -- Maximum number of report configurations per trigger state for aperiodic reporting +maxNrofNZP-CSI-RS-Resources INTEGER ::= 192 -- Maximum number of Non-Zero-Power (NZP) CSI-RS resources +maxNrofNZP-CSI-RS-Resources-1 INTEGER ::= 191 -- Maximum number of Non-Zero-Power (NZP) CSI-RS resources minus 1 +maxNrofNZP-CSI-RS-ResourcesPerSet INTEGER ::= 64 -- Maximum number of NZP CSI-RS resources per resource set +maxNrofNZP-CSI-RS-ResourceSets INTEGER ::= 64 -- Maximum number of NZP CSI-RS resources per cell +maxNrofNZP-CSI-RS-ResourceSets-1 INTEGER ::= 63 -- Maximum number of NZP CSI-RS resources per cell minus 1 +maxNrofNZP-CSI-RS-ResourceSetsPerConfig INTEGER ::= 16 -- Maximum number of resource sets per resource configuration +maxNrofNZP-CSI-RS-ResourcesPerConfig INTEGER ::= 128 -- Maximum number of resources per resource configuration +maxNrofZP-CSI-RS-Resources INTEGER ::= 32 -- Maximum number of Zero-Power (ZP) CSI-RS resources +maxNrofZP-CSI-RS-Resources-1 INTEGER ::= 31 -- Maximum number of Zero-Power (ZP) CSI-RS resources minus 1 +maxNrofZP-CSI-RS-ResourceSets-1 INTEGER ::= 15 +maxNrofZP-CSI-RS-ResourcesPerSet INTEGER ::= 16 +maxNrofZP-CSI-RS-ResourceSets INTEGER ::= 16 +maxNrofCSI-IM-Resources INTEGER ::= 32 -- Maximum number of CSI-IM resources. See CSI-IM-ResourceMax in 38.214. +maxNrofCSI-IM-Resources-1 INTEGER ::= 31 -- Maximum number of CSI-IM resources minus 1. See CSI-IM-ResourceMax in 38.214. +maxNrofCSI-IM-ResourcesPerSet INTEGER ::= 8 -- Maximum number of CSI-IM resources per set. See CSI-IM-ResourcePerSetMax in 38.214 +maxNrofCSI-IM-ResourceSets INTEGER ::= 64 -- Maximum number of NZP CSI-IM resources per cell +maxNrofCSI-IM-ResourceSets-1 INTEGER ::= 63 -- Maximum number of NZP CSI-IM resources per cell minus 1 +maxNrofCSI-IM-ResourceSetsPerConfig INTEGER ::= 16 -- Maximum number of CSI IM resource sets per resource configuration +maxNrofCSI-SSB-ResourcePerSet INTEGER ::= 64 -- Maximum number of SSB resources in a resource set +maxNrofCSI-SSB-ResourceSets INTEGER ::= 64 -- Maximum number of CSI SSB resource sets per cell +maxNrofCSI-SSB-ResourceSets-1 INTEGER ::= 63 -- Maximum number of CSI SSB resource sets per cell minus 1 +maxNrofCSI-SSB-ResourceSetsPerConfig INTEGER ::= 1 -- Maximum number of CSI SSB resource sets per resource configuration +maxNrofFailureDetectionResources INTEGER ::= 10 -- Maximum number of failure detection resources +maxNrofFailureDetectionResources-1 INTEGER ::= 9 -- Maximum number of failure detection resources minus 1 +maxNrofFreqSL-r16 INTEGER ::= 8 -- Maximum number of carrier frequncy for for NR sidelink communication +maxNrofSL-BWPs-r16 INTEGER ::= 4 -- Maximum number of BWP for for NR sidelink communication +maxFreqSL-EUTRA-r16 INTEGER ::= 8 -- Maximum number of EUTRA anchor carrier frequncy for NR sidelink communication +maxNrofSL-MeasId-r16 INTEGER ::= 64 -- Maximum number of sidelink measurement identity (RSRP) per destination +maxNrofSL-ObjectId-r16 INTEGER ::= 64 -- Maximum number of sidelink measurement objects (RSRP) per destination +maxNrofSL-ReportConfigId-r16 INTEGER ::= 64 -- Maximum number of sidelink measurement reporting configuration(RSRP) per destination +maxNrofSL-PoolToMeasureNR-r16 INTEGER ::= 8 -- Maximum number of resoure pool for NR sidelink measurement to measure for + -- each measurement object (for CBR) +maxFreqSL-NR-r16 INTEGER ::= 8 -- Maximum number of NR anchor carrier frequncy for NR sidelink communication +maxNrofSL-QFIs-r16 INTEGER ::= 2048 -- Maximum number of QoS flow for NR sidelink communication per UE +maxNrofSL-QFIsPerDest-r16 INTEGER ::= 64 -- Maximum number of QoS flow per destination for NR sidelink communication +maxNrofObjectId INTEGER ::= 64 -- Maximum number of measurement objects +maxNrofPageRec INTEGER ::= 32 -- Maximum number of page records +maxNrofPCI-Ranges INTEGER ::= 8 -- Maximum number of PCI ranges +maxPLMN INTEGER ::= 12 -- Maximum number of PLMNs broadcast and reported by UE at establisghment +maxNrofCSI-RS-ResourcesRRM INTEGER ::= 96 -- Maximum number of CSI-RS resources per cell for an RRM measurement object +maxNrofCSI-RS-ResourcesRRM-1 INTEGER ::= 95 -- Maximum number of CSI-RS resources per cell for an RRM measurement object minus 1 +maxNrofMeasId INTEGER ::= 64 -- Maximum number of configured measurements +maxNrofQuantityConfig INTEGER ::= 2 -- Maximum number of quantity configurations +maxNrofCSI-RS-CellsRRM INTEGER ::= 96 -- Maximum number of cells with CSI-RS resources for an RRM measurement object +maxNrofSL-Dest-r16 INTEGER ::= 32 -- Maximum number of destination for NR sidelink communication +maxNrofSL-Dest-1-r16 INTEGER ::= 31 -- Highest index of destination for NR sidelink communication +maxNrofSLRB-r16 INTEGER ::= 512 -- Maximum number of radio bearer for NR sidelink communication per UE +maxSL-LCID-r16 INTEGER ::= 512 -- Maximum number of RLC bearer for NR sidelink communication per UE +maxSL-SyncConfig-r16 INTEGER ::= 16 -- Maximum number of sidelink Sync configurations +maxNrofRXPool-r16 INTEGER ::= 16 -- Maximum number of Rx resource poolfor NR sidelink communication +maxNrofTXPool-r16 INTEGER ::= 8 -- Maximum number of Tx resourcepoolfor NR sidelink communication +maxNrofPoolID-r16 INTEGER ::= 16 -- Maximum index of resource pool for NR sidelink communication +maxNrofSRS-PathlossReferenceRS-r16 INTEGER ::= 64 -- Maximum number of RSs used as pathloss reference for SRS power control. +maxNrofSRS-PathlossReferenceRS-1-r16 INTEGER ::= 63 -- Maximum number of RSs used as pathloss reference for SRS power control-1. +maxNrofSRS-ResourceSets INTEGER ::= 16 -- Maximum number of SRS resource sets in a BWP. +maxNrofSRS-ResourceSets-1 INTEGER ::= 15 -- Maximum number of SRS resource sets in a BWP minus 1. +maxNrofSRS-PosResourceSets-r16 INTEGER ::= 16 -- Maximum number of SRS Positioning resource sets in a BWP. +maxNrofSRS-PosResourceSets-1-r16 INTEGER ::= 15 -- Maximum number of SRS Positioning resource sets in a BWP minus 1. +maxNrofSRS-Resources INTEGER ::= 64 -- Maximum number of SRS resources. +maxNrofSRS-Resources-1 INTEGER ::= 63 -- Maximum number of SRS resources minus 1. +maxNrofSRS-PosResources-r16 INTEGER ::= 64 -- Maximum number of SRS Positioning resources. +maxNrofSRS-PosResources-1-r16 INTEGER ::= 63 -- Maximum number of SRS Positioning resources in an SRS Positioning + -- resource set minus 1. +maxNrofSRS-ResourcesPerSet INTEGER ::= 16 -- Maximum number of SRS resources in an SRS resource set +maxNrofSRS-TriggerStates-1 INTEGER ::= 3 -- Maximum number of SRS trigger states minus 1, i.e., the largest code point. +maxNrofSRS-TriggerStates-2 INTEGER ::= 2 -- Maximum number of SRS trigger states minus 2. +maxRAT-CapabilityContainers INTEGER ::= 8 -- Maximum number of interworking RAT containers (incl NR and MRDC) +maxSimultaneousBands INTEGER ::= 32 -- Maximum number of simultaneously aggregated bands +maxULTxSwitchingBandPairs INTEGER ::= 32 -- Maximum number of band pairs supporting dynamic UL Tx switching in a band combination +maxNrofSlotFormatCombinationsPerSet INTEGER ::= 512 -- Maximum number of Slot Format Combinations in a SF-Set. +maxNrofSlotFormatCombinationsPerSet-1 INTEGER ::= 511 -- Maximum number of Slot Format Combinations in a SF-Set minus 1. +maxNrofTrafficPattern-r16 INTEGER ::= 8 -- Maximum number of Traffic Pattern for NR sidelink communication. +maxNrofPUCCH-Resources INTEGER ::= 128 +maxNrofPUCCH-Resources-1 INTEGER ::= 127 +maxNrofPUCCH-ResourceSets INTEGER ::= 4 -- Maximum number of PUCCH Resource Sets +maxNrofPUCCH-ResourceSets-1 INTEGER ::= 3 -- Maximum number of PUCCH Resource Sets minus 1. +maxNrofPUCCH-ResourcesPerSet INTEGER ::= 32 -- Maximum number of PUCCH Resources per PUCCH-ResourceSet +maxNrofPUCCH-P0-PerSet INTEGER ::= 8 -- Maximum number of P0-pucch present in a p0-pucch set +maxNrofPUCCH-PathlossReferenceRSs INTEGER ::= 4 -- Maximum number of RSs used as pathloss reference for PUCCH power control. +maxNrofPUCCH-PathlossReferenceRSs-1 INTEGER ::= 3 -- Maximum number of RSs used as pathloss reference for PUCCH power control minus 1. +maxNrofPUCCH-PathlossReferenceRSs-r16 INTEGER ::= 64 -- Maximum number of RSs used as pathloss reference for PUCCH power control extended. +maxNrofPUCCH-PathlossReferenceRSs-1-r16 INTEGER ::= 63 -- Maximum number of RSs used as pathloss reference for PUCCH power control + -- minus 1 extended. +maxNrofPUCCH-PathlossReferenceRSsDiff-r16 INTEGER ::= 60 -- Difference between the extended maximum and the non-extended maximum +maxNrofPUCCH-ResourceGroups-r16 INTEGER ::= 4 -- Maximum number of PUCCH resources groups. +maxNrofPUCCH-ResourcesPerGroup-r16 INTEGER ::= 128 -- Maximum number of PUCCH resources in a PUCCH group. +maxNrofMultiplePUSCHs-r16 INTEGER ::= 8 -- Maximum number of multiple PUSCHs in PUSCH TDRA list +maxNrofP0-PUSCH-AlphaSets INTEGER ::= 30 -- Maximum number of P0-pusch-alpha-sets (see 38,213, clause 7.1) +maxNrofP0-PUSCH-AlphaSets-1 INTEGER ::= 29 -- Maximum number of P0-pusch-alpha-sets minus 1 (see 38,213, clause 7.1) +maxNrofPUSCH-PathlossReferenceRSs INTEGER ::= 4 -- Maximum number of RSs used as pathloss reference for PUSCH power control. +maxNrofPUSCH-PathlossReferenceRSs-1 INTEGER ::= 3 -- Maximum number of RSs used as pathloss reference for PUSCH power control minus 1. +maxNrofPUSCH-PathlossReferenceRSs-r16 INTEGER ::= 64 -- Maximum number of RSs used as pathloss reference for PUSCH power control extended +maxNrofPUSCH-PathlossReferenceRSs-1-r16 INTEGER ::= 63 -- Maximum number of RSs used as pathloss reference for PUSCH power control minus 1 +maxNrofPUSCH-PathlossReferenceRSsDiff-r16 INTEGER ::= 60 -- Difference between maxNrofPUSCH-PathlossReferenceRSs-r16 and + -- maxNrofPUSCH-PathlossReferenceRSs +maxNrofNAICS-Entries INTEGER ::= 8 -- Maximum number of supported NAICS capability set +maxBands INTEGER ::= 1024 -- Maximum number of supported bands in UE capability. +maxBandsMRDC INTEGER ::= 1280 +maxBandsEUTRA INTEGER ::= 256 +maxCellReport INTEGER ::= 8 +maxDRB INTEGER ::= 29 -- Maximum number of DRBs (that can be added in DRB-ToAddModLIst). +maxFreq INTEGER ::= 8 -- Max number of frequencies. +maxFreqLayers INTEGER ::= 4 -- Max number of frequency layers. +maxFreqIDC-r16 INTEGER ::= 128 -- Max number of frequencies for IDC indication. +maxCombIDC-r16 INTEGER ::= 128 -- Max number of reported UL CA for IDC indication. +maxFreqIDC-MRDC INTEGER ::= 32 -- Maximum number of candidate NR frequencies for MR-DC IDC indication +maxNrofCandidateBeams INTEGER ::= 16 -- Max number of PRACH-ResourceDedicatedBFR that in BFR config. +maxNrofCandidateBeams-r16 INTEGER ::= 64 -- Max number of candidate beam resources in BFR config. +maxNrofCandidateBeamsExt-r16 INTEGER ::= 48 -- Max number of PRACH-ResourceDedicatedBFR in the CandidateBeamRSListExt +maxNrofPCIsPerSMTC INTEGER ::= 64 -- Maximun number of PCIs per SMTC. +maxNrofQFIs INTEGER ::= 64 +maxNrofResourceAvailabilityPerCombination-r16 INTEGER ::= 256 +maxNrOfSemiPersistentPUSCH-Triggers INTEGER ::= 64 -- Maximum number of triggers for semi persistent reporting on PUSCH +maxNrofSR-Resources INTEGER ::= 8 -- Maximum number of SR resources per BWP in a cell. +maxNrofSlotFormatsPerCombination INTEGER ::= 256 +maxNrofSpatialRelationInfos INTEGER ::= 8 +maxNrofSpatialRelationInfos-plus-1 INTEGER ::= 9 +maxNrofSpatialRelationInfos-r16 INTEGER ::= 64 +maxNrofSpatialRelationInfosDiff-r16 INTEGER ::= 56 -- Difference between maxNrofSpatialRelationInfos-r16 and maxNrofSpatialRelationInfos +maxNrofIndexesToReport INTEGER ::= 32 +maxNrofIndexesToReport2 INTEGER ::= 64 +maxNrofSSBs-r16 INTEGER ::= 64 -- Maximum number of SSB resources in a resource set. +maxNrofSSBs-1 INTEGER ::= 63 -- Maximum number of SSB resources in a resource set minus 1. +maxNrofS-NSSAI INTEGER ::= 8 -- Maximum number of S-NSSAI. +maxNrofTCI-StatesPDCCH INTEGER ::= 64 +maxNrofTCI-States INTEGER ::= 128 -- Maximum number of TCI states. +maxNrofTCI-States-1 INTEGER ::= 127 -- Maximum number of TCI states minus 1. +maxNrofUL-Allocations INTEGER ::= 16 -- Maximum number of PUSCH time domain resource allocations. +maxQFI INTEGER ::= 63 +maxRA-CSIRS-Resources INTEGER ::= 96 +maxRA-OccasionsPerCSIRS INTEGER ::= 64 -- Maximum number of RA occasions for one CSI-RS +maxRA-Occasions-1 INTEGER ::= 511 -- Maximum number of RA occasions in the system +maxRA-SSB-Resources INTEGER ::= 64 +maxSCSs INTEGER ::= 5 +maxSecondaryCellGroups INTEGER ::= 3 +maxNrofServingCellsEUTRA INTEGER ::= 32 +maxMBSFN-Allocations INTEGER ::= 8 +maxNrofMultiBands INTEGER ::= 8 +maxCellSFTD INTEGER ::= 3 -- Maximum number of cells for SFTD reporting +maxReportConfigId INTEGER ::= 64 +maxNrofCodebooks INTEGER ::= 16 -- Maximum number of codebooks suppoted by the UE +maxNrofCSI-RS-ResourcesExt-r16 INTEGER ::= 16 -- Maximum number of codebook resources supported by the UE for eType2/Codebook combo +maxNrofCSI-RS-Resources INTEGER ::= 7 -- Maximum number of codebook resources supported by the UE +maxNrofCSI-RS-ResourcesAlt-r16 INTEGER ::= 512 -- Maximum number of alternative codebook resources supported by the UE +maxNrofCSI-RS-ResourcesAlt-1-r16 INTEGER ::= 511 -- Maximum number of alternative codebook resources supported by the UE minus 1 +maxNrofSRI-PUSCH-Mappings INTEGER ::= 16 +maxNrofSRI-PUSCH-Mappings-1 INTEGER ::= 15 +maxSIB INTEGER::= 32 -- Maximum number of SIBs +maxSI-Message INTEGER::= 32 -- Maximum number of SI messages +maxPO-perPF INTEGER ::= 4 -- Maximum number of paging occasion per paging frame +maxAccessCat-1 INTEGER ::= 63 -- Maximum number of Access Categories minus 1 +maxBarringInfoSet INTEGER ::= 8 -- Maximum number of Access Categories +maxCellEUTRA INTEGER ::= 8 -- Maximum number of E-UTRA cells in SIB list +maxEUTRA-Carrier INTEGER ::= 8 -- Maximum number of E-UTRA carriers in SIB list +maxPLMNIdentities INTEGER ::= 8 -- Maximum number of PLMN identites in RAN area configurations +maxDownlinkFeatureSets INTEGER ::= 1024 -- (for NR DL) Total number of FeatureSets (size of the pool) +maxUplinkFeatureSets INTEGER ::= 1024 -- (for NR UL) Total number of FeatureSets (size of the pool) +maxEUTRA-DL-FeatureSets INTEGER ::= 256 -- (for E-UTRA) Total number of FeatureSets (size of the pool) +maxEUTRA-UL-FeatureSets INTEGER ::= 256 -- (for E-UTRA) Total number of FeatureSets (size of the pool) +maxFeatureSetsPerBand INTEGER ::= 128 -- (for NR) The number of feature sets associated with one band. +maxPerCC-FeatureSets INTEGER ::= 1024 -- (for NR) Total number of CC-specific FeatureSets (size of the pool) +maxFeatureSetCombinations INTEGER ::= 1024 -- (for MR-DC/NR)Total number of Feature set combinations (size of the pool) +maxInterRAT-RSTD-Freq INTEGER ::= 3 +maxHRNN-Len-r16 INTEGER ::= 48 -- Maximum length of HRNNs +maxNPN-r16 INTEGER ::= 12 -- Maximum number of NPNs broadcast and reported by UE at establishment +maxNrOfMinSchedulingOffsetValues-r16 INTEGER ::= 2 -- Maximum number of min. scheduling offset (K0/K2) configurations +maxK0-SchedulingOffset-r16 INTEGER ::= 16 -- Maximum number of slots configured as min. scheduling offset (K0) +maxK2-SchedulingOffset-r16 INTEGER ::= 16 -- Maximum number of slots configured as min. scheduling offset (K2) +maxDCI-2-6-Size-r16 INTEGER ::= 140 -- Maximum size of DCI format 2-6 +maxDCI-2-6-Size-1-r16 INTEGER ::= 139 -- Maximum DCI format 2-6 size minus 1 +maxNrofUL-Allocations-r16 INTEGER ::= 64 -- Maximum number of PUSCH time domain resource allocations +maxNrofP0-PUSCH-Set-r16 INTEGER ::= 2 -- Maximum number of P0 PUSCH set(s) +maxOnDemandSIB-r16 INTEGER ::= 8 -- Maximum number of SIB(s) that can be requested on-demand +maxOnDemandPosSIB-r16 INTEGER ::= 32 -- Maximum number of posSIB(s) that can be requested on-demand +maxCI-DCI-PayloadSize-r16 INTEGER ::= 126 -- Maximum number of the DCI size for CI +maxCI-DCI-PayloadSize-r16-1 INTEGER ::= 125 -- Maximum number of the DCI size for CI minus 1 +maxWLAN-Id-Report-r16 INTEGER ::= 32 -- Maximum number of WLAN IDs to report +maxWLAN-Name-r16 INTEGER ::= 4 -- Maximum number of WLAN name +maxRAReport-r16 INTEGER ::= 8 -- Maximum number of RA procedures information to be included in the RA report +maxTxConfig-r16 INTEGER ::= 64 -- Maximum number of sidelink transmission parameters configurations +maxTxConfig-1-r16 INTEGER ::= 63 -- Maximum number of sidelink transmission parameters configurations minus 1 +maxPSSCH-TxConfig-r16 INTEGER ::= 16 -- Maximum number of PSSCH TX configurations +maxNrofCLI-RSSI-Resources-r16 INTEGER ::= 64 -- Maximum number of CLI-RSSI resources for UE +maxNrofCLI-RSSI-Resources-r16-1 INTEGER ::= 63 -- Maximum number of CLI-RSSI resources for UE minus 1 +maxNrofCLI-SRS-Resources-r16 INTEGER ::= 32 -- Maximum number of SRS resources for CLI measurement for UE +maxCLI-Report-r16 INTEGER ::= 8 +maxNrofConfiguredGrantConfig-r16 INTEGER ::= 12 -- Maximum number of configured grant configurations per BWP +maxNrofConfiguredGrantConfig-r16-1 INTEGER ::= 11 -- Maximum number of configured grant configurations per BWP minus 1 +maxNrofCG-Type2DeactivationState INTEGER ::= 16 -- Maximum number of deactivation state for type 2 configured grants per BWP +maxNrofConfiguredGrantConfigMAC-r16 INTEGER ::= 32 -- Maximum number of configured grant configurations per MAC entity +maxNrofConfiguredGrantConfigMAC-r16-1 INTEGER ::= 31 -- Maximum number of configured grant configurations per MAC entity minus 1 +maxNrofSPS-Config-r16 INTEGER ::= 8 -- Maximum number of SPS configurations per BWP +maxNrofSPS-Config-r16-1 INTEGER ::= 7 -- Maximum number of SPS configurations per BWP minus 1 +maxNrofSPS-DeactivationState INTEGER ::= 16 -- Maximum number of deactivation state for SPS per BWP +maxNrofDormancyGroups INTEGER ::= 5 -- +maxNrofPUCCH-ResourceGroups-1-r16 INTEGER ::= 3 -- +maxNrofServingCellsTCI-r16 INTEGER ::= 32 -- Maximum number of serving cells in simultaneousTCI-UpdateList +maxNrofTxDC-TwoCarrier-r16 INTEGER ::= 64 -- Maximum number of UL Tx DC locations reported by the UE for 2CC uplink CA + +-- TAG-MULTIPLICITY-AND-TYPE-CONSTRAINT-DEFINITIONS-STOP + +END + +-- TAG-PC5-RRC-DEFINITIONS-START + +PC5-RRC-Definitions DEFINITIONS AUTOMATIC TAGS ::= + +BEGIN + +IMPORTS + SetupRelease, + RRC-TransactionIdentifier, + SN-FieldLengthAM, + SN-FieldLengthUM, + LogicalChannelIdentity, + maxNrofSLRB-r16, + maxNrofSL-QFIs-r16, + maxNrofSL-QFIsPerDest-r16, + RSRP-Range, + SL-MeasConfig-r16, + SL-MeasId-r16, + FreqBandList, + FreqBandIndicatorNR, + maxSimultaneousBands, + maxBandComb, + maxBands, + BandParametersSidelink-r16, + RLC-ParametersSidelink-r16 + + +FROM NR-RRC-Definitions; + +-- TAG-PC5-RRC-DEFINITIONS-STOP + + +-- TAG-SBCCH-SL-BCH-MESSAGE-START + +SBCCH-SL-BCH-Message ::= SEQUENCE { + message SBCCH-SL-BCH-MessageType +} + +SBCCH-SL-BCH-MessageType::= CHOICE { + c1 CHOICE { + masterInformationBlockSidelink MasterInformationBlockSidelink, + spare1 NULL + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-SBCCH-SL-BCH-MESSAGE-STOP +-- TAG-SCCH-MESSAGE-START + +SCCH-Message ::= SEQUENCE { + message SCCH-MessageType +} + +SCCH-MessageType ::= CHOICE { + c1 CHOICE { + measurementReportSidelink MeasurementReportSidelink, + rrcReconfigurationSidelink RRCReconfigurationSidelink, + rrcReconfigurationCompleteSidelink RRCReconfigurationCompleteSidelink, + rrcReconfigurationFailureSidelink RRCReconfigurationFailureSidelink, + ueCapabilityEnquirySidelink UECapabilityEnquirySidelink, + ueCapabilityInformationSidelink UECapabilityInformationSidelink, + spare2 NULL, spare1 NULL + }, + messageClassExtension SEQUENCE {} +} + +-- TAG-SCCH-MESSAGE-STOP +-- TAG-MASTERINFORMATIONBLOCKSIDELINK-START + +MasterInformationBlockSidelink ::= SEQUENCE { + sl-TDD-Config-r16 BIT STRING (SIZE (12)), + inCoverage-r16 BOOLEAN, + directFrameNumber-r16 BIT STRING (SIZE (10)), + slotIndex-r16 BIT STRING (SIZE (7)), + reservedBits-r16 BIT STRING (SIZE (2)) +} + +-- TAG-MASTERINFORMATIONBLOCKSIDELINK-STOP +-- TAG-MEASUREMENTREPORTSIDELINK-START + +MeasurementReportSidelink ::= SEQUENCE { + criticalExtensions CHOICE { + measurementReportSidelink-r16 MeasurementReportSidelink-IEs-r16, + criticalExtensionsFuture SEQUENCE {} + } +} + +MeasurementReportSidelink-IEs-r16 ::= SEQUENCE { + sl-measResults-r16 SL-MeasResults-r16, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +SL-MeasResults-r16 ::= SEQUENCE { + sl-MeasId-r16 SL-MeasId-r16, + sl-MeasResult-r16 SL-MeasResult-r16, + ... +} + +SL-MeasResult-r16 ::= SEQUENCE { + sl-ResultDMRS-r16 SL-MeasQuantityResult-r16 OPTIONAL, + ... +} + +SL-MeasQuantityResult-r16 ::= SEQUENCE { + sl-RSRP-r16 RSRP-Range OPTIONAL, + ... +} + +-- TAG-MEASUREMENTREPORTSIDELINK-STOP +-- TAG-RRCRECONFIGURATIONSIDELINK-START + +RRCReconfigurationSidelink ::= SEQUENCE { + rrc-TransactionIdentifier-r16 RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcReconfigurationSidelink-r16 RRCReconfigurationSidelink-IEs-r16, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReconfigurationSidelink-IEs-r16 ::= SEQUENCE { + slrb-ConfigToAddModList-r16 SEQUENCE (SIZE (1..maxNrofSLRB-r16)) OF SLRB-Config-r16 OPTIONAL, -- Need N + slrb-ConfigToReleaseList-r16 SEQUENCE (SIZE (1..maxNrofSLRB-r16)) OF SLRB-PC5-ConfigIndex-r16 OPTIONAL, -- Need N + sl-MeasConfig-r16 SetupRelease {SL-MeasConfig-r16} OPTIONAL, -- Need M + sl-CSI-RS-Config-r16 SetupRelease {SL-CSI-RS-Config-r16} OPTIONAL, -- Need M + sl-ResetConfig-r16 ENUMERATED {true} OPTIONAL, -- Need N + sl-LatencyBoundCSI-Report-r16 INTEGER (3..160) OPTIONAL, -- Need M + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +SLRB-Config-r16::= SEQUENCE { + slrb-PC5-ConfigIndex-r16 SLRB-PC5-ConfigIndex-r16, + sl-SDAP-ConfigPC5-r16 SL-SDAP-ConfigPC5-r16 OPTIONAL, -- Need M + sl-PDCP-ConfigPC5-r16 SL-PDCP-ConfigPC5-r16 OPTIONAL, -- Need M + sl-RLC-ConfigPC5-r16 SL-RLC-ConfigPC5-r16 OPTIONAL, -- Need M + sl-MAC-LogicalChannelConfigPC5-r16 SL-LogicalChannelConfigPC5-r16 OPTIONAL, -- Need M + ... +} + +SLRB-PC5-ConfigIndex-r16 ::= INTEGER (1..maxNrofSLRB-r16) + +SL-SDAP-ConfigPC5-r16 ::= SEQUENCE { + sl-MappedQoS-FlowsToAddList-r16 SEQUENCE (SIZE (1.. maxNrofSL-QFIsPerDest-r16)) OF SL-PQFI-r16 OPTIONAL, -- Need N + sl-MappedQoS-FlowsToReleaseList-r16 SEQUENCE (SIZE (1.. maxNrofSL-QFIsPerDest-r16)) OF SL-PQFI-r16 OPTIONAL, -- Need N + sl-SDAP-Header-r16 ENUMERATED {present, absent}, + ... +} + +SL-PDCP-ConfigPC5-r16 ::= SEQUENCE { + sl-PDCP-SN-Size-r16 ENUMERATED {len12bits, len18bits} OPTIONAL, -- Need M + sl-OutOfOrderDelivery-r16 ENUMERATED { true } OPTIONAL, -- Need R + ... +} + +SL-RLC-ConfigPC5-r16 ::= CHOICE { + sl-AM-RLC-r16 SEQUENCE { + sl-SN-FieldLengthAM-r16 SN-FieldLengthAM OPTIONAL, -- Need M + ... + }, + sl-UM-Bi-Directional-RLC-r16 SEQUENCE { + sl-SN-FieldLengthUM-r16 SN-FieldLengthUM OPTIONAL, -- Need M + ... + }, + sl-UM-Uni-Directional-RLC-r16 SEQUENCE { + sl-SN-FieldLengthUM-r16 SN-FieldLengthUM OPTIONAL, -- Need M + ... + } +} + +SL-LogicalChannelConfigPC5-r16 ::= SEQUENCE { + sl-LogicalChannelIdentity-r16 LogicalChannelIdentity, + ... +} + +SL-PQFI-r16 ::= INTEGER (1..64) + +SL-CSI-RS-Config-r16 ::= SEQUENCE { + sl-CSI-RS-FreqAllocation-r16 CHOICE { + sl-OneAntennaPort-r16 BIT STRING (SIZE (12)), + sl-TwoAntennaPort-r16 BIT STRING (SIZE (6)) + } OPTIONAL, -- Need M + sl-CSI-RS-FirstSymbol-r16 INTEGER (3..12) OPTIONAL, -- Need M + ... +} + +-- TAG-RRCRECONFIGURATIONSIDELINK-STOP +-- TAG-RRCRECONFIGURATIONCOMPLETESIDELINK-START + +RRCReconfigurationCompleteSidelink ::= SEQUENCE { + rrc-TransactionIdentifier-r16 RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcReconfigurationCompleteSidelink-r16 RRCReconfigurationCompleteSidelink-IEs-r16, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReconfigurationCompleteSidelink-IEs-r16 ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-RRCRECONFIGURATIONCOMPLETESIDELINK-STOP +-- TAG-RRCRECONFIGURATIONFAILURESIDELINK-START + +RRCReconfigurationFailureSidelink ::= SEQUENCE { + rrc-TransactionIdentifier-r16 RRC-TransactionIdentifier, + criticalExtensions CHOICE { + rrcReconfigurationFailureSidelink-r16 RRCReconfigurationFailureSidelink-IEs-r16, + criticalExtensionsFuture SEQUENCE {} + } +} + +RRCReconfigurationFailureSidelink-IEs-r16 ::= SEQUENCE { + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-RRCRECONFIGURATIONFAILURESIDELINK-STOP +-- TAG-UECAPABILITYENQUIRYSIDELINK-START + +UECapabilityEnquirySidelink ::= SEQUENCE { + rrc-TransactionIdentifier-r16 RRC-TransactionIdentifier, + criticalExtensions CHOICE { + ueCapabilityEnquirySidelink-r16 UECapabilityEnquirySidelink-IEs-r16, + criticalExtensionsFuture SEQUENCE {} + } +} + +UECapabilityEnquirySidelink-IEs-r16 ::= SEQUENCE { + frequencyBandListFilterSidelink-r16 FreqBandList OPTIONAL, -- Need N + ue-CapabilityInformationSidelink-r16 OCTET STRING OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +-- TAG-UECAPABILITYENQUIRYSIDELINK-STOP +-- TAG-UECAPABILITYINFORMATIONSIDELINK-START + +UECapabilityInformationSidelink ::= SEQUENCE { + rrc-TransactionIdentifier-r16 RRC-TransactionIdentifier, + criticalExtensions CHOICE { + ueCapabilityInformationSidelink-r16 UECapabilityInformationSidelink-IEs-r16, + criticalExtensionsFuture SEQUENCE {} + } +} + +UECapabilityInformationSidelink-IEs-r16 ::= SEQUENCE { + accessStratumReleaseSidelink-r16 AccessStratumReleaseSidelink-r16, + pdcp-ParametersSidelink-r16 PDCP-ParametersSidelink-r16 OPTIONAL, + rlc-ParametersSidelink-r16 RLC-ParametersSidelink-r16 OPTIONAL, + supportedBandCombinationListSidelinkNR-r16 BandCombinationListSidelinkNR-r16 OPTIONAL, + supportedBandListSidelink-r16 SEQUENCE (SIZE (1..maxBands)) OF BandSidelinkPC5-r16 OPTIONAL, + appliedFreqBandListFilter-r16 FreqBandList OPTIONAL, + lateNonCriticalExtension OCTET STRING OPTIONAL, + nonCriticalExtension SEQUENCE{} OPTIONAL +} + +AccessStratumReleaseSidelink-r16 ::= ENUMERATED { rel16, spare7, spare6, spare5, spare4, spare3, spare2, spare1, ... } + +PDCP-ParametersSidelink-r16 ::= SEQUENCE { + outOfOrderDeliverySidelink-r16 ENUMERATED {supported} OPTIONAL, + ... +} + +BandCombinationListSidelinkNR-r16 ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombinationParametersSidelinkNR-r16 + +BandCombinationParametersSidelinkNR-r16 ::= SEQUENCE (SIZE (1..maxSimultaneousBands)) OF BandParametersSidelink-r16 + +BandSidelinkPC5-r16 ::= SEQUENCE { + freqBandSidelink-r16 FreqBandIndicatorNR, + --15-1 + sl-Reception-r16 SEQUENCE { + harq-RxProcessSidelink-r16 ENUMERATED {n16, n24, n32, n64}, + pscch-RxSidelink-r16 ENUMERATED {value1, value2}, + scs-CP-PatternRxSidelink-r16 CHOICE { + fr1-r16 SEQUENCE { + scs-15kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-30kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-60kHz-r16 BIT STRING (SIZE (16)) OPTIONAL + }, + fr2-r16 SEQUENCE { + scs-60kHz-r16 BIT STRING (SIZE (16)) OPTIONAL, + scs-120kHz-r16 BIT STRING (SIZE (16)) OPTIONAL + } + } OPTIONAL, + extendedCP-RxSidelink-r16 ENUMERATED {supported} OPTIONAL + } OPTIONAL, + --15-10 + sl-Tx-256QAM-r16 ENUMERATED {supported} OPTIONAL, + --15-12 + lowSE-64QAM-MCS-TableSidelink-r16 ENUMERATED {supported} OPTIONAL, + ..., + [[ + --15-14 + csi-ReportSidelink-r16 SEQUENCE { + csi-RS-PortsSidelink-r16 ENUMERATED {p1, p2} + } OPTIONAL, + --15-19 + rankTwoReception-r16 ENUMERATED {supported} OPTIONAL, + --15-23 + sl-openLoopPC-RSRP-ReportSidelink-r16 ENUMERATED {supported} OPTIONAL, + --13-1 + sl-Rx-256QAM-r16 ENUMERATED {supported} OPTIONAL + ]] +} + +-- TAG-UECAPABILITYINFORMATIONSIDELINK-STOP + +END + +-- NR-UE-VARIABLES-START +NR-UE-Variables DEFINITIONS AUTOMATIC TAGS ::= + +BEGIN + +IMPORTS + ARFCN-ValueNR, + CellIdentity, + EUTRA-PhysCellId, + MeasId, + MeasIdToAddModList, + MeasIdleCarrierEUTRA-r16, + MeasIdleCarrierNR-r16, + MeasResultIdleEUTRA-r16, + MeasResultIdleNR-r16, + MeasObjectToAddModList, + PhysCellId, + RNTI-Value, + ReportConfigToAddModList, + RSRP-Range, + SL-MeasId-r16, + SL-MeasIdList-r16, + SL-MeasObjectList-r16, + SL-ReportConfigList-r16, + SL-QuantityConfig-r16, + Tx-PoolMeasList-r16, + QuantityConfig, + maxNrofCellMeas, + maxNrofMeasId, + maxFreqIdle-r16, + PhysCellIdUTRA-FDD-r16, + ValidityAreaList-r16, + CondReconfigToAddModList-r16, + ConnEstFailReport-r16, + LoggingDuration-r16, + LoggingInterval-r16, + LogMeasInfoList-r16, + LogMeasInfo-r16, + RA-Report-r16, + RLF-Report-r16, + TraceReference-r16, + WLAN-Identifiers-r16, + WLAN-NameList-r16, + BT-NameList-r16, + PLMN-Identity, + maxPLMN, + RA-ReportList-r16, + VisitedCellInfoList-r16, + AbsoluteTimeInfo-r16, + LoggedEventTriggerConfig-r16, + LoggedPeriodicalReportConfig-r16, + Sensor-NameList-r16, + PLMN-IdentityList2-r16, + AreaConfiguration-r16, + maxNrofSL-MeasId-r16, + maxNrofFreqSL-r16, + maxNrofCLI-RSSI-Resources-r16, + maxNrofCLI-SRS-Resources-r16, + RSSI-ResourceId-r16, + SRS-ResourceId +FROM NR-RRC-Definitions; + +-- NR-UE-VARIABLES-STOP +-- TAG-VARCONDITIONALRECONFIG-START + +VarConditionalReconfig ::= SEQUENCE { + condReconfigList CondReconfigToAddModList-r16 OPTIONAL +} + + +-- TAG-VARCONDITIONALRECONFIG-STOP +-- TAG-VARCONNESTFAILREPORT-START + +VarConnEstFailReport-r16 ::= SEQUENCE { + connEstFailReport-r16 ConnEstFailReport-r16, + plmn-Identity-r16 PLMN-Identity +} + +-- TAG-VARCONNESTFAILREPORT-STOP +-- TAG-VARLOGMEASCONFIG-START + +VarLogMeasConfig-r16-IEs ::= SEQUENCE { + areaConfiguration-r16 AreaConfiguration-r16 OPTIONAL, + bt-NameList-r16 BT-NameList-r16 OPTIONAL, + wlan-NameList-r16 WLAN-NameList-r16 OPTIONAL, + sensor-NameList-r16 Sensor-NameList-r16 OPTIONAL, + loggingDuration-r16 LoggingDuration-r16, + reportType CHOICE { + periodical LoggedPeriodicalReportConfig-r16, + eventTriggered LoggedEventTriggerConfig-r16 + } +} +-- TAG-VARLOGMEASCONFIG-STOP +-- TAG-VARLOGMEASREPORT-START + +VarLogMeasReport-r16 ::= SEQUENCE { + absoluteTimeInfo-r16 AbsoluteTimeInfo-r16, + traceReference-r16 TraceReference-r16, + traceRecordingSessionRef-r16 OCTET STRING (SIZE (2)), + tce-Id-r16 OCTET STRING (SIZE (1)), + logMeasInfoList-r16 LogMeasInfoList-r16, + plmn-IdentityList-r16 PLMN-IdentityList2-r16 +} + +-- TAG-VARLOGMEASREPORT-STOP +-- TAG-VARMEASCONFIG-START + +VarMeasConfig ::= SEQUENCE { + -- Measurement identities + measIdList MeasIdToAddModList OPTIONAL, + -- Measurement objects + measObjectList MeasObjectToAddModList OPTIONAL, + -- Reporting configurations + reportConfigList ReportConfigToAddModList OPTIONAL, + -- Other parameters + quantityConfig QuantityConfig OPTIONAL, + s-MeasureConfig CHOICE { + ssb-RSRP RSRP-Range, + csi-RSRP RSRP-Range + } OPTIONAL + +} + +-- TAG-VARMEASCONFIG-STOP +-- TAG-VARMEASCONFIGSL-START + +VarMeasConfigSL-r16 ::= SEQUENCE { + -- NR sidelink measurement identities + sl-MeasIdList-r16 SL-MeasIdList-r16 OPTIONAL, + -- NR sidelink measurement objects + sl-MeasObjectList-r16 SL-MeasObjectList-r16 OPTIONAL, + -- NR sidelink reporting configurations + sl-reportConfigList-r16 SL-ReportConfigList-r16 OPTIONAL, + -- Other parameters + sl-QuantityConfig-r16 SL-QuantityConfig-r16 OPTIONAL +} + +-- TAG-VARMEASCONFIGSL-STOP +-- TAG-VARMEASIDLECONFIG-START + +VarMeasIdleConfig-r16 ::= SEQUENCE { + measIdleCarrierListNR-r16 SEQUENCE (SIZE (1..maxFreqIdle-r16)) OF MeasIdleCarrierNR-r16 OPTIONAL, + measIdleCarrierListEUTRA-r16 SEQUENCE (SIZE (1..maxFreqIdle-r16)) OF MeasIdleCarrierEUTRA-r16 OPTIONAL, + measIdleDuration-r16 ENUMERATED {sec10, sec30, sec60, sec120, sec180, sec240, sec300, spare}, + validityAreaList-r16 ValidityAreaList-r16 OPTIONAL +} + +-- TAG-VARMEASIDLECONFIG-STOP +-- TAG-VARMEASIDLEREPORT-START + +VarMeasIdleReport-r16 ::= SEQUENCE { + measReportIdleNR-r16 MeasResultIdleNR-r16 OPTIONAL, + measReportIdleEUTRA-r16 MeasResultIdleEUTRA-r16 OPTIONAL +} + +-- TAG-VARMEASIDLEREPORT-STOP +-- TAG-VARMEASREPORTLIST-START + +VarMeasReportList ::= SEQUENCE (SIZE (1..maxNrofMeasId)) OF VarMeasReport + +VarMeasReport ::= SEQUENCE { + -- List of measurement that have been triggered + measId MeasId, + cellsTriggeredList CellsTriggeredList OPTIONAL, + numberOfReportsSent INTEGER, + cli-TriggeredList-r16 CLI-TriggeredList-r16 OPTIONAL, + tx-PoolMeasToAddModListNR-r16 Tx-PoolMeasList-r16 OPTIONAL +} + +CellsTriggeredList ::= SEQUENCE (SIZE (1..maxNrofCellMeas)) OF CHOICE { + physCellId PhysCellId, + physCellIdEUTRA EUTRA-PhysCellId, + physCellIdUTRA-FDD-r16 PhysCellIdUTRA-FDD-r16 + } + +CLI-TriggeredList-r16 ::= CHOICE { + srs-RSRP-TriggeredList-r16 SRS-RSRP-TriggeredList-r16, + cli-RSSI-TriggeredList-r16 CLI-RSSI-TriggeredList-r16 + } + +SRS-RSRP-TriggeredList-r16 ::= SEQUENCE (SIZE (1.. maxNrofCLI-SRS-Resources-r16)) OF SRS-ResourceId + +CLI-RSSI-TriggeredList-r16 ::= SEQUENCE (SIZE (1.. maxNrofCLI-RSSI-Resources-r16)) OF RSSI-ResourceId-r16 + +-- TAG-VARMEASREPORTLIST-STOP +-- TAG-VARMEASREPORTLISTSL-START + +VarMeasReportListSL-r16 ::= SEQUENCE (SIZE (1..maxNrofSL-MeasId-r16)) OF VarMeasReportSL-r16 + +VarMeasReportSL-r16 ::= SEQUENCE { + -- List of NR sidelink measurement that have been triggered + sl-MeasId-r16 SL-MeasId-r16, + sl-FrequencyTriggeredList-r16 SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF ARFCN-ValueNR OPTIONAL, + sl-NumberOfReportsSent-r16 INTEGER +} + +-- TAG-VARMEASREPORTLISTSL-STOP +-- TAG-VARMOBILITYHISTORYREPORT-START + +VarMobilityHistoryReport-r16 ::= VisitedCellInfoList-r16 + +-- TAG-VARMOBILITYHISTORYREPORT-STOP +-- TAG-VARPENDINGRNA-UPDATE-START + +VarPendingRNA-Update ::= SEQUENCE { + pendingRNA-Update BOOLEAN OPTIONAL +} + +-- TAG-VARPENDINGRNA-UPDATE-STOP +-- TAG-VARRA-REPORT-START + +VarRA-Report-r16 ::= SEQUENCE { + ra-ReportList-r16 RA-ReportList-r16, + plmn-IdentityList-r16 PLMN-IdentityList-r16 +} + +PLMN-IdentityList-r16 ::= SEQUENCE (SIZE (1..maxPLMN)) OF PLMN-Identity + +-- TAG-VARRA-REPORT-STOP +-- TAG-VARRESUMEMAC-INPUT-START + +VarResumeMAC-Input ::= SEQUENCE { + sourcePhysCellId PhysCellId, + targetCellIdentity CellIdentity, + source-c-RNTI RNTI-Value + +} + +-- TAG-VARRESUMEMAC-INPUT-STOP +-- TAG-VARRLF-REPORT-START + +VarRLF-Report-r16 ::= SEQUENCE { + rlf-Report-r16 RLF-Report-r16, + plmn-IdentityList-r16 PLMN-IdentityList2-r16 +} + +-- TAG-VARRLF-REPORT-STOP +-- TAG-VARSHORTMAC-INPUT-START + +VarShortMAC-Input ::= SEQUENCE { + sourcePhysCellId PhysCellId, + targetCellIdentity CellIdentity, + source-c-RNTI RNTI-Value +} + +-- TAG-VARSHORTMAC-INPUT-STOP + +END + +-- TAG-NR-SIDELINK-PRECONF-DEFINITIONS-START + +NR-Sidelink-Preconf DEFINITIONS AUTOMATIC TAGS ::= + +BEGIN + +IMPORTS + SL-CBR-CommonTxConfigList-r16, + SL-FreqConfigCommon-r16, + SL-RadioBearerConfig-r16, + SL-RLC-BearerConfig-r16, + SL-EUTRA-AnchorCarrierFreqList-r16, + SL-NR-AnchorCarrierFreqList-r16, + SL-MeasConfigCommon-r16, + SL-UE-SelectedConfig-r16, + TDD-UL-DL-ConfigCommon, + maxNrofFreqSL-r16, + maxNrofSLRB-r16, + maxSL-LCID-r16 +FROM NR-RRC-Definitions; + +-- TAG-NR-SIDELINK-PRECONF-DEFINITIONS-STOP + +-- TAG-SL-PRECONFIGURATIONNR-START + +SL-PreconfigurationNR-r16 ::= SEQUENCE { + sidelinkPreconfigNR-r16 SidelinkPreconfigNR-r16, + ... +} + +SidelinkPreconfigNR-r16 ::= SEQUENCE { + sl-PreconfigFreqInfoList-r16 SEQUENCE (SIZE (1..maxNrofFreqSL-r16)) OF SL-FreqConfigCommon-r16 OPTIONAL, + sl-PreconfigNR-AnchorCarrierFreqList-r16 SL-NR-AnchorCarrierFreqList-r16 OPTIONAL, + sl-PreconfigEUTRA-AnchorCarrierFreqList-r16 SL-EUTRA-AnchorCarrierFreqList-r16 OPTIONAL, + sl-RadioBearerPreConfigList-r16 SEQUENCE (SIZE (1..maxNrofSLRB-r16)) OF SL-RadioBearerConfig-r16 OPTIONAL, + sl-RLC-BearerPreConfigList-r16 SEQUENCE (SIZE (1..maxSL-LCID-r16)) OF SL-RLC-BearerConfig-r16 OPTIONAL, + sl-MeasPreConfig-r16 SL-MeasConfigCommon-r16 OPTIONAL, + sl-OffsetDFN-r16 INTEGER (1..1000) OPTIONAL, + t400-r16 ENUMERATED{ms100, ms200, ms300, ms400, ms600, ms1000, ms1500, ms2000} OPTIONAL, + sl-MaxNumConsecutiveDTX-r16 ENUMERATED {n1, n2, n3, n4, n6, n8, n16, n32} OPTIONAL, + sl-SSB-PriorityNR-r16 INTEGER (1..8) OPTIONAL, + sl-PreconfigGeneral-r16 SL-PreconfigGeneral-r16 OPTIONAL, + sl-UE-SelectedPreConfig-r16 SL-UE-SelectedConfig-r16 OPTIONAL, + sl-CSI-Acquisition-r16 ENUMERATED {enabled} OPTIONAL, + sl-RoHC-Profiles-r16 SL-RoHC-Profiles-r16 OPTIONAL, + sl-MaxCID-r16 INTEGER (1..16383) DEFAULT 15, + ... +} + +SL-PreconfigGeneral-r16 ::= SEQUENCE { + sl-TDD-Configuration-r16 TDD-UL-DL-ConfigCommon OPTIONAL, + reservedBits-r16 BIT STRING (SIZE (2)) OPTIONAL, + ... +} + +SL-RoHC-Profiles-r16 ::= SEQUENCE { + profile0x0001-r16 BOOLEAN, + profile0x0002-r16 BOOLEAN, + profile0x0003-r16 BOOLEAN, + profile0x0004-r16 BOOLEAN, + profile0x0006-r16 BOOLEAN, + profile0x0101-r16 BOOLEAN, + profile0x0102-r16 BOOLEAN, + profile0x0103-r16 BOOLEAN, + profile0x0104-r16 BOOLEAN +} + +-- TAG-SL-PRECONFIGURATIONNR-STOP + +END + +-- TAG-NR-INTER-NODE-DEFINITIONS-START +NR-InterNodeDefinitions DEFINITIONS AUTOMATIC TAGS ::= + +BEGIN + +IMPORTS + ARFCN-ValueNR, + ARFCN-ValueEUTRA, + CellIdentity, + CGI-InfoEUTRA, + CGI-InfoNR, + CSI-RS-Index, + CSI-RS-CellMobility, + DRX-Config, + EUTRA-PhysCellId, + FeatureSetDownlinkPerCC-Id, + FeatureSetUplinkPerCC-Id, + FreqBandIndicatorNR, + GapConfig, + maxBandComb, + maxBands, + maxCellSFTD, + maxFeatureSetsPerBand, + maxFreqIDC-MRDC, + maxNrofCombIDC, + maxNrofPhysicalResourceBlocks, + maxNrofSCells, + maxNrofServingCells, + maxNrofServingCells-1, + maxNrofServingCellsEUTRA, + maxNrofIndexesToReport, + maxSimultaneousBands, + MeasQuantityResults, + MeasResultCellListSFTD-EUTRA, + MeasResultCellListSFTD-NR, + MeasResultList2NR, + MeasResultSCG-Failure, + MeasResultServFreqListEUTRA-SCG, + NeedForGapsInfoNR-r16, + OverheatingAssistance, + P-Max, + PhysCellId, + RadioBearerConfig, + RAN-NotificationAreaInfo, + RRCReconfiguration, + ServCellIndex, + SetupRelease, + SSB-Index, + SSB-MTC, + SSB-ToMeasure, + SS-RSSI-Measurement, + ShortMAC-I, + SubcarrierSpacing, + UEAssistanceInformation, + UE-CapabilityRAT-ContainerList, + maxNrofCLI-RSSI-Resources-r16, + maxNrofCLI-SRS-Resources-r16, + RSSI-ResourceId-r16, + SidelinkUEInformationNR-r16, + SRS-ResourceId +FROM NR-RRC-Definitions; + +-- TAG-NR-INTER-NODE-DEFINITIONS-STOP +-- TAG-HANDOVER-COMMAND-START + +HandoverCommand ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE{ + handoverCommand HandoverCommand-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +HandoverCommand-IEs ::= SEQUENCE { + handoverCommandMessage OCTET STRING (CONTAINING RRCReconfiguration), + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-HANDOVER-COMMAND-STOP +-- TAG-HANDOVER-PREPARATION-INFORMATION-START + +HandoverPreparationInformation ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE{ + handoverPreparationInformation HandoverPreparationInformation-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +HandoverPreparationInformation-IEs ::= SEQUENCE { + ue-CapabilityRAT-List UE-CapabilityRAT-ContainerList, + sourceConfig AS-Config OPTIONAL, -- Cond HO + rrm-Config RRM-Config OPTIONAL, + as-Context AS-Context OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +AS-Config ::= SEQUENCE { + rrcReconfiguration OCTET STRING (CONTAINING RRCReconfiguration), + ..., + [[ + sourceRB-SN-Config OCTET STRING (CONTAINING RadioBearerConfig) OPTIONAL, + sourceSCG-NR-Config OCTET STRING (CONTAINING RRCReconfiguration) OPTIONAL, + sourceSCG-EUTRA-Config OCTET STRING OPTIONAL + ]], + [[ + sourceSCG-Configured ENUMERATED {true} OPTIONAL + ]] + +} + +AS-Context ::= SEQUENCE { + reestablishmentInfo ReestablishmentInfo OPTIONAL, + configRestrictInfo ConfigRestrictInfoSCG OPTIONAL, + ..., + [[ ran-NotificationAreaInfo RAN-NotificationAreaInfo OPTIONAL + ]], + [[ ueAssistanceInformation OCTET STRING (CONTAINING UEAssistanceInformation) OPTIONAL -- Cond HO2 + ]], + [[ + selectedBandCombinationSN BandCombinationInfoSN OPTIONAL + ]], + [[ + configRestrictInfoDAPS-r16 ConfigRestrictInfoDAPS-r16 OPTIONAL, + sidelinkUEInformationNR-r16 OCTET STRING OPTIONAL, + sidelinkUEInformationEUTRA-r16 OCTET STRING OPTIONAL, + ueAssistanceInformationEUTRA-r16 OCTET STRING OPTIONAL, + ueAssistanceInformationSCG-r16 OCTET STRING (CONTAINING UEAssistanceInformation) OPTIONAL, -- Cond HO2 + needForGapsInfoNR-r16 NeedForGapsInfoNR-r16 OPTIONAL + ]], + [[ + configRestrictInfoDAPS-v1640 ConfigRestrictInfoDAPS-v1640 OPTIONAL + ]] +} + +ConfigRestrictInfoDAPS-r16 ::= SEQUENCE { + powerCoordination-r16 SEQUENCE { + p-DAPS-Source-r16 P-Max, + p-DAPS-Target-r16 P-Max, + uplinkPowerSharingDAPS-Mode-r16 ENUMERATED {semi-static-mode1, semi-static-mode2, dynamic } + } OPTIONAL +} + +ConfigRestrictInfoDAPS-v1640 ::= SEQUENCE { + sourceFeatureSetPerDownlinkCC-r16 FeatureSetDownlinkPerCC-Id, + sourceFeatureSetPerUplinkCC-r16 FeatureSetUplinkPerCC-Id +} + +ReestablishmentInfo ::= SEQUENCE { + sourcePhysCellId PhysCellId, + targetCellShortMAC-I ShortMAC-I, + additionalReestabInfoList ReestabNCellInfoList OPTIONAL +} + +ReestabNCellInfoList ::= SEQUENCE ( SIZE (1..maxCellPrep) ) OF ReestabNCellInfo + +ReestabNCellInfo::= SEQUENCE{ + cellIdentity CellIdentity, + key-gNodeB-Star BIT STRING (SIZE (256)), + shortMAC-I ShortMAC-I +} + +RRM-Config ::= SEQUENCE { + ue-InactiveTime ENUMERATED { + s1, s2, s3, s5, s7, s10, s15, s20, + s25, s30, s40, s50, min1, min1s20, min1s40, + min2, min2s30, min3, min3s30, min4, min5, min6, + min7, min8, min9, min10, min12, min14, min17, min20, + min24, min28, min33, min38, min44, min50, hr1, + hr1min30, hr2, hr2min30, hr3, hr3min30, hr4, hr5, hr6, + hr8, hr10, hr13, hr16, hr20, day1, day1hr12, day2, + day2hr12, day3, day4, day5, day7, day10, day14, day19, + day24, day30, dayMoreThan30} OPTIONAL, + candidateCellInfoList MeasResultList2NR OPTIONAL, + ..., + [[ + candidateCellInfoListSN-EUTRA MeasResultServFreqListEUTRA-SCG OPTIONAL + ]] +} + +-- TAG-HANDOVER-PREPARATION-INFORMATION-STOP +-- TAG-CG-CONFIG-START + +CG-Config ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE{ + cg-Config CG-Config-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +CG-Config-IEs ::= SEQUENCE { + scg-CellGroupConfig OCTET STRING (CONTAINING RRCReconfiguration) OPTIONAL, + scg-RB-Config OCTET STRING (CONTAINING RadioBearerConfig) OPTIONAL, + configRestrictModReq ConfigRestrictModReqSCG OPTIONAL, + drx-InfoSCG DRX-Info OPTIONAL, + candidateCellInfoListSN OCTET STRING (CONTAINING MeasResultList2NR) OPTIONAL, + measConfigSN MeasConfigSN OPTIONAL, + selectedBandCombination BandCombinationInfoSN OPTIONAL, + fr-InfoListSCG FR-InfoList OPTIONAL, + candidateServingFreqListNR CandidateServingFreqListNR OPTIONAL, + nonCriticalExtension CG-Config-v1540-IEs OPTIONAL +} + +CG-Config-v1540-IEs ::= SEQUENCE { + pSCellFrequency ARFCN-ValueNR OPTIONAL, + reportCGI-RequestNR SEQUENCE { + requestedCellInfo SEQUENCE { + ssbFrequency ARFCN-ValueNR, + cellForWhichToReportCGI PhysCellId + } OPTIONAL + } OPTIONAL, + ph-InfoSCG PH-TypeListSCG OPTIONAL, + nonCriticalExtension CG-Config-v1560-IEs OPTIONAL +} + +CG-Config-v1560-IEs ::= SEQUENCE { + pSCellFrequencyEUTRA ARFCN-ValueEUTRA OPTIONAL, + scg-CellGroupConfigEUTRA OCTET STRING OPTIONAL, + candidateCellInfoListSN-EUTRA OCTET STRING OPTIONAL, + candidateServingFreqListEUTRA CandidateServingFreqListEUTRA OPTIONAL, + needForGaps ENUMERATED {true} OPTIONAL, + drx-ConfigSCG DRX-Config OPTIONAL, + reportCGI-RequestEUTRA SEQUENCE { + requestedCellInfoEUTRA SEQUENCE { + eutraFrequency ARFCN-ValueEUTRA, + cellForWhichToReportCGI-EUTRA EUTRA-PhysCellId + } OPTIONAL + } OPTIONAL, + nonCriticalExtension CG-Config-v1590-IEs OPTIONAL +} + +CG-Config-v1590-IEs ::= SEQUENCE { + scellFrequenciesSN-NR SEQUENCE (SIZE (1.. maxNrofServingCells-1)) OF ARFCN-ValueNR OPTIONAL, + scellFrequenciesSN-EUTRA SEQUENCE (SIZE (1.. maxNrofServingCells-1)) OF ARFCN-ValueEUTRA OPTIONAL, + nonCriticalExtension CG-Config-v1610-IEs OPTIONAL +} + +CG-Config-v1610-IEs ::= SEQUENCE { + drx-InfoSCG2 DRX-Info2 OPTIONAL, + nonCriticalExtension CG-Config-v1620-IEs OPTIONAL +} + +CG-Config-v1620-IEs ::= SEQUENCE { + ueAssistanceInformationSCG-r16 OCTET STRING (CONTAINING UEAssistanceInformation) OPTIONAL, + nonCriticalExtension CG-Config-v1630-IEs OPTIONAL +} + +CG-Config-v1630-IEs ::= SEQUENCE { + selectedToffset-r16 T-Offset-r16 OPTIONAL, + nonCriticalExtension CG-Config-v1640-IEs OPTIONAL +} + +CG-Config-v1640-IEs ::= SEQUENCE { + servCellInfoListSCG-NR-r16 ServCellInfoListSCG-NR-r16 OPTIONAL, + servCellInfoListSCG-EUTRA-r16 ServCellInfoListSCG-EUTRA-r16 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +ServCellInfoListSCG-NR-r16 ::= SEQUENCE (SIZE (1.. maxNrofServingCells)) OF ServCellInfoXCG-NR-r16 + +ServCellInfoXCG-NR-r16 ::= SEQUENCE { + dl-FreqInfo-NR-r16 FrequencyConfig-NR-r16 OPTIONAL, + ul-FreqInfo-NR-r16 FrequencyConfig-NR-r16 OPTIONAL, -- Cond FDD + ... +} + +FrequencyConfig-NR-r16 ::= SEQUENCE { + freqBandIndicatorNR-r16 FreqBandIndicatorNR, + carrierCenterFreq-NR-r16 ARFCN-ValueNR, + carrierBandwidth-NR-r16 INTEGER (1..maxNrofPhysicalResourceBlocks), + subcarrierSpacing-NR-r16 SubcarrierSpacing +} + +ServCellInfoListSCG-EUTRA-r16 ::= SEQUENCE (SIZE (1.. maxNrofServingCellsEUTRA)) OF ServCellInfoXCG-EUTRA-r16 + +ServCellInfoXCG-EUTRA-r16 ::= SEQUENCE { + dl-CarrierFreq-EUTRA-r16 ARFCN-ValueEUTRA OPTIONAL, + ul-CarrierFreq-EUTRA-r16 ARFCN-ValueEUTRA OPTIONAL, -- Cond FDD + transmissionBandwidth-EUTRA-r16 TransmissionBandwidth-EUTRA-r16 OPTIONAL, + ... +} + +TransmissionBandwidth-EUTRA-r16 ::= ENUMERATED {rb6, rb15, rb25, rb50, rb75, rb100} + +PH-TypeListSCG ::= SEQUENCE (SIZE (1..maxNrofServingCells)) OF PH-InfoSCG + +PH-InfoSCG ::= SEQUENCE { + servCellIndex ServCellIndex, + ph-Uplink PH-UplinkCarrierSCG, + ph-SupplementaryUplink PH-UplinkCarrierSCG OPTIONAL, + ... +} + +PH-UplinkCarrierSCG ::= SEQUENCE{ + ph-Type1or3 ENUMERATED {type1, type3}, + ... +} + +MeasConfigSN ::= SEQUENCE { + measuredFrequenciesSN SEQUENCE (SIZE (1..maxMeasFreqsSN)) OF NR-FreqInfo OPTIONAL, + ... +} + +NR-FreqInfo ::= SEQUENCE { + measuredFrequency ARFCN-ValueNR OPTIONAL, + ... +} + +ConfigRestrictModReqSCG ::= SEQUENCE { + requestedBC-MRDC BandCombinationInfoSN OPTIONAL, + requestedP-MaxFR1 P-Max OPTIONAL, + ..., + [[ + requestedPDCCH-BlindDetectionSCG INTEGER (1..15) OPTIONAL, + requestedP-MaxEUTRA P-Max OPTIONAL + ]], + [[ + requestedP-MaxFR2-r16 P-Max OPTIONAL, + requestedMaxInterFreqMeasIdSCG-r16 INTEGER(1..maxMeasIdentitiesMN) OPTIONAL, + requestedMaxIntraFreqMeasIdSCG-r16 INTEGER(1..maxMeasIdentitiesMN) OPTIONAL, + requestedToffset-r16 T-Offset-r16 OPTIONAL + ]] +} + +BandCombinationIndex ::= INTEGER (1..maxBandComb) + +BandCombinationInfoSN ::= SEQUENCE { + bandCombinationIndex BandCombinationIndex, + requestedFeatureSets FeatureSetEntryIndex +} + +FR-InfoList ::= SEQUENCE (SIZE (1..maxNrofServingCells-1)) OF FR-Info + +FR-Info ::= SEQUENCE { + servCellIndex ServCellIndex, + fr-Type ENUMERATED {fr1, fr2} +} + +CandidateServingFreqListNR ::= SEQUENCE (SIZE (1.. maxFreqIDC-MRDC)) OF ARFCN-ValueNR + +CandidateServingFreqListEUTRA ::= SEQUENCE (SIZE (1.. maxFreqIDC-MRDC)) OF ARFCN-ValueEUTRA + +T-Offset-r16 ::= ENUMERATED {ms0dot5, ms0dot75, ms1, ms1dot5, ms2, ms2dot5, ms3, spare1} + +-- TAG-CG-CONFIG-STOP +-- TAG-CG-CONFIG-INFO-START + +CG-ConfigInfo ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE{ + cg-ConfigInfo CG-ConfigInfo-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +CG-ConfigInfo-IEs ::= SEQUENCE { + ue-CapabilityInfo OCTET STRING (CONTAINING UE-CapabilityRAT-ContainerList) OPTIONAL,-- Cond SN-AddMod + candidateCellInfoListMN MeasResultList2NR OPTIONAL, + candidateCellInfoListSN OCTET STRING (CONTAINING MeasResultList2NR) OPTIONAL, + measResultCellListSFTD-NR MeasResultCellListSFTD-NR OPTIONAL, + scgFailureInfo SEQUENCE { + failureType ENUMERATED { t310-Expiry, randomAccessProblem, + rlc-MaxNumRetx, synchReconfigFailure-SCG, + scg-reconfigFailure, + srb3-IntegrityFailure}, + measResultSCG OCTET STRING (CONTAINING MeasResultSCG-Failure) + } OPTIONAL, + configRestrictInfo ConfigRestrictInfoSCG OPTIONAL, + drx-InfoMCG DRX-Info OPTIONAL, + measConfigMN MeasConfigMN OPTIONAL, + sourceConfigSCG OCTET STRING (CONTAINING RRCReconfiguration) OPTIONAL, + scg-RB-Config OCTET STRING (CONTAINING RadioBearerConfig) OPTIONAL, + mcg-RB-Config OCTET STRING (CONTAINING RadioBearerConfig) OPTIONAL, + mrdc-AssistanceInfo MRDC-AssistanceInfo OPTIONAL, + nonCriticalExtension CG-ConfigInfo-v1540-IEs OPTIONAL +} + +CG-ConfigInfo-v1540-IEs ::= SEQUENCE { + ph-InfoMCG PH-TypeListMCG OPTIONAL, + measResultReportCGI SEQUENCE { + ssbFrequency ARFCN-ValueNR, + cellForWhichToReportCGI PhysCellId, + cgi-Info CGI-InfoNR + } OPTIONAL, + nonCriticalExtension CG-ConfigInfo-v1560-IEs OPTIONAL +} + +CG-ConfigInfo-v1560-IEs ::= SEQUENCE { + candidateCellInfoListMN-EUTRA OCTET STRING OPTIONAL, + candidateCellInfoListSN-EUTRA OCTET STRING OPTIONAL, + sourceConfigSCG-EUTRA OCTET STRING OPTIONAL, + scgFailureInfoEUTRA SEQUENCE { + failureTypeEUTRA ENUMERATED { t313-Expiry, randomAccessProblem, + rlc-MaxNumRetx, scg-ChangeFailure}, + measResultSCG-EUTRA OCTET STRING + } OPTIONAL, + drx-ConfigMCG DRX-Config OPTIONAL, + measResultReportCGI-EUTRA SEQUENCE { + eutraFrequency ARFCN-ValueEUTRA, + cellForWhichToReportCGI-EUTRA EUTRA-PhysCellId, + cgi-InfoEUTRA CGI-InfoEUTRA + } OPTIONAL, + measResultCellListSFTD-EUTRA MeasResultCellListSFTD-EUTRA OPTIONAL, + fr-InfoListMCG FR-InfoList OPTIONAL, + nonCriticalExtension CG-ConfigInfo-v1570-IEs OPTIONAL +} + +CG-ConfigInfo-v1570-IEs ::= SEQUENCE { + sftdFrequencyList-NR SFTD-FrequencyList-NR OPTIONAL, + sftdFrequencyList-EUTRA SFTD-FrequencyList-EUTRA OPTIONAL, + nonCriticalExtension CG-ConfigInfo-v1590-IEs OPTIONAL +} + +CG-ConfigInfo-v1590-IEs ::= SEQUENCE { + servFrequenciesMN-NR SEQUENCE (SIZE (1.. maxNrofServingCells-1)) OF ARFCN-ValueNR OPTIONAL, + nonCriticalExtension CG-ConfigInfo-v1610-IEs OPTIONAL +} + +CG-ConfigInfo-v1610-IEs ::= SEQUENCE { + drx-InfoMCG2 DRX-Info2 OPTIONAL, + alignedDRX-Indication ENUMERATED {true} OPTIONAL, + scgFailureInfo-r16 SEQUENCE { + failureType-r16 ENUMERATED { scg-lbtFailure-r16, beamFailureRecoveryFailure-r16, + t312-Expiry-r16, bh-RLF-r16, + spare4, spare3, spare2, spare1}, + measResultSCG-r16 OCTET STRING (CONTAINING MeasResultSCG-Failure) + } OPTIONAL, + scgFailureInfoEUTRA-r16 SEQUENCE { + failureTypeEUTRA-r16 ENUMERATED { scg-lbtFailure-r16, beamFailureRecoveryFailure-r16, + t312-Expiry-r16, spare5, + spare4, spare3, spare2, spare1}, + measResultSCG-EUTRA-r16 OCTET STRING + } OPTIONAL, + sidelinkUEInformationNR-r16 OCTET STRING (CONTAINING SidelinkUEInformationNR-r16) OPTIONAL, + sidelinkUEInformationEUTRA-r16 OCTET STRING OPTIONAL, + nonCriticalExtension CG-ConfigInfo-v1620-IEs OPTIONAL +} + +CG-ConfigInfo-v1620-IEs ::= SEQUENCE { + ueAssistanceInformationSourceSCG-r16 OCTET STRING (CONTAINING UEAssistanceInformation) OPTIONAL, + nonCriticalExtension CG-ConfigInfo-v1640-IEs OPTIONAL +} + +CG-ConfigInfo-v1640-IEs ::= SEQUENCE { + servCellInfoListMCG-NR-r16 ServCellInfoListMCG-NR-r16 OPTIONAL, + servCellInfoListMCG-EUTRA-r16 ServCellInfoListMCG-EUTRA-r16 OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +ServCellInfoListMCG-NR-r16 ::= SEQUENCE (SIZE (1.. maxNrofServingCells)) OF ServCellInfoXCG-NR-r16 + +ServCellInfoListMCG-EUTRA-r16 ::= SEQUENCE (SIZE (1.. maxNrofServingCellsEUTRA)) OF ServCellInfoXCG-EUTRA-r16 + +SFTD-FrequencyList-NR ::= SEQUENCE (SIZE (1..maxCellSFTD)) OF ARFCN-ValueNR + +SFTD-FrequencyList-EUTRA ::= SEQUENCE (SIZE (1..maxCellSFTD)) OF ARFCN-ValueEUTRA + +ConfigRestrictInfoSCG ::= SEQUENCE { + allowedBC-ListMRDC BandCombinationInfoList OPTIONAL, + powerCoordination-FR1 SEQUENCE { + p-maxNR-FR1 P-Max OPTIONAL, + p-maxEUTRA P-Max OPTIONAL, + p-maxUE-FR1 P-Max OPTIONAL + } OPTIONAL, + servCellIndexRangeSCG SEQUENCE { + lowBound ServCellIndex, + upBound ServCellIndex + } OPTIONAL, -- Cond SN-AddMod + maxMeasFreqsSCG INTEGER(1..maxMeasFreqsMN) OPTIONAL, + dummy INTEGER(1..maxMeasIdentitiesMN) OPTIONAL, + ..., + [[ + selectedBandEntriesMNList SEQUENCE (SIZE (1..maxBandComb)) OF SelectedBandEntriesMN OPTIONAL, + pdcch-BlindDetectionSCG INTEGER (1..15) OPTIONAL, + maxNumberROHC-ContextSessionsSN INTEGER(0.. 16384) OPTIONAL + ]], + [[ + maxIntraFreqMeasIdentitiesSCG INTEGER(1..maxMeasIdentitiesMN) OPTIONAL, + maxInterFreqMeasIdentitiesSCG INTEGER(1..maxMeasIdentitiesMN) OPTIONAL + ]], + [[ + p-maxNR-FR1-MCG-r16 P-Max OPTIONAL, + powerCoordination-FR2-r16 SEQUENCE { + p-maxNR-FR2-MCG-r16 P-Max OPTIONAL, + p-maxNR-FR2-SCG-r16 P-Max OPTIONAL, + p-maxUE-FR2-r16 P-Max OPTIONAL + } OPTIONAL, + nrdc-PC-mode-FR1-r16 ENUMERATED {semi-static-mode1, semi-static-mode2, dynamic} OPTIONAL, + nrdc-PC-mode-FR2-r16 ENUMERATED {semi-static-mode1, semi-static-mode2, dynamic} OPTIONAL, + maxMeasSRS-ResourceSCG-r16 INTEGER(0..maxNrofCLI-SRS-Resources-r16) OPTIONAL, + maxMeasCLI-ResourceSCG-r16 INTEGER(0..maxNrofCLI-RSSI-Resources-r16) OPTIONAL, + maxNumberEHC-ContextsSN-r16 INTEGER(0..65536) OPTIONAL, + allowedReducedConfigForOverheating-r16 OverheatingAssistance OPTIONAL, + maxToffset-r16 T-Offset-r16 OPTIONAL + ]] +} + +SelectedBandEntriesMN ::= SEQUENCE (SIZE (1..maxSimultaneousBands)) OF BandEntryIndex + +BandEntryIndex ::= INTEGER (0.. maxNrofServingCells) + +PH-TypeListMCG ::= SEQUENCE (SIZE (1..maxNrofServingCells)) OF PH-InfoMCG + +PH-InfoMCG ::= SEQUENCE { + servCellIndex ServCellIndex, + ph-Uplink PH-UplinkCarrierMCG, + ph-SupplementaryUplink PH-UplinkCarrierMCG OPTIONAL, + ... +} + +PH-UplinkCarrierMCG ::= SEQUENCE{ + ph-Type1or3 ENUMERATED {type1, type3}, + ... +} + +BandCombinationInfoList ::= SEQUENCE (SIZE (1..maxBandComb)) OF BandCombinationInfo + +BandCombinationInfo ::= SEQUENCE { + bandCombinationIndex BandCombinationIndex, + allowedFeatureSetsList SEQUENCE (SIZE (1..maxFeatureSetsPerBand)) OF FeatureSetEntryIndex +} + +FeatureSetEntryIndex ::= INTEGER (1.. maxFeatureSetsPerBand) + +DRX-Info ::= SEQUENCE { + drx-LongCycleStartOffset CHOICE { + ms10 INTEGER(0..9), + ms20 INTEGER(0..19), + ms32 INTEGER(0..31), + ms40 INTEGER(0..39), + ms60 INTEGER(0..59), + ms64 INTEGER(0..63), + ms70 INTEGER(0..69), + ms80 INTEGER(0..79), + ms128 INTEGER(0..127), + ms160 INTEGER(0..159), + ms256 INTEGER(0..255), + ms320 INTEGER(0..319), + ms512 INTEGER(0..511), + ms640 INTEGER(0..639), + ms1024 INTEGER(0..1023), + ms1280 INTEGER(0..1279), + ms2048 INTEGER(0..2047), + ms2560 INTEGER(0..2559), + ms5120 INTEGER(0..5119), + ms10240 INTEGER(0..10239) + }, + shortDRX SEQUENCE { + drx-ShortCycle ENUMERATED { + ms2, ms3, ms4, ms5, ms6, ms7, ms8, ms10, ms14, ms16, ms20, ms30, ms32, + ms35, ms40, ms64, ms80, ms128, ms160, ms256, ms320, ms512, ms640, spare9, + spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 }, + drx-ShortCycleTimer INTEGER (1..16) + } OPTIONAL +} + +DRX-Info2 ::= SEQUENCE { + drx-onDurationTimer CHOICE { + subMilliSeconds INTEGER (1..31), + milliSeconds ENUMERATED { + ms1, ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, ms40, ms50, ms60, + ms80, ms100, ms200, ms300, ms400, ms500, ms600, ms800, ms1000, ms1200, + ms1600, spare8, spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + } +} + +MeasConfigMN ::= SEQUENCE { + measuredFrequenciesMN SEQUENCE (SIZE (1..maxMeasFreqsMN)) OF NR-FreqInfo OPTIONAL, + measGapConfig SetupRelease { GapConfig } OPTIONAL, + gapPurpose ENUMERATED {perUE, perFR1} OPTIONAL, + ..., + [[ + measGapConfigFR2 SetupRelease { GapConfig } OPTIONAL + ]] + +} + +MRDC-AssistanceInfo ::= SEQUENCE { + affectedCarrierFreqCombInfoListMRDC SEQUENCE (SIZE (1..maxNrofCombIDC)) OF AffectedCarrierFreqCombInfoMRDC, + ..., + [[ + overheatingAssistanceSCG-r16 OCTET STRING (CONTAINING OverheatingAssistance) OPTIONAL + ]] +} + +AffectedCarrierFreqCombInfoMRDC ::= SEQUENCE { + victimSystemType VictimSystemType, + interferenceDirectionMRDC ENUMERATED {eutra-nr, nr, other, utra-nr-other, nr-other, spare3, spare2, spare1}, + affectedCarrierFreqCombMRDC SEQUENCE { + affectedCarrierFreqCombEUTRA AffectedCarrierFreqCombEUTRA OPTIONAL, + affectedCarrierFreqCombNR AffectedCarrierFreqCombNR + } OPTIONAL +} + +VictimSystemType ::= SEQUENCE { + gps ENUMERATED {true} OPTIONAL, + glonass ENUMERATED {true} OPTIONAL, + bds ENUMERATED {true} OPTIONAL, + galileo ENUMERATED {true} OPTIONAL, + wlan ENUMERATED {true} OPTIONAL, + bluetooth ENUMERATED {true} OPTIONAL +} + +AffectedCarrierFreqCombEUTRA ::= SEQUENCE (SIZE (1..maxNrofServingCellsEUTRA)) OF ARFCN-ValueEUTRA + +AffectedCarrierFreqCombNR ::= SEQUENCE (SIZE (1..maxNrofServingCells)) OF ARFCN-ValueNR + +-- TAG-CG-CONFIG-INFO-STOP +-- TAG-MEASUREMENT-TIMING-CONFIGURATION-START + +MeasurementTimingConfiguration ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE{ + measTimingConf MeasurementTimingConfiguration-IEs, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +MeasurementTimingConfiguration-IEs ::= SEQUENCE { + measTiming MeasTimingList OPTIONAL, + nonCriticalExtension MeasurementTimingConfiguration-v1550-IEs OPTIONAL +} + +MeasurementTimingConfiguration-v1550-IEs ::= SEQUENCE { + campOnFirstSSB BOOLEAN, + psCellOnlyOnFirstSSB BOOLEAN, + nonCriticalExtension MeasurementTimingConfiguration-v1610-IEs OPTIONAL +} + +MeasurementTimingConfiguration-v1610-IEs ::= SEQUENCE { + csi-RS-Config-r16 SEQUENCE { + csi-RS-SubcarrierSpacing-r16 SubcarrierSpacing, + csi-RS-CellMobility-r16 CSI-RS-CellMobility, + refSSBFreq-r16 ARFCN-ValueNR + }, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +MeasTimingList ::= SEQUENCE (SIZE (1..maxMeasFreqsMN)) OF MeasTiming + +MeasTiming ::= SEQUENCE { + frequencyAndTiming SEQUENCE { + carrierFreq ARFCN-ValueNR, + ssbSubcarrierSpacing SubcarrierSpacing, + ssb-MeasurementTimingConfiguration SSB-MTC, + ss-RSSI-Measurement SS-RSSI-Measurement OPTIONAL + } OPTIONAL, + ..., + [[ + ssb-ToMeasure SSB-ToMeasure OPTIONAL, + physCellId PhysCellId OPTIONAL + ]] +} + +-- TAG-MEASUREMENT-TIMING-CONFIGURATION-STOP +-- TAG-UE-RADIO-PAGING-INFORMATION-START + +UERadioPagingInformation ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE{ + ueRadioPagingInformation UERadioPagingInformation-IEs, + spare7 NULL, + spare6 NULL, spare5 NULL, spare4 NULL, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +UERadioPagingInformation-IEs ::= SEQUENCE { + supportedBandListNRForPaging SEQUENCE (SIZE (1..maxBands)) OF FreqBandIndicatorNR OPTIONAL, + nonCriticalExtension SEQUENCE {} OPTIONAL +} + + +-- TAG-UE-RADIO-PAGING-INFORMATION-STOP +-- TAG-UE-RADIO-ACCESS-CAPABILITY-INFORMATION-START + +UERadioAccessCapabilityInformation ::= SEQUENCE { + criticalExtensions CHOICE { + c1 CHOICE{ + ueRadioAccessCapabilityInformation UERadioAccessCapabilityInformation-IEs, + spare7 NULL, + spare6 NULL, spare5 NULL, spare4 NULL, + spare3 NULL, spare2 NULL, spare1 NULL + }, + criticalExtensionsFuture SEQUENCE {} + } +} + +UERadioAccessCapabilityInformation-IEs ::= SEQUENCE { + ue-RadioAccessCapabilityInfo OCTET STRING (CONTAINING UE-CapabilityRAT-ContainerList), + nonCriticalExtension SEQUENCE {} OPTIONAL +} + +-- TAG-UE-RADIO-ACCESS-CAPABILITY-INFORMATION-STOP +-- TAG-NR-MULTIPLICITY-AND-CONSTRAINTS-START + +maxMeasFreqsMN INTEGER ::= 32 -- Maximum number of MN-configured measurement frequencies +maxMeasFreqsSN INTEGER ::= 32 -- Maximum number of SN-configured measurement frequencies +maxMeasIdentitiesMN INTEGER ::= 62 -- Maximum number of measurement identities that a UE can be configured with +maxCellPrep INTEGER ::= 32 -- Maximum number of cells prepared for handover + +-- TAG-NR-MULTIPLICITY-AND-CONSTRAINTS-STOP +-- TAG-NR-INTER-NODE-DEFINITIONS-END-START + +END + +-- TAG-NR-INTER-NODE-DEFINITIONS-END-STOP diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 420238e751..b20b014b45 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -1382,7 +1382,7 @@ if (!servingcellconfigdedicated) { secondaryCellGroup->spCellConfig->spCellConfigDedicated->sCellDeactivationTimer=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->crossCarrierSchedulingConfig=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->tag_Id=0; - secondaryCellGroup->spCellConfig->spCellConfigDedicated->dummy=NULL; + secondaryCellGroup->spCellConfig->spCellConfigDedicated->dummy1=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->pathlossReferenceLinking=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->servingCellMO=NULL; diff --git a/targets/ARCH/rfsimulator/apply_channelmod.c b/targets/ARCH/rfsimulator/apply_channelmod.c index ab8ae77edd..d7d3a68965 100644 --- a/targets/ARCH/rfsimulator/apply_channelmod.c +++ b/targets/ARCH/rfsimulator/apply_channelmod.c @@ -74,10 +74,10 @@ void rxAddInput( struct complex16 *input_sig, struct complex16 *after_channel_si for (int i=0; i<((int)nbSamples-dd); i++) { struct complex16 *out_ptr=after_channel_sig+dd+i; - struct complex rx_tmp= {0}; + struct complexd rx_tmp= {0}; for (int txAnt=0; txAnt < nbTx; txAnt++) { - const struct complex *channelModel= channelDesc->ch[rxAnt+(txAnt*channelDesc->nb_rx)]; + const struct complexd *channelModel= channelDesc->ch[rxAnt+(txAnt*channelDesc->nb_rx)]; //const struct complex *channelModelEnd=channelModel+channelDesc->channel_length; for (int l = 0; l<(int)channelDesc->channel_length; l++) { @@ -88,14 +88,14 @@ void rxAddInput( struct complex16 *input_sig, struct complex16 *after_channel_si // it would be better to split out each antenna in a separate flow // that will allow to mix ru antennas freely struct complex16 tx16=input_sig[((TS+i-l)*nbTx+txAnt)%CirSize]; - rx_tmp.x += tx16.r * channelModel[l].x - tx16.i * channelModel[l].y; - rx_tmp.y += tx16.i * channelModel[l].x + tx16.r * channelModel[l].y; + rx_tmp.r += tx16.r * channelModel[l].r - tx16.i * channelModel[l].i; + rx_tmp.i += tx16.i * channelModel[l].r + tx16.r * channelModel[l].i; } //l } // Fixme: lround(), rount(), ... is detected by valgrind as error, not found why - out_ptr->r += lround(rx_tmp.x*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); - out_ptr->i += lround(rx_tmp.y*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); + out_ptr->r += lround(rx_tmp.r*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); + out_ptr->i += lround(rx_tmp.i*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); out_ptr++; } diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpn300.conf index ef171bf0a9..0fd0dd4551 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpn300.conf @@ -234,8 +234,10 @@ MACRLCs = ( L1s = ( { - num_cc = 1; - tr_n_preference = "local_mac"; + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpx300.conf index 360b75c67d..2c97e2cad3 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.32PRB.usrpx300.conf @@ -218,8 +218,10 @@ MACRLCs = ( L1s = ( { - num_cc = 1; - tr_n_preference = "local_mac"; + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.66PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.66PRB.usrpn300.conf index e018aef006..3ec84d34ce 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.66PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band257.tm1.66PRB.usrpn300.conf @@ -216,8 +216,10 @@ MACRLCs = ( L1s = ( { - num_cc = 1; - tr_n_preference = "local_mac"; + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band261.tm1.32PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band261.tm1.32PRB.usrpn300.conf index 834060b071..90ae80fb8d 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band261.tm1.32PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band261.tm1.32PRB.usrpn300.conf @@ -235,6 +235,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpn300.conf index 2028f40552..cd57096e00 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpn300.conf @@ -219,6 +219,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpx300.conf index 35c4924ba1..5230fa91ef 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band66.tm1.106PRB.usrpx300.conf @@ -219,6 +219,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.106PRB.slave.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.106PRB.slave.conf index aa61153c93..51b252e1a4 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.106PRB.slave.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.106PRB.slave.conf @@ -231,8 +231,10 @@ MACRLCs = ( L1s = ( { - num_cc = 1; - tr_n_preference = "local_mac"; + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.PTRS.usrpx300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.PTRS.usrpx300.conf index d08234ab16..5530f33d65 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.PTRS.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.PTRS.usrpx300.conf @@ -269,6 +269,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpb210.conf index f22d5f781f..1cd08ec115 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpb210.conf @@ -215,8 +215,10 @@ MACRLCs = ( L1s = ( { - num_cc = 1; - tr_n_preference = "local_mac"; + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf index b57cb00779..8a5fbb740f 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf @@ -218,6 +218,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpx300.conf index 862185835e..705fa277e0 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpx300.conf @@ -219,6 +219,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpn300.conf index 335c3d9f0b..7d8b80f83c 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpn300.conf @@ -230,11 +230,12 @@ MACRLCs = ( ); L1s = ( - { - num_cc = 1; - tr_n_preference = "local_mac"; - pusch_proc_threads = 8; - } + { + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 + } ); RUs = ( diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpx300.conf index bf75edc360..a1a4972470 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.217PRB.usrpx300.conf @@ -214,11 +214,12 @@ MACRLCs = ( ); L1s = ( - { - num_cc = 1; - tr_n_preference = "local_mac"; - pusch_proc_threads = 8; - } + { + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 + } ); RUs = ( diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpb210.conf index b4f40d2205..44d238bce2 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpb210.conf @@ -219,6 +219,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpn300.conf index 639b11828b..a4df8399ea 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpn300.conf @@ -219,6 +219,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpx300.conf index b93b530bed..2899f6982d 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.24PRB.usrpx300.conf @@ -219,6 +219,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.273PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.273PRB.usrpn300.conf index 50276c3289..2b646fcacd 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.273PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.273PRB.usrpn300.conf @@ -214,11 +214,12 @@ MACRLCs = ( ); L1s = ( - { - num_cc = 1; - tr_n_preference = "local_mac"; - pusch_proc_threads = 8; - } + { + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 + } ); RUs = ( diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/oaiL1.nfapi.usrpx300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/oaiL1.nfapi.usrpx300.conf index 5cfd4dc768..06ae5af066 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/oaiL1.nfapi.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/oaiL1.nfapi.usrpx300.conf @@ -28,6 +28,7 @@ L1s = ( remote_n_portc = 50001; // vnf p5 port local_n_portd = 50010; // pnf p7 port remote_n_portd = 50011; // vnf p7 port + pusch_proc_threads = 8; } ); @@ -42,5 +43,7 @@ RUs = ( max_pdschReferenceSignalPower = -27; max_rxgain = 114; sdr_addrs = "type=x300"; // USRP type + clock_src = "external"; + time_src = "external"; } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf index 7d21dce681..95b05f320b 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf @@ -235,6 +235,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb_24PRB.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb_24PRB.conf index 463b7d6d06..f7546cde79 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb_24PRB.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb_24PRB.conf @@ -251,6 +251,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf index 1a28ebb950..e573d3499d 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf @@ -240,6 +240,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index 718038657b..0cac691e74 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -240,6 +240,7 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; pusch_proc_threads = 8; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 } ); From 503eba46cea5dd074eb49d389e6a401832d6191b Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 10 Sep 2021 13:39:50 +0200 Subject: [PATCH 036/233] fix compilation issue --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 6b8f06d922..7c0e2c8906 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1301,10 +1301,6 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, // reset TA flag ul_time_alignment->apply_ta = 0; LOG_D(PHY,"Frame %d slot %d -- Starting UL time alignment procedures. TA update will be applied at frame %d slot %d\n", frame_rx, nr_slot_rx, ul_time_alignment->ta_frame, ul_time_alignment->ta_slot); - } - // reset TA flag - ul_time_alignment->apply_ta = 0; - LOG_D(PHY,"Frame %d slot %d -- Starting UL time alignment procedures. TA update will be applied at frame %d slot %d\n", frame_rx, nr_slot_rx, ul_time_alignment->ta_frame, ul_time_alignment->ta_slot); } } } From 1e6c36d8770911ef8ab8ff23d066c0f13c771e9d Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 1 Nov 2021 12:03:25 +0100 Subject: [PATCH 037/233] fix segfault in SA rfsim --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 9d9e456c14..ad98198908 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -496,14 +496,18 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format NR_ServingCellConfig_t *scd = mac->cg->spCellConfig->spCellConfigDedicated; - if (bwp_ind && dci_format){ + int n_ubwp = 0; + if (scd && scd->uplinkConfig && + scd->uplinkConfig->uplinkBWP_ToAddModList) + n_ubwp = scd->uplinkConfig->uplinkBWP_ToAddModList->list.count; + if (bwp_ind && dci_format){ switch(*dci_format){ case NR_UL_DCI_FORMAT_0_1: - mac->UL_BWP_Id = *bwp_ind +1 ; + mac->UL_BWP_Id = n_ubwp < 4 ? *bwp_ind : *bwp_ind + 1;; break; case NR_DL_DCI_FORMAT_1_1: - mac->DL_BWP_Id = *bwp_ind +1; + mac->DL_BWP_Id = n_ubwp < 4 ? *bwp_ind : *bwp_ind + 1;; break; default: LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); From f1992a58f8ddef53a99ff1062ac3c489524733c0 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 1 Nov 2021 18:14:39 +0100 Subject: [PATCH 038/233] fixes in bwp ss and coreset configuration at ue --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 110 +++++++++++------- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 2 +- openair2/LAYER2/NR_MAC_UE/mac_proto.h | 4 + openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 10 +- .../NR_MAC_UE/nr_ue_dci_configuration.c | 20 +--- 5 files changed, 84 insertions(+), 62 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index ad98198908..c6f31e97e3 100755 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -512,7 +512,10 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format default: LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); } - + // configure ss coreset after switching BWP + configure_ss_coreset(mac, + scd, + mac->DL_BWP_Id); } else { if (scd->firstActiveDownlinkBWP_Id) @@ -540,82 +543,103 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format */ void config_control_ue(NR_UE_MAC_INST_t *mac){ - uint8_t coreset_id = 1, ss_id; - - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + int bwp_id; NR_ServingCellConfig_t *scd = mac->cg->spCellConfig->spCellConfigDedicated; - if (dl_bwp_id==0) AssertFatal(mac->scc_SIB,"dl_bwp_id 0 (DL %d,UL %d) means mac->scc_SIB should exist here!\n",(int)mac->DL_BWP_Id,(int)mac->UL_BWP_Id); - NR_BWP_DownlinkCommon_t *bwp_Common = dl_bwp_id>0 ? scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Common : - &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; - - if (dl_bwp_id > 0 ) { - AssertFatal(scd->downlinkBWP_ToAddModList != NULL, "downlinkBWP_ToAddModList is null\n"); - AssertFatal(scd->downlinkBWP_ToAddModList->list.count == 1, "downlinkBWP_ToAddModList->list->count is %d\n", scd->downlinkBWP_ToAddModList->list.count); - } - NR_BWP_DownlinkDedicated_t *dl_bwp_Dedicated = dl_bwp_id>0 ? scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Dedicated: - scd->initialDownlinkBWP; - AssertFatal(dl_bwp_Dedicated != NULL, "dl_bwp_Dedicated is null\n"); config_bwp_ue(mac, NULL, NULL); + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; - NR_SetupRelease_PDCCH_Config_t *pdcch_Config = dl_bwp_Dedicated->pdcch_Config; - AssertFatal(pdcch_Config != NULL, "pdcch_Config is null\n"); + // configure DLbwp + if (scd->downlinkBWP_ToAddModList) { + for (int i = 0; i < scd->downlinkBWP_ToAddModList->list.count; i++) { + bwp_id = scd->downlinkBWP_ToAddModList->list.array[i]->bwp_Id; + mac->DLbwp[bwp_id-1] = scd->downlinkBWP_ToAddModList->list.array[i]; + } + } + + // configure ULbwp + if (scd->uplinkConfig->uplinkBWP_ToAddModList) { + for (int i = 0; i < scd->uplinkConfig->uplinkBWP_ToAddModList->list.count; i++) { + bwp_id = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[i]->bwp_Id; + mac->ULbwp[bwp_id-1] = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[i]; + } + } + + configure_ss_coreset(mac, scd, dl_bwp_id); +} + + +void configure_ss_coreset(NR_UE_MAC_INST_t *mac, + NR_ServingCellConfig_t *scd, + NR_BWP_Id_t dl_bwp_id) { + + + NR_BWP_DownlinkCommon_t *bwp_Common; + if (dl_bwp_id>0) + bwp_Common = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Common; + else { + if (mac->scc_SIB) + bwp_Common = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; + else + bwp_Common = mac->scc->downlinkConfigCommon->initialDownlinkBWP; + } NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; AssertFatal(pdcch_ConfigCommon != NULL, "pdcch_ConfigCommon is null\n"); AssertFatal(pdcch_ConfigCommon->choice.setup->ra_SearchSpace != NULL, "ra_SearchSpace must be available in DL BWP\n"); - struct NR_PDCCH_ConfigCommon__commonSearchSpaceList *commonSearchSpaceList = pdcch_ConfigCommon->choice.setup->commonSearchSpaceList; - AssertFatal(commonSearchSpaceList != NULL, "commonSearchSpaceList is null\n"); - AssertFatal(commonSearchSpaceList->list.count > 0, "PDCCH CSS list has 0 elements\n"); + // configuring eventual common coreset + NR_ControlResourceSet_t *coreset = pdcch_ConfigCommon->choice.setup->commonControlResourceSet; + if (coreset) + mac->coreset[dl_bwp_id][coreset->controlResourceSetId - 1] = coreset; + + NR_BWP_DownlinkDedicated_t *dl_bwp_Dedicated = dl_bwp_id>0 ? scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Dedicated: + scd->initialDownlinkBWP; + + AssertFatal(dl_bwp_Dedicated != NULL, "dl_bwp_Dedicated is null\n"); + + NR_SetupRelease_PDCCH_Config_t *pdcch_Config = dl_bwp_Dedicated->pdcch_Config; + AssertFatal(pdcch_Config != NULL, "pdcch_Config is null\n"); struct NR_PDCCH_Config__controlResourceSetToAddModList *controlResourceSetToAddModList = pdcch_Config->choice.setup->controlResourceSetToAddModList; AssertFatal(controlResourceSetToAddModList != NULL, "controlResourceSetToAddModList is null\n"); - AssertFatal(controlResourceSetToAddModList->list.count == 1, "controlResourceSetToAddModList->list.count=%d\n", controlResourceSetToAddModList->list.count); - AssertFatal(controlResourceSetToAddModList->list.array[0] != NULL, "coreset[0][0] is null\n"); + + // configuring dedicated coreset + // In case network reconfigures control resource set with the same ControlResourceSetId as used for commonControlResourceSet configured via PDCCH-ConfigCommon, + // the configuration from PDCCH-Config always takes precedence + for (int i=0; ilist.count; i++) { + coreset = controlResourceSetToAddModList->list.array[i]; + mac->coreset[dl_bwp_id][coreset->controlResourceSetId - 1] = coreset; + } struct NR_PDCCH_Config__searchSpacesToAddModList *searchSpacesToAddModList = pdcch_Config->choice.setup->searchSpacesToAddModList; AssertFatal(searchSpacesToAddModList != NULL, "searchSpacesToAddModList is null\n"); AssertFatal(searchSpacesToAddModList->list.count > 0, "list of UE specifically configured Search Spaces is empty\n"); AssertFatal(searchSpacesToAddModList->list.count < FAPI_NR_MAX_SS_PER_CORESET, "too many searchpaces per coreset %d\n", searchSpacesToAddModList->list.count); - struct NR_UplinkConfig__uplinkBWP_ToAddModList *uplinkBWP_ToAddModList = scd->uplinkConfig->uplinkBWP_ToAddModList; - if (ul_bwp_id > 0) { - AssertFatal(uplinkBWP_ToAddModList != NULL, "uplinkBWP_ToAddModList is null\n"); - AssertFatal(uplinkBWP_ToAddModList->list.count == 1, "uplinkBWP_ToAddModList->list->count is %d\n", uplinkBWP_ToAddModList->list.count); - } - // check pdcch_Config, pdcch_ConfigCommon and DL BWP - mac->DLbwp[0] = dl_bwp_id>0?scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]:NULL; - mac->coreset[dl_bwp_id][coreset_id - 1] = controlResourceSetToAddModList->list.array[0]; - - // Check dedicated UL BWP and pass to MAC - mac->ULbwp[ul_bwp_id] = ul_bwp_id>0?uplinkBWP_ToAddModList->list.array[0]:NULL; - if (mac->ULbwp[ul_bwp_id]) AssertFatal(mac->ULbwp[ul_bwp_id]->bwp_Dedicated != NULL, "UL bwp_Dedicated is null\n"); - // check available Search Spaces in the searchSpacesToAddModList and pass to MAC // note: the network configures at most 10 Search Spaces per BWP per cell (including UE-specific and common Search Spaces). - for (ss_id = 0; ss_id < searchSpacesToAddModList->list.count; ss_id++) { + for (int ss_id = 0; ss_id < searchSpacesToAddModList->list.count; ss_id++) { NR_SearchSpace_t *ss = searchSpacesToAddModList->list.array[ss_id]; AssertFatal(ss->controlResourceSetId != NULL, "ss->controlResourceSetId is null\n"); AssertFatal(ss->searchSpaceType != NULL, "ss->searchSpaceType is null\n"); - AssertFatal(*ss->controlResourceSetId == mac->coreset[dl_bwp_id][coreset_id - 1]->controlResourceSetId, "ss->controlResourceSetId is unknown\n"); AssertFatal(ss->monitoringSymbolsWithinSlot != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot->buf != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id][0][ss_id] = ss; + mac->SSpace[dl_bwp_id][ss->searchSpaceId - 1] = ss; } + struct NR_PDCCH_ConfigCommon__commonSearchSpaceList *commonSearchSpaceList = pdcch_ConfigCommon->choice.setup->commonSearchSpaceList; + AssertFatal(commonSearchSpaceList != NULL, "commonSearchSpaceList is null\n"); + AssertFatal(commonSearchSpaceList->list.count > 0, "PDCCH CSS list has 0 elements\n"); + // Check available CSSs in the commonSearchSpaceList (list of additional common search spaces) // note: commonSearchSpaceList SIZE(1..4) for (int css_id = 0; css_id < commonSearchSpaceList->list.count; css_id++) { NR_SearchSpace_t *css = commonSearchSpaceList->list.array[css_id]; AssertFatal(css->controlResourceSetId != NULL, "ss->controlResourceSetId is null\n"); - AssertFatal(*css->controlResourceSetId == 0 || *css->controlResourceSetId == mac->coreset[dl_bwp_id][coreset_id - 1]->controlResourceSetId, "css->controlResourceSetId %ld is unknown, mac->coreset[%ld][%d]->controlResourceSetId %ld\n",*css->controlResourceSetId,dl_bwp_id,coreset_id-1,mac->coreset[dl_bwp_id][coreset_id - 1]->controlResourceSetId); - AssertFatal(css->searchSpaceType != NULL, "css->searchSpaceType is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot != NULL, "css->monitoringSymbolsWithinSlot is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot->buf != NULL, "css->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id][coreset_id -1][ss_id] = css; - ss_id++; + mac->SSpace[dl_bwp_id][css->searchSpaceId - 1] = css; } } diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 062928c306..c46331f8ae 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -375,7 +375,7 @@ typedef struct { NR_BWP_Downlink_t *DLbwp[MAX_NUM_BWP_UE]; NR_BWP_Uplink_t *ULbwp[MAX_NUM_BWP_UE]; NR_ControlResourceSet_t *coreset[MAX_NUM_BWP_UE][FAPI_NR_MAX_CORESET_PER_BWP]; - NR_SearchSpace_t *SSpace[MAX_NUM_BWP_UE][FAPI_NR_MAX_CORESET_PER_BWP][FAPI_NR_MAX_SS_PER_CORESET]; + NR_SearchSpace_t *SSpace[MAX_NUM_BWP_UE][FAPI_NR_MAX_SS_PER_CORESET]; lte_frame_type_t frame_type; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index a078e385dc..10ab6252fd 100755 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -377,6 +377,10 @@ void build_ssb_to_ro_map(NR_UE_MAC_INST_t *mac); void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format); +void configure_ss_coreset(NR_UE_MAC_INST_t *mac, + NR_ServingCellConfig_t *scd, + NR_BWP_Id_t dl_bwp_id); + fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot); void fill_ul_config(fapi_nr_ul_config_request_t *ul_config, frame_t frame_tx, int slot_tx, uint8_t pdu_type); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index c3c195a64a..c81e5ffa83 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -94,10 +94,12 @@ void init_RA(module_id_t mod_id, } } if (ss_id < 0) { - ra_ss = mac->DLbwp[0]->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace; - if (ra_ss) { - commonSearchSpaceList = mac->DLbwp[0]->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList; - ss_id = *mac->DLbwp[0]->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace; + if (mac->DL_BWP_Id>0) { + ra_ss = mac->DLbwp[mac->DL_BWP_Id-1]->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace; + if (ra_ss) { + commonSearchSpaceList = mac->DLbwp[mac->DL_BWP_Id-1]->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList; + ss_id = *mac->DLbwp[mac->DL_BWP_Id-1]->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace; + } } } } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index ff6d9e10d3..209ed8c513 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -97,7 +97,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t NR_SearchSpace_t *ss; NR_ControlResourceSet_t *coreset; if(ss_id>=0) { - ss = mac->SSpace[dl_bwp_id][coreset_id - 1][ss_id]; + ss = mac->SSpace[dl_bwp_id][ss_id-1]; coreset = mac->coreset[dl_bwp_id][coreset_id - 1]; rel15->coreset.CoreSetType = NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG; } else { @@ -255,18 +255,19 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl RA_config_t *ra = &mac->ra; int ss_id; - uint8_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0, coreset_id = (mac->cg) ? 1 : 0; + uint8_t bwp_id = (mac->cg) ? mac->DL_BWP_Id : 0; //NR_ServingCellConfig_t *scd = mac->scg->spCellConfig->spCellConfigDedicated; NR_BWP_DownlinkDedicated_t *bwpd = (bwp_id>0) ? mac->DLbwp[bwp_id-1]->bwp_Dedicated : mac->cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP; NR_BWP_DownlinkCommon_t *bwp_Common = (bwp_id>0) ? mac->DLbwp[bwp_id-1]->bwp_Common : &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; LOG_D(NR_MAC, "[DCI_CONFIG] ra_rnti %p (%x) crnti %p (%x) t_crnti %p (%x)\n", &ra->ra_rnti, ra->ra_rnti, &mac->crnti, mac->crnti, &ra->t_crnti, ra->t_crnti); - // loop over all available SS for CORESET ID 1 + // loop over all available SS for bwp_id if (bwpd) { - for (ss_id = 0; ss_id < FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id][coreset_id - 1][ss_id] != NULL; ss_id++){ + for (ss_id = 1; ss_id <= FAPI_NR_MAX_SS_PER_CORESET && mac->SSpace[bwp_id][ss_id-1] != NULL; ss_id++){ LOG_D(NR_MAC, "[DCI_CONFIG] ss_id %d\n",ss_id); - NR_SearchSpace_t *ss = mac->SSpace[bwp_id][coreset_id - 1][ss_id]; + NR_SearchSpace_t *ss = mac->SSpace[bwp_id][ss_id-1]; + AssertFatal(ss_id == ss->searchSpaceId,"SS IDs don't correspond\n"); fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; struct NR_PhysicalCellGroupConfig *phy_cgc = mac->cg->physicalCellGroupConfig; @@ -274,15 +275,6 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl case NR_SearchSpace__searchSpaceType_PR_common: // this is for CSSs, we use BWP common and pdcch_ConfigCommon - // Fetch configuration for searchSpaceZero - // note: The search space with the SearchSpaceId = 0 identifies the search space configured via PBCH (MIB) and in ServingCellConfigCommon (searchSpaceZero). - if (pdcch_ConfigCommon->choice.setup->searchSpaceZero){ - if (pdcch_ConfigCommon->choice.setup->searchSpaceSIB1 == NULL){ - pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=calloc(1,sizeof(*pdcch_ConfigCommon->choice.setup->searchSpaceSIB1)); - } - *pdcch_ConfigCommon->choice.setup->searchSpaceSIB1 = 0; - LOG_D(NR_MAC, "[DCI_CONFIG] Configure SearchSpace#0 of the initial BWP\n"); - } if (ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0){ // check available SS IDs if (pdcch_ConfigCommon->choice.setup->ra_SearchSpace){ From 5bfd50791587654c4de6c245e3fa843d1bc2e980 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 4 Nov 2021 09:19:29 +0100 Subject: [PATCH 039/233] some more fixes for RA at gNB --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 44 ++++++++++--------- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 6 ++- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 9bee80f7bc..2b8e93383f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -542,13 +542,19 @@ void nr_initiate_ra_proc(module_id_t module_idP, ra_rnti = 1 + symbol + (slotP * 14) + (freq_index * 14 * 80) + (ul_carrier_id * 14 * 80 * 8); // This should be handled differently when we use the initialBWP for RA - ra->bwp_id = 0;//TODO + ra->dl_bwp_id = 0;//TODO + ra->ul_bwp_id = 0; NR_BWP_Downlink_t *bwp=NULL; - if (ra->CellGroup && ra->CellGroup->spCellConfig && ra->CellGroup->spCellConfig->spCellConfigDedicated && + if (ra->CellGroup && ra->CellGroup->spCellConfig && ra->CellGroup->spCellConfig->spCellConfigDedicated) { + if (ra->CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id && ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { - ra->bwp_id = 1; - bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; - } + ra->dl_bwp_id = *ra->CellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id; + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->dl_bwp_id - 1]; + } + if (ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig && + ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id) + ra->ul_bwp_id = *ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id; + } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 1); @@ -698,7 +704,7 @@ void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL; NR_BWP_t *genericParameters = NULL; if(ra->CellGroup) { - ubwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + ubwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->ul_bwp_id-1]; ubwpd = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP; genericParameters = &ubwp->bwp_Common->genericParameters; pusch_TimeDomainAllocationList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; @@ -780,7 +786,7 @@ void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t nfapi_nr_ul_dci_request_t *ul_dci_req = &nr_mac->UL_dci_req[CC_id]; const int coresetid = coreset->controlResourceSetId; - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid]; + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][ra->ul_bwp_id][coresetid]; if (!pdcch_pdu_rel15) { nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus]; memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t)); @@ -789,7 +795,7 @@ void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t pdcch_pdu_rel15 = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15; ul_dci_req->numPdus += 1; nr_configure_pdcch(nr_mac, pdcch_pdu_rel15, ss, coreset, scc, genericParameters, NULL); - nr_mac->pdcch_pdu_idx[CC_id][ra->bwp_id][coresetid] = pdcch_pdu_rel15; + nr_mac->pdcch_pdu_idx[CC_id][ra->ul_bwp_id][coresetid] = pdcch_pdu_rel15; } uint8_t aggregation_level; @@ -829,7 +835,7 @@ void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t ra->Msg3_tda_id, ra->msg3_TPC, 0, // not used in format 0_0 - ra->bwp_id); + ra->ul_bwp_id); fill_dci_pdu_rel15(scc, ra->CellGroup, @@ -838,7 +844,7 @@ void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t NR_UL_DCI_FORMAT_0_0, NR_RNTI_TC, pusch_pdu->bwp_size, - ra->bwp_id); + ra->ul_bwp_id); // Mark the corresponding RBs as used for (int rb = 0; rb < ra->msg3_nb_rb; rb++) { @@ -923,9 +929,7 @@ void nr_get_Msg3alloc(module_id_t module_id, int bwpStart = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); if (ra->CellGroup) { - AssertFatal(ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1, - "downlinkBWP_ToAddModList has %d BWP!\n", ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); - NR_BWP_Uplink_t *ubwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; + NR_BWP_Uplink_t *ubwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->ul_bwp_id - 1]; int act_bwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); int act_bwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); if (!((bwpStart >= act_bwp_start) && ((bwpStart+bwpSize) <= (act_bwp_start+act_bwp_size)))) @@ -1064,9 +1068,7 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t int mappingtype = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->mappingType; if (ra->CellGroup) { - AssertFatal(ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count <= 4, - "downlinkBWP_ToAddModList has %d BWP!\n", ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count); - NR_BWP_Uplink_t *ubwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id - 1]; + NR_BWP_Uplink_t *ubwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->ul_bwp_id - 1]; startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->startSymbolAndLength; mappingtype = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->mappingType; @@ -1122,8 +1124,8 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra ra->CellGroup->spCellConfig && ra->CellGroup->spCellConfig->spCellConfigDedicated && ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && - ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]) { - bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->dl_bwp_id-1]) { + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->dl_bwp_id-1]; genericParameters = &bwp->bwp_Common->genericParameters; pdsch_TimeDomainAllocationList = bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; } @@ -1342,7 +1344,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra // Program UL processing for Msg3 NR_BWP_Uplink_t *ubwp = ra->CellGroup ? - ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id-1] : + ra->CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->dl_bwp_id-1] : NULL; nr_get_Msg3alloc(module_idP, CC_id, scc, ubwp, slotP, frameP, ra, nr_mac->tdd_beam_association); nr_add_msg3(module_idP, CC_id, frameP, slotP, ra, (uint8_t *) &tx_req->TLVs[0].value.direct[0]); @@ -1393,8 +1395,8 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra ra->CellGroup->spCellConfig && ra->CellGroup->spCellConfig->spCellConfigDedicated && ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && - ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]) { - bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id-1]; + ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->dl_bwp_id-1]) { + bwp = ra->CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->dl_bwp_id-1]; pdsch_TimeDomainAllocationList = bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; } else { diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index aabe816391..4aa017ad6a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -107,8 +107,10 @@ typedef struct NR_preamble_ue { typedef struct { /// Flag to indicate this process is active RA_gNB_state_t state; - /// BWP id of RA process - int bwp_id; + /// DL BWP id of RA process + int dl_bwp_id; + /// UL BWP id of RA process + int ul_bwp_id; /// CORESET0 configured flag int coreset0_configured; /// Slot where preamble was received From ee054770c46954677ad21149050d179d00f766f1 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 30 Nov 2021 13:49:34 +0000 Subject: [PATCH 040/233] Fix Segmentation Fault and avoid multiple calloc for the same pointers --- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- openair2/RRC/NR/rrc_gNB_reconfig.c | 48 +++++++++++-------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 57e34fd263..4eb52bc353 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1947,7 +1947,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG add_nr_list(&UE_info->list, UE_id); memset(&UE_info->mac_stats[UE_id], 0, sizeof(NR_mac_stats_t)); set_Y(UE_info->Y[UE_id], rntiP); - if (CellGroup && CellGroup->spCellConfig && CellGroup->spCellConfig && CellGroup->spCellConfig->spCellConfigDedicated) + if (CellGroup && CellGroup->spCellConfig && CellGroup->spCellConfig->spCellConfigDedicated) compute_csi_bitlen (CellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup, UE_info, UE_id, mod_idP); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; memset(sched_ctrl, 0, sizeof(*sched_ctrl)); diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index c263353acb..b52367b258 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -375,19 +375,21 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (servingcellconfigdedicated) { bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; } else { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); - - bwp=calloc(1,sizeof(*bwp)); - // copy common BWP size from initial BWP except for bandwdith - bwp->bwp_Id = bwp_loop +1; + if(secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList == NULL){ + secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); + } + bwp = calloc(1,sizeof(*bwp)); + bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); + bwp->bwp_Id = bwp_loop + 1; } - if(bwp->bwp_Common->genericParameters.subcarrierSpacing == 0 || bwp->bwp_Common->genericParameters.locationAndBandwidth == 0) - { - memcpy((void*)&bwp->bwp_Common->genericParameters, - &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, - sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + + if ( bwp->bwp_Common->genericParameters.subcarrierSpacing == 0 && bwp->bwp_Common->genericParameters.locationAndBandwidth == 0 ) { + // copy common BWP size from initial BWP except for bandwdith + memcpy((void*)&bwp->bwp_Common->genericParameters, + &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, + sizeof(bwp->bwp_Common->genericParameters)); + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); } bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); @@ -858,16 +860,22 @@ if (!servingcellconfigdedicated) { if (servingcellconfigdedicated) { ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; } else { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); + if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList == NULL) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); + } ubwp = calloc(1,sizeof(*ubwp)); + ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); + ubwp->bwp_Id = bwp_loop + 1; + } + + if ( ubwp->bwp_Common->genericParameters.subcarrierSpacing == 0 && ubwp->bwp_Common->genericParameters.locationAndBandwidth == 0 ) { + // copy bwp_Common from Initial UL BWP except for bandwidth + memcpy((void*)&ubwp->bwp_Common->genericParameters, + (void*)&servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters, + sizeof(servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters)); + ubwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + ubwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); } - ubwp->bwp_Id=bwp_loop+1; - ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); - // copy bwp_Common from Initial UL BWP except for bandwidth - memcpy((void*)&ubwp->bwp_Common->genericParameters, - (void*)&servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters, - sizeof(servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters)); - ubwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); ubwp->bwp_Common->rach_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon; ubwp->bwp_Common->pusch_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; From 6edd0bb2cacea0126db6e95cac1f6059d4bf3276 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 30 Nov 2021 16:32:18 +0000 Subject: [PATCH 041/233] Change LOG levels --- openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c | 12 ++++++------ openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index 37a2849d21..0894ae9166 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -383,7 +383,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, printf("[AbsSFN %u.%d] Slot%d Symbol %d type %d: Pilot/Data extraction %5.2f \n", frame,nr_slot_rx,slot,symbol,type,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #else - LOG_I(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d type %d: Pilot/Data extraction %5.2f \n", + LOG_D(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d type %d: Pilot/Data extraction %5.2f \n", frame,nr_slot_rx,slot,symbol,type,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #endif @@ -405,7 +405,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, #if DISABLE_LOG_X printf("[AbsSFN %u.%d] Slot%d Symbol %d: Channel Scale %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #else - LOG_I(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d: Channel Scale %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); + LOG_D(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d: Channel Scale %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #endif start_meas(&ue->generic_stat_bis[proc->thread_id][slot]); if (first_symbol_flag==1) { @@ -466,7 +466,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, #if DISABLE_LOG_X printf("[AbsSFN %u.%d] Slot%d Symbol %d first_symbol_flag %d: Channel Level %5.2f \n",frame,nr_slot_rx,slot,symbol,first_symbol_flag,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #else - LOG_I(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d first_symbol_flag %d: Channel Level %5.2f \n",frame,nr_slot_rx,slot,symbol,first_symbol_flag,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); + LOG_D(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d first_symbol_flag %d: Channel Level %5.2f \n",frame,nr_slot_rx,slot,symbol,first_symbol_flag,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #endif start_meas(&ue->generic_stat_bis[proc->thread_id][slot]); @@ -518,7 +518,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, #if DISABLE_LOG_X printf("[AbsSFN %u.%d] Slot%d Symbol %d log2_maxh %d channel_level %d: Channel Comp %5.2f \n", frame, nr_slot_rx, slot, symbol, pdsch_vars[gNB_id]->log2_maxh, proc->channel_level, ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #else - LOG_I(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d log2_maxh %d channel_level %d: Channel Comp %5.2f \n", frame, nr_slot_rx, slot, symbol, pdsch_vars[gNB_id]->log2_maxh, proc->channel_level, ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); + LOG_D(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d log2_maxh %d channel_level %d: Channel Comp %5.2f \n", frame, nr_slot_rx, slot, symbol, pdsch_vars[gNB_id]->log2_maxh, proc->channel_level, ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #endif start_meas(&ue->generic_stat_bis[proc->thread_id][slot]); @@ -577,7 +577,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, #if DISABLE_LOG_X printf("[AbsSFN %u.%d] Slot%d Symbol %d: Channel Combine %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #else - LOG_I(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d: Channel Combine %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); + LOG_D(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d: Channel Combine %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #endif start_meas(&ue->generic_stat_bis[proc->thread_id][slot]); /* Store the valid DL RE's */ @@ -660,7 +660,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, #if DISABLE_LOG_X printf("[AbsSFN %u.%d] Slot%d Symbol %d: LLR Computation %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #else - LOG_I(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d: LLR Computation %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); + LOG_D(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d: LLR Computation %5.2f \n",frame,nr_slot_rx,slot,symbol,ue->generic_stat_bis[proc->thread_id][slot].p_time/(cpuf*1000.0)); #endif // Please keep it: useful for debugging diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 1a75ae71d1..3d0307f1f2 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1048,9 +1048,9 @@ bool nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, printf("AbsSubframe %d.%d --> LDPC Decoding for CW0 %5.3f\n", frame_rx%1024, nr_slot_rx,(ue->dlsch_decoding_stats[proc->thread_id].p_time)/(cpuf*1000.0)); #else - LOG_I(PHY, " --> Unscrambling for CW0 %5.3f\n", + LOG_D(PHY, " --> Unscrambling for CW0 %5.3f\n", (ue->dlsch_unscrambling_stats.p_time)/(cpuf*1000.0)); - LOG_I(PHY, "AbsSubframe %d.%d --> LDPC Decoding for CW0 %5.3f\n", + LOG_D(PHY, "AbsSubframe %d.%d --> LDPC Decoding for CW0 %5.3f\n", frame_rx%1024, nr_slot_rx,(ue->dlsch_decoding_stats[proc->thread_id].p_time)/(cpuf*1000.0)); #endif From 8cc8ef123bfd512b6e55defb421fe6f3532ac75b Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 30 Nov 2021 16:38:16 +0000 Subject: [PATCH 042/233] Add ServingCellConfigDedicated to RRCReconfiguration message in NSA --- openair2/GNB_APP/RRC_nr_paramsvalues.h | 71 +++++++++++++++++--------- openair2/RRC/NR/nr_rrc_defs.h | 1 + openair2/RRC/NR/rrc_gNB.c | 1 + openair2/RRC/NR/rrc_gNB_nsa.c | 3 ++ 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index c1cb4afdd9..85af6f5aef 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -229,20 +229,33 @@ #define GNB_CONFIG_STRING_ULPTRSREOFFSET_0 "ul_ptrsReOffset_0" #define GNB_CONFIG_STRING_ULPTRSMAXPORTS_0 "ul_ptrsMaxPorts_0" #define GNB_CONFIG_STRING_ULPTRSPOWER_0 "ul_ptrsPower_0" -#define GNB_CONFIG_STRING_DLBWP1_ID "bwp-Id_1" -#define GNB_CONFIG_STRING_DLBWP2_ID "bwp-Id_2" -#define GNB_CONFIG_STRING_DLBWP3_ID "bwp-Id_3" -#define GNB_CONFIG_STRING_DLBWP4_ID "bwp-Id_4" + +#define GNB_CONFIG_STRING_DLBWP1_ID "dl_bwp-Id_1" +#define GNB_CONFIG_STRING_DLBWP2_ID "dl_bwp-Id_2" +#define GNB_CONFIG_STRING_DLBWP3_ID "dl_bwp-Id_3" +#define GNB_CONFIG_STRING_DLBWP4_ID "dl_bwp-Id_4" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP1 "dl_bwp1_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP2 "dl_bwp2_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP3 "dl_bwp3_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP4 "dl_bwp4_locationAndBandwidth" +#define GNB_CONFIG_STRING_SCS_DLBWP1 "dl_bwp1_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_DLBWP2 "dl_bwp2_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_DLBWP3 "dl_bwp3_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_DLBWP4 "dl_bwp4_subcarrierSpacing" #define GNB_CONFIG_STRING_FIRSTACTIVEDLBWP_ID "firstActiveDownlinkBWP-Id" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP1 "bwp1_locationAndBandwidth" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP2 "bwp2_locationAndBandwidth" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP3 "bwp3_locationAndBandwidth" -#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP4 "bwp4_locationAndBandwidth" -#define GNB_CONFIG_STRING_SCS_BWP1 "bwp1_subcarrierSpacing" -#define GNB_CONFIG_STRING_SCS_BWP2 "bwp2_subcarrierSpacing" -#define GNB_CONFIG_STRING_SCS_BWP3 "bwp3_subcarrierSpacing" -#define GNB_CONFIG_STRING_SCS_BWP4 "bwp4_subcarrierSpacing" #define GNB_CONFIG_STRING_DEFAULTDLBWP_ID "defaultDownlinkBWP-Id" +#define GNB_CONFIG_STRING_ULBWP1_ID "ul_bwp-Id_1" +#define GNB_CONFIG_STRING_ULBWP2_ID "ul_bwp-Id_2" +#define GNB_CONFIG_STRING_ULBWP3_ID "ul_bwp-Id_3" +#define GNB_CONFIG_STRING_ULBWP4_ID "ul_bwp-Id_4" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP1 "ul_bwp1_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP2 "ul_bwp2_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP3 "ul_bwp3_locationAndBandwidth" +#define GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP4 "ul_bwp4_locationAndBandwidth" +#define GNB_CONFIG_STRING_SCS_ULBWP1 "ul_bwp1_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_ULBWP2 "ul_bwp2_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_ULBWP3 "ul_bwp3_subcarrierSpacing" +#define GNB_CONFIG_STRING_SCS_ULBWP4 "ul_bwp4_subcarrierSpacing" #define GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID "firstActiveUplinkBWP-Id" @@ -457,18 +470,26 @@ {GNB_CONFIG_STRING_DLBWP2_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:2,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_DLBWP3_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Id,defint64val:3,TYPE_INT64,0}, \ {GNB_CONFIG_STRING_DLBWP4_ID,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Id,defint64val:4,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_SCS_BWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_SCS_BWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_SCS_BWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_SCS_BWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ -{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_BWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}} - - - - - +{GNB_CONFIG_STRING_SCS_DLBWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_DLBWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_DLBWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_DLBWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP1,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP2,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP3,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_DLBWP4,NULL,0,i64ptr:&scd->downlinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_ULBWP1_ID,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Id,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_ULBWP2_ID,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[1]->bwp_Id,defint64val:2,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_ULBWP3_ID,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[2]->bwp_Id,defint64val:3,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_ULBWP4_ID,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[3]->bwp_Id,defint64val:4,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_ULBWP1,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_ULBWP2,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_ULBWP3,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_SCS_ULBWP4,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.subcarrierSpacing,defint64val:1,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP1,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP2,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[1]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP3,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[2]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_LOCATIONANDBANDWIDTH_ULBWP4,NULL,0,i64ptr:&scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[3]->bwp_Common->genericParameters.locationAndBandwidth,defint64val:0,TYPE_INT64,0}, \ +{GNB_CONFIG_STRING_DEFAULTDLBWP_ID,NULL,0,i64ptr:scd->defaultDownlinkBWP_Id,defint64val:0,TYPE_INT64,0}} #endif diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index ad31e8cac7..6b67690bd1 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -451,6 +451,7 @@ typedef struct { int do_CSIRS; NR_BCCH_DL_SCH_Message_t *siblock1; NR_ServingCellConfigCommon_t *servingcellconfigcommon; + NR_ServingCellConfig_t *servingcellconfig; NR_PDCCH_ConfigSIB1_t *pdcch_ConfigSIB1; NR_CellGroupConfig_t *secondaryCellGroup[MAX_NR_RRC_UE_CONTEXTS]; NR_SRB_INFO SI; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 34e570070d..94c3f93b9b 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -327,6 +327,7 @@ char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigu rrc->initial_id2_ngap_ids = hashtable_create (NUMBER_OF_UE_MAX * 2, NULL, NULL); rrc->ngap_id2_ngap_ids = hashtable_create (NUMBER_OF_UE_MAX * 2, NULL, NULL); rrc->carrier.servingcellconfigcommon = configuration->scc; + rrc->carrier.servingcellconfig = configuration->scd; rrc->carrier.ssb_SubcarrierOffset = configuration->ssb_SubcarrierOffset; rrc->carrier.pdsch_AntennaPorts = configuration->pdsch_AntennaPorts; rrc->carrier.pusch_AntennaPorts = configuration->pusch_AntennaPorts; diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index f9fce48f41..faf1ac8867 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -124,6 +124,9 @@ void rrc_parse_ue_capabilities(gNB_RRC_INST *rrc, NR_UE_CapabilityRAT_ContainerL xer_fprint(stdout, &asn_DEF_NR_UE_MRDC_Capability, ue_context_p->ue_context.UE_Capability_MRDC); } + ue_context_p->ue_context.spCellConfig = calloc(1, sizeof(struct NR_SpCellConfig)); + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated = rrc->carrier.servingcellconfig; + LOG_I(NR_RRC,"Adding new NSA user (%p)\n",ue_context_p); rrc_add_nsa_user(rrc,ue_context_p, m); } From 1302b48b923e24b09680297a0ff6c8c3ae0f0c4f Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 1 Dec 2021 22:28:06 +0000 Subject: [PATCH 043/233] Temporary fix to avoid QUECTEL to reject the RRCReconfiguration in NSA mode For now, QUECTEL rejects the RRCReconfiguration with more than 1 BWP Dedicated, need to understand why... --- openair2/RRC/NR/rrc_gNB_nsa.c | 11 +++++++++++ openair2/RRC/NR/rrc_gNB_reconfig.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index faf1ac8867..a7f00a408c 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -126,6 +126,17 @@ void rrc_parse_ue_capabilities(gNB_RRC_INST *rrc, NR_UE_CapabilityRAT_ContainerL ue_context_p->ue_context.spCellConfig = calloc(1, sizeof(struct NR_SpCellConfig)); ue_context_p->ue_context.spCellConfig->spCellConfigDedicated = rrc->carrier.servingcellconfig; + + // TODO: to be removed! For now, QUECTEL rejects the RRCReconfiguration with more than 1 BWP Dedicated, need to understand why... + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[1] = NULL; + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[2] = NULL; + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[3] = NULL; + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count = 1; + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[1] = NULL; + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[2] = NULL; + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[3] = NULL; + ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count = 1; + LOG_I(NR_RRC,"Adding new NSA user (%p)\n",ue_context_p); rrc_add_nsa_user(rrc,ue_context_p, m); } diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index b52367b258..52ace5beb2 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -369,7 +369,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; NR_BWP_Downlink_t *bwp = NULL; - for (int bwp_loop = 0; bwp_loop < 4 ; bwp_loop++ ) + for (int bwp_loop = 0; bwp_loop < 1 ; bwp_loop++ ) { if (servingcellconfigdedicated) { From 0f63e948613133512e2405942b3a5b27118f0e2c Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 1 Dec 2021 23:17:20 +0000 Subject: [PATCH 044/233] Update Dedicated Serving Cell Configuration section in configuration file --- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 100 +++++++++++------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index c21f586d0a..05ba28b3f7 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -175,51 +175,79 @@ gNBs = ); -# Dedicated Serving Cell Configuration - servingCellConfigDedicated = ( - { - # BWP-Downlink - # BWP 1 Configuration - bwp-Id_1 = 1; - bwp1_locationAndBandwidth = 28908; // RBstart=33,L=106 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - bwp1_subcarrierSpacing = 1 ; - # BWP 2 Configuration - bwp-Id_2 = 2; - bwp2_locationAndBandwidth = 6325; // RBstart=0,L=24 ( 10 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - bwp2_subcarrierSpacing = 1 ; - # BWP 3 Configuration - bwp-Id_3 = 3; - bwp3_locationAndBandwidth = 13750; // RBstart=0,L=51 ( 20 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - bwp3_subcarrierSpacing = 1 ; - # BWP 4 Configuration - bwp-Id_4 = 4; - bwp4_locationAndBandwidth = 28908; // RBstart=33,L=106 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - bwp4_subcarrierSpacing = 1 ; - firstActiveDownlinkBWP-Id = 1; #BWP-Id +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + dl_bwp-Id_2 = 2; + dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + dl_bwp-Id_3 = 3; + dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + dl_bwp-Id_4 = 4; + dl_bwp4_locationAndBandwidth = 28899; // RBstart=24, L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp4_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } - defaultDownlinkBWP-Id = 1; #BWP-Id + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; - # UplinkConfig - firstActiveUplinkBWP-Id = 1; #BWP-Id + # BWP 2 Configuration + ul_bwp-Id_2 = 2; + ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp2_subcarrierSpacing = 1; - #BWP-Uplink - - } - ); + # BWP 3 Configuration + ul_bwp-Id_3 = 3; + ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + ul_bwp-Id_4 = 4; + ul_bwp4_locationAndBandwidth = 28899; // RBstart=24, L=106 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp4_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); # ------- SCTP definitions SCTP : From e6ae92ea17a82145776cbda242cf48122c0fbd9e Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Fri, 3 Dec 2021 12:56:01 +0000 Subject: [PATCH 045/233] Revert "Temporary fix to avoid QUECTEL to reject the RRCReconfiguration in NSA mode" This reverts commit 1302b48b923e24b09680297a0ff6c8c3ae0f0c4f --- openair2/RRC/NR/rrc_gNB_nsa.c | 11 ----------- openair2/RRC/NR/rrc_gNB_reconfig.c | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index a7f00a408c..faf1ac8867 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -126,17 +126,6 @@ void rrc_parse_ue_capabilities(gNB_RRC_INST *rrc, NR_UE_CapabilityRAT_ContainerL ue_context_p->ue_context.spCellConfig = calloc(1, sizeof(struct NR_SpCellConfig)); ue_context_p->ue_context.spCellConfig->spCellConfigDedicated = rrc->carrier.servingcellconfig; - - // TODO: to be removed! For now, QUECTEL rejects the RRCReconfiguration with more than 1 BWP Dedicated, need to understand why... - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[1] = NULL; - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[2] = NULL; - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[3] = NULL; - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count = 1; - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[1] = NULL; - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[2] = NULL; - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[3] = NULL; - ue_context_p->ue_context.spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count = 1; - LOG_I(NR_RRC,"Adding new NSA user (%p)\n",ue_context_p); rrc_add_nsa_user(rrc,ue_context_p, m); } diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 52ace5beb2..b52367b258 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -369,7 +369,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; NR_BWP_Downlink_t *bwp = NULL; - for (int bwp_loop = 0; bwp_loop < 1 ; bwp_loop++ ) + for (int bwp_loop = 0; bwp_loop < 4 ; bwp_loop++ ) { if (servingcellconfigdedicated) { From e4a7eda96878e2e43c90926619f3333429cbdc0c Mon Sep 17 00:00:00 2001 From: yihz Date: Tue, 7 Dec 2021 10:43:28 +0800 Subject: [PATCH 046/233] base on 2021.w48, diff_1206_2.diff, UL_NR_MIMO 4*4 --- diff_1206_2.txt | 4257 +++++++++++++++++ executables/nr-softmodem-common.h | 1 + executables/nr-softmodem.h | 2 + executables/nr-ue.c | 5 +- executables/nr-uesoftmodem.c | 7 +- .../nfapi/public_inc/fapi_nr_ue_interface.h | 3 +- .../nfapi/public_inc/nfapi_nr_interface_scf.h | 3 +- nfapi/open-nFAPI/nfapi/src/nfapi_p7.c | 6 +- openair1/PHY/INIT/nr_init.c | 20 +- openair1/PHY/INIT/nr_init_ru.c | 2 +- openair1/PHY/INIT/nr_init_ue.c | 5 +- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 9 +- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 30 +- openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 32 + openair1/PHY/NR_TRANSPORT/nr_ulsch.h | 6 + .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 1363 +++++- .../NR_TRANSPORT/nr_ulsch_llr_computation.c | 28 +- openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c | 6 +- .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 9 + openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 357 +- openair1/PHY/defs_gNB.h | 6 +- openair1/PHY/defs_nr_UE.h | 3 + openair1/PHY/defs_nr_common.h | 8 +- openair1/SCHED_NR/nr_ru_procedures.c | 3 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 20 +- openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 4 +- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 9 +- openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- openair1/SIMULATION/NR_PHY/ulschsim.c | 32 +- openair1/SIMULATION/NR_PHY/ulsim.c | 271 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 3 + openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 109 +- openair2/LAYER2/NR_MAC_gNB/config.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 3 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 14 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 28 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 34 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 1 + openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 3 +- openair2/RRC/NR/MESSAGES/asn1_msg.c | 1 + openair2/RRC/NR/nr_rrc_proto.h | 2 + openair2/RRC/NR/rrc_gNB_nsa.c | 4 +- openair2/RRC/NR/rrc_gNB_reconfig.c | 13 +- targets/ARCH/rfsimulator/simulator.c | 17 +- ....sa.band78.fr1.106PRB.usrpb210.4layer.conf | 320 ++ 46 files changed, 6444 insertions(+), 623 deletions(-) create mode 100644 diff_1206_2.txt create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf diff --git a/diff_1206_2.txt b/diff_1206_2.txt new file mode 100644 index 0000000000..ff352f4157 --- /dev/null +++ b/diff_1206_2.txt @@ -0,0 +1,4257 @@ +diff --git a/executables/nr-softmodem-common.h b/executables/nr-softmodem-common.h +index c02155326b..1238b43c27 100644 +--- a/executables/nr-softmodem-common.h ++++ b/executables/nr-softmodem-common.h +@@ -67,6 +67,7 @@ + #define CONFIG_HLP_ITTIL "Generate ITTI analyzser logs (similar to wireshark logs but with more details)\n" + #define CONFIG_HLP_DLMCS_PHYTEST "Set the downlink MCS for PHYTEST mode\n" + #define CONFIG_HLP_DLNL_PHYTEST "Set the downlink nrOfLayers for PHYTEST mode\n" ++#define CONFIG_HLP_ULNL_PHYTEST "Set the uplink nrOfLayers for PHYTEST mode\n" + #define CONFIG_HLP_STMON "Enable processing timing measurement of lte softmodem on per subframe basis \n" + #define CONFIG_HLP_MSLOTS "Skip the missed slots/subframes \n" + #define CONFIG_HLP_ULMCS_PHYTEST "Set the uplink MCS for PHYTEST mode\n" +diff --git a/executables/nr-softmodem.h b/executables/nr-softmodem.h +index 896c363cbf..a11572c0e1 100644 +--- a/executables/nr-softmodem.h ++++ b/executables/nr-softmodem.h +@@ -23,6 +23,7 @@ + {"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, i8ptr:&threequarter_fs, defintval:0, TYPE_INT8, 0}, \ + {"m" , CONFIG_HLP_DLMCS_PHYTEST,0, uptr:&target_dl_mcs, defintval:0, TYPE_UINT, 0}, \ + {"l" , CONFIG_HLP_DLNL_PHYTEST,0, uptr:&target_dl_Nl, defintval:0, TYPE_UINT, 0}, \ ++ {"L" , CONFIG_HLP_ULNL_PHYTEST,0, uptr:&target_ul_Nl, defintval:0, TYPE_UINT, 0}, \ + {"t" , CONFIG_HLP_ULMCS_PHYTEST,0, uptr:&target_ul_mcs, defintval:0, TYPE_UINT, 0}, \ + {"M" , CONFIG_HLP_DLBW_PHYTEST,0, uptr:&target_dl_bw, defintval:0, TYPE_UINT, 0}, \ + {"T" , CONFIG_HLP_ULBW_PHYTEST,0, uptr:&target_ul_bw, defintval:0, TYPE_UINT, 0}, \ +@@ -36,6 +37,7 @@ + extern threads_t threads; + extern uint32_t target_dl_mcs; + extern uint32_t target_dl_Nl; ++extern uint32_t target_ul_Nl; + extern uint32_t target_ul_mcs; + extern uint32_t target_dl_bw; + extern uint32_t target_ul_bw; +diff --git a/executables/nr-ue.c b/executables/nr-ue.c +index 60ff40abeb..a23f91b1dd 100644 +--- a/executables/nr-ue.c ++++ b/executables/nr-ue.c +@@ -278,7 +278,7 @@ void processSlotTX(void *arg) { + int tx_slot_type = nr_ue_slot_select(cfg, proc->frame_tx, proc->nr_slot_tx); + uint8_t gNB_id = 0; + +- LOG_D(PHY,"%d.%d => slot type %d\n",proc->frame_tx,proc->nr_slot_tx,tx_slot_type); ++ LOG_D(PHY,"processSlotTX %d.%d => slot type %d\n",proc->frame_tx,proc->nr_slot_tx,tx_slot_type); + if (tx_slot_type == NR_UPLINK_SLOT || tx_slot_type == NR_MIXED_SLOT){ + + // trigger L2 to run ue_scheduler thru IF module +@@ -372,7 +372,8 @@ void processSlotRX(void *arg) { + LOG_D(PHY, "Sending Uplink data \n"); + nr_ue_pusch_common_procedures(UE, + proc->nr_slot_tx, +- &UE->frame_parms,1); ++ &UE->frame_parms, ++ UE->frame_parms.nb_antennas_tx); + } + + if (UE->UE_mode[gNB_id] > NOT_SYNCHED && UE->UE_mode[gNB_id] < PUSCH) { +diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c +index ad5ea982c9..57fd284601 100644 +--- a/executables/nr-uesoftmodem.c ++++ b/executables/nr-uesoftmodem.c +@@ -465,8 +465,11 @@ int main( int argc, char **argv ) { + memset(UE[CC_id],0,sizeof(PHY_VARS_NR_UE)); + + set_options(CC_id, UE[CC_id]); +- NR_UE_MAC_INST_t *mac = get_mac_inst(0); +- ++ NR_DL_FRAME_PARMS *fp = &UE[CC_id]->frame_parms; ++ NR_UE_MAC_INST_t *mac = get_mac_inst(0); ++ ++ mac->phy_config.config_req.carrier_config.num_tx_ant = fp->nb_antennas_tx; ++ + if (get_softmodem_params()->sa) { + uint16_t nr_band = get_band(downlink_frequency[CC_id][0],uplink_frequency_offset[CC_id][0]); + mac->nr_band = nr_band; +diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +index 0e923931ad..8b93417cb0 100644 +--- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h ++++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +@@ -298,9 +298,10 @@ typedef struct + uint8_t qam_mod_order; + uint8_t mcs_index; + uint8_t mcs_table; +- uint8_t transform_precoding; ++ uint8_t transformPrecoder; + uint16_t data_scrambling_id; + uint8_t nrOfLayers; ++ uint8_t Tpmi; + //DMRS + uint16_t ul_dmrs_symb_pos; + uint8_t dmrs_config_type; +diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +index 9d245446c8..58ff9037ce 100644 +--- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h ++++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +@@ -1208,9 +1208,10 @@ typedef struct + uint8_t qam_mod_order; + uint8_t mcs_index; + uint8_t mcs_table; +- uint8_t transform_precoding; ++ uint8_t transformPrecoder; + uint16_t data_scrambling_id; + uint8_t nrOfLayers; ++ uint8_t Tpmi; + //DMRS + uint16_t ul_dmrs_symb_pos; + uint8_t dmrs_config_type; +diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c +index 7cb74f8562..473b0900bc 100644 +--- a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c ++++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c +@@ -941,9 +941,10 @@ static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, ui + push8(pusch_pdu->qam_mod_order, ppWritePackedMsg, end) && + push8(pusch_pdu->mcs_index, ppWritePackedMsg, end) && + push8(pusch_pdu->mcs_table, ppWritePackedMsg, end) && +- push8(pusch_pdu->transform_precoding, ppWritePackedMsg, end) && ++ push8(pusch_pdu->transformPrecoder, ppWritePackedMsg, end) && + push16(pusch_pdu->data_scrambling_id, ppWritePackedMsg, end) && + push8(pusch_pdu->nrOfLayers, ppWritePackedMsg, end) && ++ push8(pusch_pdu->Tpmi, ppWritePackedMsg, end) && + push16(pusch_pdu->ul_dmrs_symb_pos, ppWritePackedMsg, end) && + push8(pusch_pdu->dmrs_config_type, ppWritePackedMsg, end) && + push16(pusch_pdu->ul_dmrs_scrambling_id, ppWritePackedMsg, end) && +@@ -4445,9 +4446,10 @@ static uint8_t unpack_ul_tti_request_pusch_pdu(void *tlv, uint8_t **ppReadPacked + pull8(ppReadPackedMsg, &pusch_pdu->qam_mod_order, end) && + pull8(ppReadPackedMsg, &pusch_pdu->mcs_index, end) && + pull8(ppReadPackedMsg, &pusch_pdu->mcs_table, end) && +- pull8(ppReadPackedMsg, &pusch_pdu->transform_precoding, end) && ++ pull8(ppReadPackedMsg, &pusch_pdu->transformPrecoder, end) && + pull16(ppReadPackedMsg, &pusch_pdu->data_scrambling_id, end) && + pull8(ppReadPackedMsg, &pusch_pdu->nrOfLayers, end) && ++ pull8(ppReadPackedMsg, &pusch_pdu->Tpmi, end) && + pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_symb_pos, end) && + pull8(ppReadPackedMsg, &pusch_pdu->dmrs_config_type, end) && + pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_scrambling_id, end) && +diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c +index cd4848ea8c..2ac3230215 100644 +--- a/openair1/PHY/INIT/nr_init.c ++++ b/openair1/PHY/INIT/nr_init.c +@@ -276,11 +276,21 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, + pusch_vars[ULSCH_id]->ul_ch_magb0 = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); + pusch_vars[ULSCH_id]->ul_ch_mag = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); + pusch_vars[ULSCH_id]->ul_ch_magb = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); +- pusch_vars[ULSCH_id]->rho = (int32_t **)malloc16_clear(n_buf*sizeof(int32_t*) ); ++ pusch_vars[ULSCH_id]->rho = (int32_t ***)malloc16_clear(Prx*sizeof(int32_t**) ); ++ pusch_vars[ULSCH_id]->llr_layers = (int16_t **)malloc16(max_ul_mimo_layers*sizeof(int32_t *) ); + + for (i=0; irxdataF_ext[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); + pusch_vars[ULSCH_id]->rxdataF_ext2[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); ++ pusch_vars[ULSCH_id]->rho[i] = (int32_t **)malloc16_clear( NR_MAX_NB_LAYERS*NR_MAX_NB_LAYERS*sizeof(int32_t*)); ++ ++ for (int j=0; j< max_ul_mimo_layers; j++) ++ { ++ for (int k=0; krho[i][j*max_ul_mimo_layers+k]=(int32_t *)malloc16_clear( sizeof(int32_t) * fp->N_RB_UL*12*7*2 ); ++ } ++ } + } + for (i=0; iul_ch_estimates[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*fp->ofdm_symbol_size*2*fp->symbols_per_slot ); +@@ -293,8 +303,12 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, + pusch_vars[ULSCH_id]->ul_ch_mag0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); + pusch_vars[ULSCH_id]->ul_ch_magb0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); + pusch_vars[ULSCH_id]->ul_ch_mag[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); +- pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); +- pusch_vars[ULSCH_id]->rho[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*(fp->N_RB_UL*12*7*2) ); ++ pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); ++ } ++ ++ for (i=0; i< max_ul_mimo_layers; i++) ++ { ++ pusch_vars[ULSCH_id]->llr_layers[i] = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear + } + pusch_vars[ULSCH_id]->llr = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear + pusch_vars[ULSCH_id]->ul_valid_re_per_slot = (int16_t *)malloc16_clear( sizeof(int16_t)*fp->symbols_per_slot); +diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c +index 4024ed1617..169db48e69 100644 +--- a/openair1/PHY/INIT/nr_init_ru.c ++++ b/openair1/PHY/INIT/nr_init_ru.c +@@ -41,7 +41,7 @@ int nr_phy_init_RU(RU_t *ru) { + int p; + int re; + +- LOG_I(PHY,"Initializing RU signal buffers (if_south %s) nb_tx %d\n",ru_if_types[ru->if_south],ru->nb_tx); ++ LOG_I(PHY,"Initializing RU signal buffers (if_south %s) nb_tx %d, nb_rx %d\n",ru_if_types[ru->if_south],ru->nb_tx, ru->nb_rx); + + nfapi_nr_config_request_scf_t *cfg; + ru->nb_log_antennas=0; +diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c +index b60e77e814..11fb6e7d61 100644 +--- a/openair1/PHY/INIT/nr_init_ue.c ++++ b/openair1/PHY/INIT/nr_init_ue.c +@@ -124,7 +124,8 @@ void phy_init_nr_ue_PUSCH(NR_UE_PUSCH *const pusch, + AssertFatal( pusch, "pusch==0" ); + + for (int i=0; itxdataF_layers[i] = (int32_t *)malloc16_clear(NR_MAX_PUSCH_ENCODED_LENGTH*sizeof(int32_t)); ++ pusch->txdataF_layers[i] = (int32_t *)malloc16_clear((NR_MAX_PUSCH_ENCODED_LENGTH)*sizeof(int32_t *)); ++ pusch->txdataF_precoding[i] = (int32_t *)malloc16_clear((NR_MAX_PUSCH_ENCODED_LENGTH)*sizeof(int32_t *)); + } + } + +@@ -143,7 +144,7 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, + uint16_t N_n_scid[2] = {0,1}; // [HOTFIX] This is a temporary implementation of scramblingID0 and scramblingID1 which are given by DMRS-UplinkConfig + int n_scid; + abstraction_flag = 0; +- LOG_I(PHY, "Initializing UE vars (abstraction %u) for gNB TXant %u, UE RXant %u\n", abstraction_flag, fp->nb_antennas_tx, fp->nb_antennas_rx); ++ LOG_I(PHY, "Initializing UE vars (abstraction %u) for UE TXant %u, UE RXant %u\n", abstraction_flag, fp->nb_antennas_tx, fp->nb_antennas_rx); + //LOG_D(PHY,"[MSC_NEW][FRAME 00000][PHY_UE][MOD %02u][]\n", ue->Mod_id+NB_gNB_INST); + phy_init_nr_top(ue); + // many memory allocation sizes are hard coded +diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +index 1f164748bf..9a432e8615 100644 +--- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c ++++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +@@ -110,7 +110,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, + fr = filt8_r1; + fmm = filt8_mm1; + fml = filt8_ml1; +- fmr = filt8_m1; ++ fmr = filt8_mm1; + fdcl = filt8_dcl1; + fdcr = filt8_dcr1; + fdclh = filt8_dcl1_h; +@@ -129,9 +129,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, + + //------------------generate DMRS------------------// + +- // transform precoding = 1 means disabled +- if (pusch_pdu->transform_precoding == 1) { +- nr_pusch_dmrs_rx(gNB, Ns, gNB->nr_gold_pusch_dmrs[pusch_pdu->scid][Ns][symbol], &pilot[0], 1000, 0, nb_rb_pusch, ++ if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { ++ nr_pusch_dmrs_rx(gNB, Ns, gNB->nr_gold_pusch_dmrs[pusch_pdu->scid][Ns][symbol], &pilot[0], (1000+p), 0, nb_rb_pusch, + (pusch_pdu->bwp_start + pusch_pdu->rb_start)*NR_NB_SC_PER_RB, pusch_pdu->dmrs_config_type); + } + else { // if transform precoding or SC-FDMA is enabled in Uplink +@@ -291,7 +290,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, + printf("pilot %u : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); + printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); + #endif +- multadd_real_vector_complex_scalar(fml, ++ multadd_real_vector_complex_scalar(fm, + ch, + ul_ch, + 8); +diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +index 4fed3a468f..87e4d57362 100644 +--- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h ++++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +@@ -137,21 +137,22 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + @param nb_rb_pusch The number of RBs allocated (used for Resource Allocation Type 1 in NR) + @param frame_parms, Pointer to frame descriptor structure + */ +-void nr_ulsch_extract_rbs_single(int32_t **rxdataF, +- NR_gNB_PUSCH *pusch_vars, +- int slot, +- unsigned char symbol, +- uint8_t is_dmrs_symbol, +- nfapi_nr_pusch_pdu_t *pusch_pdu, +- NR_DL_FRAME_PARMS *frame_parms); ++void nr_ulsch_extract_rbs(int32_t **rxdataF, ++ NR_gNB_PUSCH *pusch_vars, ++ int slot, ++ unsigned char symbol, ++ uint8_t is_dmrs_symbol, ++ nfapi_nr_pusch_pdu_t *pusch_pdu, ++ NR_DL_FRAME_PARMS *frame_parms); + + void nr_ulsch_scale_channel(int32_t **ul_ch_estimates_ext, + NR_DL_FRAME_PARMS *frame_parms, + NR_gNB_ULSCH_t **ulsch_gNB, +- uint8_t symbol, +- uint8_t start_symbol, +- uint16_t nb_rb, +- pusch_dmrs_type_t pusch_dmrs_type); ++ uint8_t symbol, ++ uint8_t is_dmrs_symbol, ++ uint32_t len, ++ uint8_t nrOfLayers, ++ uint16_t nb_rb); + + /** \brief This function computes the average channel level over all allocated RBs and antennas (TX/RX) in order to compute output shift for compensated signal + @param ul_ch_estimates_ext Channel estimates in allocated RBs +@@ -185,9 +186,10 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, + int **ul_ch_mag, + int **ul_ch_magb, + int **rxdataF_comp, +- int **rho, ++ int ***rho, + NR_DL_FRAME_PARMS *frame_parms, + unsigned char symbol, ++ int length, + uint8_t is_dmrs_symbol, + unsigned char mod_order, + uint8_t nrOfLayers, +@@ -255,8 +257,8 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, + @param mod_order modulation order + */ + void nr_ulsch_compute_llr(int32_t *rxdataF_comp, +- int32_t **ul_ch_mag, +- int32_t **ul_ch_magb, ++ int32_t *ul_ch_mag, ++ int32_t *ul_ch_magb, + int16_t *ulsch_llr, + uint32_t nb_rb, + uint32_t nb_re, +diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +index 4278338dbc..3ea19a9970 100644 +--- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c ++++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +@@ -137,6 +137,38 @@ void nr_ulsch_unscrambling_optim(int16_t* llr, + #endif + } + ++void nr_ulsch_layer_demapping(int16_t *llr_cw, ++ uint8_t Nl, ++ uint8_t mod_order, ++ uint32_t length, ++ int16_t **llr_layers) ++{ ++ ++ switch (Nl) { ++ ++ case 1: ++ memcpy((void*)llr_cw, (void*)llr_layers[0], (length)*sizeof(int16_t)); ++ break; ++ ++ case 2: ++ case 3: ++ case 4: ++ for (int i=0; i<(length/Nl/mod_order); i++) ++ { ++ for (int l=0; lnumber_of_nr_ulsch_max;i++) { +diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +index 80c10d74b1..0e660351ae 100644 +--- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h ++++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +@@ -82,6 +82,12 @@ void nr_ulsch_unscrambling_optim(int16_t* llr, + uint32_t Nid, + uint32_t n_RNTI); + ++void nr_ulsch_layer_demapping(int16_t *llr_cw, ++ uint8_t Nl, ++ uint8_t mod_order, ++ uint32_t length, ++ int16_t **llr_layers); ++ + void nr_ulsch_procedures(PHY_VARS_gNB *gNB, + int frame_rx, + int slot_rx, +diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +index 28dd5ab219..8314c83ad9 100644 +--- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c ++++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +@@ -302,26 +302,27 @@ void nr_idft(int32_t *z, uint32_t Msc_PUSCH) + } + + +-void nr_ulsch_extract_rbs_single(int32_t **rxdataF, +- NR_gNB_PUSCH *pusch_vars, +- int slot, +- unsigned char symbol, +- uint8_t is_dmrs_symbol, +- nfapi_nr_pusch_pdu_t *pusch_pdu, +- NR_DL_FRAME_PARMS *frame_parms) ++void nr_ulsch_extract_rbs(int32_t **rxdataF, ++ NR_gNB_PUSCH *pusch_vars, ++ int slot, ++ unsigned char symbol, ++ uint8_t is_dmrs_symbol, ++ nfapi_nr_pusch_pdu_t *pusch_pdu, ++ NR_DL_FRAME_PARMS *frame_parms) + { + + unsigned short start_re, re, nb_re_pusch; +- unsigned char aarx; ++ unsigned char aarx, aatx; + uint32_t rxF_ext_index = 0; + uint32_t ul_ch0_ext_index = 0; + uint32_t ul_ch0_index = 0; +- uint8_t k_prime; +- uint16_t n; ++ //uint8_t k_prime; ++ //uint16_t n; + int16_t *rxF,*rxF_ext; + int *ul_ch0,*ul_ch0_ext; +- uint8_t delta = 0; ++ //uint8_t delta = 0; + int soffset = (slot&3)*frame_parms->symbols_per_slot*frame_parms->ofdm_symbol_size; ++ + #ifdef DEBUG_RB_EXT + + printf("--------------------symbol = %d-----------------------\n", symbol); +@@ -329,7 +330,7 @@ void nr_ulsch_extract_rbs_single(int32_t **rxdataF, + + #endif + +- uint8_t is_dmrs_re; ++ uint8_t is_data_re; + start_re = (frame_parms->first_carrier_offset + (pusch_pdu->rb_start + pusch_pdu->bwp_start) * NR_NB_SC_PER_RB)%frame_parms->ofdm_symbol_size; + nb_re_pusch = NR_NB_SC_PER_RB * pusch_pdu->rb_size; + #ifdef __AVX2__ +@@ -338,64 +339,95 @@ void nr_ulsch_extract_rbs_single(int32_t **rxdataF, + int nb_re_pusch2 = nb_re_pusch; + #endif + +- for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { ++ for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) ++ { + + rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol * frame_parms->ofdm_symbol_size)]; + rxF_ext = (int16_t *)&pusch_vars->rxdataF_ext[aarx][symbol * nb_re_pusch2]; // [hna] rxdataF_ext isn't contiguous in order to solve an alignment problem ib llr computation in case of mod_order = 4, 6 +- +- ul_ch0 = &pusch_vars->ul_ch_estimates[aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available +- +- ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aarx][symbol*nb_re_pusch2]; +- +- n = 0; +- k_prime = 0; +- rxF_ext_index = 0; +- ul_ch0_ext_index = 0; +- ul_ch0_index = 0; +- +- if (is_dmrs_symbol == 0) { ++ ++ if (is_dmrs_symbol == 0) ++ { + // + //rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); +- if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) { ++ if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) ++ { + memcpy1((void*)rxF_ext, + (void*)&rxF[start_re*2], + nb_re_pusch*sizeof(int32_t)); +- } else { +- int neg_length = frame_parms->ofdm_symbol_size-start_re; +- int pos_length = nb_re_pusch-neg_length; ++ } ++ else ++ { ++ int neg_length = frame_parms->ofdm_symbol_size-start_re; ++ int pos_length = nb_re_pusch-neg_length; ++ memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); ++ memcpy1((void*)&rxF_ext[2*neg_length],(void*)rxF,pos_length*sizeof(int32_t)); ++ } + +- memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); +- memcpy1((void*)&rxF_ext[2*neg_length],(void*)rxF,pos_length*sizeof(int32_t)); ++ #ifdef SUPPORT_PMI_MATRIC ++ for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) ++ #else ++ aatx = aarx; ++ #endif ++ { ++ ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available ++ ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; ++ ++ memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); + } +- memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); + } +- else { +- for (re = 0; re < nb_re_pusch; re++) { +- +- is_dmrs_re = (re == get_dmrs_freq_idx_ul(n, k_prime, delta, pusch_pdu->dmrs_config_type)); +- +-#ifdef DEBUG_RB_EXT +- printf("re = %d, kprime %d, n %d, is_dmrs_symbol = %d, symbol = %d\n", re, k_prime, n, is_dmrs_symbol, symbol); +-#endif +- +- /* save only data and respective channel estimates */ +- if (is_dmrs_re == 0) { +- rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); +- rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); +- ul_ch0_ext[ul_ch0_ext_index] = ul_ch0[ul_ch0_index]; ++ else ++ { ++ #ifdef SUPPORT_PMI_MATRIC ++ for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) ++ #else ++ aatx = aarx; ++ #endif ++ { ++ ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available ++ ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; ++ ++ //n = 0; ++ //k_prime = 0; ++ rxF_ext_index = 0; ++ ul_ch0_ext_index = 0; ++ ul_ch0_index = 0; ++ ++ for (re = 0; re < nb_re_pusch; re++) ++ { ++ uint16_t k= start_re+re; ++ ++ is_data_re = allowed_xlsch_re_in_dmrs_symbol(k, start_re, frame_parms->ofdm_symbol_size, pusch_pdu->num_dmrs_cdm_grps_no_data, pusch_pdu->dmrs_config_type); ++ ++ if (++k >= frame_parms->ofdm_symbol_size) ++ { ++ k -= frame_parms->ofdm_symbol_size; ++ } + +-#ifdef DEBUG_RB_EXT +- printf("dmrs symb %d: rxF_ext[%d] = (%d,%d), ul_ch0_ext[%d] = (%d,%d)\n", ++ #ifdef DEBUG_RB_EXT ++ printf("re = %d, kprime %d, n %d, is_dmrs_symbol = %d, symbol = %d\n", re, k_prime, n, is_dmrs_symbol, symbol); ++ #endif ++ ++ /* save only data and respective channel estimates */ ++ if (is_data_re == 1) ++ { ++ if (aatx == aarx) ++ { ++ rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); ++ rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); ++ rxF_ext_index +=2; ++ } ++ ++ ul_ch0_ext[ul_ch0_ext_index] = ul_ch0[ul_ch0_index]; ++ ul_ch0_ext_index++; ++ ++ #ifdef DEBUG_RB_EXT ++ printf("dmrs symb %d: rxF_ext[%d] = (%d,%d), ul_ch0_ext[%d] = (%d,%d)\n", + is_dmrs_symbol,rxF_ext_index>>1, rxF_ext[rxF_ext_index],rxF_ext[rxF_ext_index+1], + ul_ch0_ext_index, ((int16_t*)&ul_ch0_ext[ul_ch0_ext_index])[0], ((int16_t*)&ul_ch0_ext[ul_ch0_ext_index])[1]); +-#endif +- ul_ch0_ext_index++; +- rxF_ext_index +=2; +- } else { +- n += k_prime; +- k_prime ^= 1; ++ #endif ++ } ++ ul_ch0_index++; + } +- ul_ch0_index++; + } + } + } +@@ -406,16 +438,19 @@ void nr_ulsch_scale_channel(int **ul_ch_estimates_ext, + NR_gNB_ULSCH_t **ulsch_gNB, + uint8_t symbol, + uint8_t is_dmrs_symbol, +- unsigned short nb_rb, +- pusch_dmrs_type_t pusch_dmrs_type) ++ uint32_t len, ++ uint8_t nrOfLayers, ++ unsigned short nb_rb) + { + + #if defined(__x86_64__)||defined(__i386__) + + short rb, ch_amp; +- unsigned char aarx; ++ unsigned char aarx,aatx; + __m128i *ul_ch128, ch_amp128; + ++ uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); ++ + // Determine scaling amplitude based the symbol + + ch_amp = 1024*8; //((pilots) ? (ulsch_gNB[0]->sqrt_rho_b) : (ulsch_gNB[0]->sqrt_rho_a)); +@@ -431,35 +466,26 @@ void nr_ulsch_scale_channel(int **ul_ch_estimates_ext, + int off = 0; + #endif + +- for (aarx=0; aarx < frame_parms->nb_antennas_rx; aarx++) { +- +- ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aarx][symbol*(off+(nb_rb*NR_NB_SC_PER_RB))]; +- +- if (is_dmrs_symbol==1){ +- if (pusch_dmrs_type == pusch_dmrs_type1) +- nb_rb = nb_rb>>1; +- else +- nb_rb = (2*nb_rb)/3; +- } +- +- for (rb=0;rbnb_antennas_rx; aarx++) ++ { ++ ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*NR_NB_SC_PER_RB))]; + ++ for (rb=0;rb < nb_rb_0;rb++) ++ { + ul_ch128[0] = _mm_mulhi_epi16(ul_ch128[0], ch_amp128); + ul_ch128[0] = _mm_slli_epi16(ul_ch128[0], 3); + + ul_ch128[1] = _mm_mulhi_epi16(ul_ch128[1], ch_amp128); + ul_ch128[1] = _mm_slli_epi16(ul_ch128[1], 3); + +- if (is_dmrs_symbol) { +- ul_ch128+=2; +- } else { +- ul_ch128[2] = _mm_mulhi_epi16(ul_ch128[2], ch_amp128); +- ul_ch128[2] = _mm_slli_epi16(ul_ch128[2], 3); +- ul_ch128+=3; +- +- } ++ ul_ch128[2] = _mm_mulhi_epi16(ul_ch128[2], ch_amp128); ++ ul_ch128[2] = _mm_slli_epi16(ul_ch128[2], 3); ++ ul_ch128+=3; + } +- } ++ } ++} + #endif + } + +@@ -481,6 +507,8 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, + + int16_t x = factor2(len); + int16_t y = (len)>>x; ++ ++ uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); + + #ifdef __AVX2__ + int off = ((nb_rb&1) == 1)? 4:0; +@@ -489,13 +517,16 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, + #endif + + for (aatx = 0; aatx < nrOfLayers; aatx++) +- for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { ++ { ++ for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) ++ { + //clear average level + avg128U = _mm_setzero_si128(); + + ul_ch128=(__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; + +- for (rb = 0; rb < len/12; rb++) { ++ for (rb = 0; rb < nb_rb_0; rb++) ++ { + avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[0], ul_ch128[0]), x)); + avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[1], ul_ch128[1]), x)); + avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[2], ul_ch128[2]), x)); +@@ -508,7 +539,8 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, + ((int32_t*)&avg128U)[3]) / y; + + } +- ++ } ++ + _mm_empty(); + _m_empty(); + +@@ -520,7 +552,7 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, + int16x4_t *ul_ch128; + + symbol_mod = (symbol>=(7-frame_parms->Ncp)) ? symbol-(7-frame_parms->Ncp) : symbol; +- ++ uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); + for (aatx=0; aatxnb_antennas_rx; aarx++) { + //clear average level +@@ -529,7 +561,7 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, + + ul_ch128 = (int16x4_t *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*frame_parms->N_RB_UL*12]; + +- for (rb = 0; rb < nb_rb; rb++) { ++ for (rb = 0; rb < nb_rb_0; rb++) { + // printf("rb %d : ",rb); + // print_shorts("ch",&ul_ch128[0]); + avg128U = vqaddq_s32(avg128U, vmull_s16(ul_ch128[0], ul_ch128[0])); +@@ -568,14 +600,20 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, + #endif + } + ++ ++ ++//============================================================================================== ++// Pre-processing for LLR computation ++//============================================================================================== + void nr_ulsch_channel_compensation(int **rxdataF_ext, + int **ul_ch_estimates_ext, + int **ul_ch_mag, + int **ul_ch_magb, + int **rxdataF_comp, +- int **rho, ++ int ***rho, + NR_DL_FRAME_PARMS *frame_parms, + unsigned char symbol, ++ int length, + uint8_t is_dmrs_symbol, + unsigned char mod_order, + uint8_t nrOfLayers, +@@ -637,20 +675,24 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, + __m128i mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3,QAM_amp128={0},QAM_amp128b={0}; + QAM_amp128b = _mm_setzero_si128(); + +- for (aatx=0; aatxnb_antennas_rx; aarx++) { +- ++ for (aarx=0; aarxnb_antennas_rx; aarx++) ++ { + ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; + ul_ch_mag128 = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; + ul_ch_mag128b = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; +@@ -658,8 +700,10 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, + rxdataF_comp128 = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; + + +- for (rb=0; rb2) { ++ for (rb=0; rb2) ++ { + // get channel amplitude if not QPSK + + //print_shorts("ch:",(int16_t*)&ul_ch128[0]); +@@ -679,6 +723,8 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, + ul_ch_mag128[0] = _mm_mulhi_epi16(ul_ch_mag128[0],QAM_amp128); + ul_ch_mag128[0] = _mm_slli_epi16(ul_ch_mag128[0],1); + ++ ul_ch_mag128b[0] = _mm_mulhi_epi16(ul_ch_mag128b[0],QAM_amp128b); ++ ul_ch_mag128b[0] = _mm_slli_epi16(ul_ch_mag128b[0],1); + // print_ints("ch: = ",(int32_t*)&mmtmpD0); + // print_shorts("QAM_amp:",(int16_t*)&QAM_amp128); + // print_shorts("mag:",(int16_t*)&ul_ch_mag128[0]); +@@ -687,30 +733,24 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, + ul_ch_mag128b[1] = ul_ch_mag128[1]; + ul_ch_mag128[1] = _mm_mulhi_epi16(ul_ch_mag128[1],QAM_amp128); + ul_ch_mag128[1] = _mm_slli_epi16(ul_ch_mag128[1],1); ++ ++ ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128); ++ ul_ch_mag128b[1] = _mm_slli_epi16(ul_ch_mag128b[1],1); + +- if (is_dmrs_symbol==0) { +- mmtmpD0 = _mm_madd_epi16(ul_ch128[2],ul_ch128[2]); +- mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); +- mmtmpD1 = _mm_packs_epi32(mmtmpD0,mmtmpD0); +- +- ul_ch_mag128[2] = _mm_unpacklo_epi16(mmtmpD1,mmtmpD1); +- ul_ch_mag128b[2] = ul_ch_mag128[2]; ++ mmtmpD0 = _mm_madd_epi16(ul_ch128[2],ul_ch128[2]); ++ mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); ++ mmtmpD1 = _mm_packs_epi32(mmtmpD0,mmtmpD0); + +- ul_ch_mag128[2] = _mm_mulhi_epi16(ul_ch_mag128[2],QAM_amp128); +- ul_ch_mag128[2] = _mm_slli_epi16(ul_ch_mag128[2],1); +- } ++ ul_ch_mag128[2] = _mm_unpacklo_epi16(mmtmpD1,mmtmpD1); ++ ul_ch_mag128b[2] = ul_ch_mag128[2]; + +- ul_ch_mag128b[0] = _mm_mulhi_epi16(ul_ch_mag128b[0],QAM_amp128b); +- ul_ch_mag128b[0] = _mm_slli_epi16(ul_ch_mag128b[0],1); ++ ul_ch_mag128[2] = _mm_mulhi_epi16(ul_ch_mag128[2],QAM_amp128); ++ ul_ch_mag128[2] = _mm_slli_epi16(ul_ch_mag128[2],1); + + +- ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128b); +- ul_ch_mag128b[1] = _mm_slli_epi16(ul_ch_mag128b[1],1); ++ ul_ch_mag128b[2] = _mm_mulhi_epi16(ul_ch_mag128b[2],QAM_amp128b); ++ ul_ch_mag128b[2] = _mm_slli_epi16(ul_ch_mag128b[2],1); + +- if (is_dmrs_symbol==0) { +- ul_ch_mag128b[2] = _mm_mulhi_epi16(ul_ch_mag128b[2],QAM_amp128b); +- ul_ch_mag128b[2] = _mm_slli_epi16(ul_ch_mag128b[2],1); +- } + } + + // multiply by conjugated channel +@@ -755,117 +795,157 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, + // print_shorts("ch:",ul_ch128+1); + // print_shorts("pack:",rxdataF_comp128+1); + +- if (is_dmrs_symbol==0) { +- // multiply by conjugated channel +- mmtmpD0 = _mm_madd_epi16(ul_ch128[2],rxdataF128[2]); +- // mmtmpD0 contains real part of 4 consecutive outputs (32-bit) +- mmtmpD1 = _mm_shufflelo_epi16(ul_ch128[2],_MM_SHUFFLE(2,3,0,1)); +- mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); +- mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)conjugate); +- mmtmpD1 = _mm_madd_epi16(mmtmpD1,rxdataF128[2]); +- // mmtmpD1 contains imag part of 4 consecutive outputs (32-bit) +- mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); +- mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift); +- mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); +- mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); +- +- rxdataF_comp128[2] = _mm_packs_epi32(mmtmpD2,mmtmpD3); +- // print_shorts("rx:",rxdataF128+2); +- // print_shorts("ch:",ul_ch128+2); +- // print_shorts("pack:",rxdataF_comp128+2); +- +- ul_ch128+=3; +- ul_ch_mag128+=3; +- ul_ch_mag128b+=3; +- rxdataF128+=3; +- rxdataF_comp128+=3; +- } else { // we have a smaller PUSCH in symbols with pilots so skip last group of 4 REs and increment less +- ul_ch128+=2; +- ul_ch_mag128+=2; +- ul_ch_mag128b+=2; +- rxdataF128+=2; +- rxdataF_comp128+=2; +- } +- +- } +- } +- } +- +- if (rho) { +- +- for (aarx=0; aarxnb_antennas_rx; aarx++) { +- rho128 = (__m128i *)&rho[aarx][symbol*frame_parms->N_RB_UL*12]; +- ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aarx][symbol*frame_parms->N_RB_UL*12]; +- ul_ch128_2 = (__m128i *)&ul_ch_estimates_ext[2+aarx][symbol*frame_parms->N_RB_UL*12]; +- +- for (rb=0; rbnb_antennas_rx][nrOfLayers*nrOfLayers]; ++ int avg_rho_im[frame_parms->nb_antennas_rx][nrOfLayers*nrOfLayers]; ++ ++ for (aarx=0; aarxnb_antennas_rx; aarx++) ++ { ++ for (aatx=0; aatx < nrOfLayers; aatx++) ++ { ++ ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; ++ ++ for (int atx=0; atx< nrOfLayers; atx++) ++ { ++ avg_rho_re[aarx][aatx*nrOfLayers+atx] = 0; ++ avg_rho_im[aarx][aatx*nrOfLayers+atx] = 0; ++ rho128 = (__m128i *)&rho[aarx][aatx*nrOfLayers+atx][symbol*(off+(nb_rb*12))]; ++ ul_ch128_2 = (__m128i *)&ul_ch_estimates_ext[atx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; ++ ++ for (rb=0; rbrx_correlation[0][0][aarx] = signal_energy(&rho[aarx][aatx*nb_aatx+atx][symbol*nb_rb*12],rb*12); ++ avg_rho_re[aarx][aatx*nrOfLayers+atx] = 16*avg_rho_re[aarx][aatx*nrOfLayers+atx]/(nb_rb*12); ++ avg_rho_im[aarx][aatx*nrOfLayers+atx] = 16*avg_rho_im[aarx][aatx*nrOfLayers+atx]/(nb_rb*12); ++ //printf("rho[rx]%d tx%d tx%d = Re: %d Im: %d\n",aarx, aatx,atx, avg_rho_re[aarx][aatx*nb_aatx+atx], avg_rho_im[aarx][aatx*nb_aatx+atx]); ++ } ++ } ++ } + } + } + +@@ -1105,19 +1185,23 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, + } + + void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, +- int32_t **rxdataF_comp, +- int32_t **ul_ch_mag, +- int32_t **ul_ch_magb, +- uint8_t symbol, +- uint16_t nb_rb) { ++ int32_t **rxdataF_comp, ++ int32_t **ul_ch_mag, ++ int32_t **ul_ch_magb, ++ int32_t ***rho, ++ uint8_t nrOfLayers, ++ uint8_t symbol, ++ uint16_t nb_rb, ++ int length) { + int n_rx = frame_parms->nb_antennas_rx; + #if defined(__x86_64__) || defined(__i386__) +- __m128i *rxdataF_comp128[1+n_rx],*ul_ch_mag128[1+n_rx],*ul_ch_mag128b[1+n_rx]; ++ __m128i *rxdataF_comp128[2],*ul_ch_mag128[2],*ul_ch_mag128b[2]; + #elif defined(__arm__) + int16x8_t *rxdataF_comp128_0,*ul_ch_mag128_0,*ul_ch_mag128_0b; + int16x8_t *rxdataF_comp128_1,*ul_ch_mag128_1,*ul_ch_mag128_1b; + #endif + int32_t i; ++ uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); + + #ifdef __AVX2__ + int off = ((nb_rb&1) == 1)? 4:0; +@@ -1125,23 +1209,34 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, + int off = 0; + #endif + +- if (frame_parms->nb_antennas_rx>1) { +-#if defined(__x86_64__) || defined(__i386__) +- int nb_re = nb_rb*12; +- for (int aa=0;aanb_antennas_rx;aa++) { +- rxdataF_comp128[aa] = (__m128i *)&rxdataF_comp[aa][(symbol*(nb_re + off))]; +- ul_ch_mag128[aa] = (__m128i *)&ul_ch_mag[aa][(symbol*(nb_re + off))]; +- ul_ch_mag128b[aa] = (__m128i *)&ul_ch_magb[aa][(symbol*(nb_re + off))]; +- } +- for (int aa=1;aanb_antennas_rx;aa++) { +- // MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM llr computation) +- for (i=0; i 1) ++ { ++ #if defined(__x86_64__) || defined(__i386__) ++ for (int aatx=0; aatxnb_antennas_rx][(symbol*(nb_re + off))]; ++ ul_ch_mag128[0] = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx][(symbol*(nb_re + off))]; ++ ul_ch_mag128b[0] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx][(symbol*(nb_re + off))]; ++ ++ for (int aa=1;aa < n_rx;aa++) ++ { ++ rxdataF_comp128[1] = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; ++ ul_ch_mag128[1] = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; ++ ul_ch_mag128b[1] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; ++ ++ // MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM llr computation) ++ for (i=0; iN_RB_DL*12]; + rxdataF_comp128_1 = (int16x8_t *)&rxdataF_comp[1][symbol*frame_parms->N_RB_DL*12]; + ul_ch_mag128_0 = (int16x8_t *)&ul_ch_mag[0][symbol*frame_parms->N_RB_DL*12]; +@@ -1156,7 +1251,7 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, + ul_ch_mag128_0b[i] = vhaddq_s16(ul_ch_mag128_0b[i],ul_ch_mag128_1b[i]); + rxdataF_comp128_0[i] = vqaddq_s16(rxdataF_comp128_0[i],(*(int16x8_t *)&jitterc[0])); + } +-#endif ++ #endif + } + + #if defined(__x86_64__) || defined(__i386__) +@@ -1165,6 +1260,696 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, + #endif + } + ++/* Zero Forcing Rx function: nr_det_HhH() ++ * ++ * ++ * */ ++void nr_ulsch_det_HhH(int32_t *after_mf_00,//a ++ int32_t *after_mf_01,//b ++ int32_t *after_mf_10,//c ++ int32_t *after_mf_11,//d ++ int32_t *det_fin,//1/ad-bc ++ unsigned short nb_rb, ++ unsigned char symbol, ++ int32_t shift) ++{ ++ int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; ++ unsigned short rb; ++ __m128i *after_mf_00_128,*after_mf_01_128, *after_mf_10_128, *after_mf_11_128, ad_re_128, bc_re_128; //ad_im_128, bc_im_128; ++ __m128i *det_fin_128, det_re_128; //det_im_128, tmp_det0, tmp_det1; ++ ++ after_mf_00_128 = (__m128i *)after_mf_00; ++ after_mf_01_128 = (__m128i *)after_mf_01; ++ after_mf_10_128 = (__m128i *)after_mf_10; ++ after_mf_11_128 = (__m128i *)after_mf_11; ++ ++ det_fin_128 = (__m128i *)det_fin; ++ ++ for (rb=0; rb<3*nb_rb; rb++) { ++ ++ //complex multiplication (I_a+jQ_a)(I_d+jQ_d) = (I_aI_d - Q_aQ_d) + j(Q_aI_d + I_aQ_d) ++ //The imag part is often zero, we compute only the real part ++ ad_re_128 = _mm_sign_epi16(after_mf_00_128[0],*(__m128i*)&nr_conjug2[0]); ++ ad_re_128 = _mm_madd_epi16(ad_re_128,after_mf_11_128[0]); //Re: I_a0*I_d0 - Q_a1*Q_d1 ++ //ad_im_128 = _mm_shufflelo_epi16(after_mf_00_128[0],_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ //ad_im_128 = _mm_shufflehi_epi16(ad_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ //ad_im_128 = _mm_madd_epi16(ad_im_128,after_mf_11_128[0]);//Im: (Q_aI_d + I_aQ_d) ++ ++ //complex multiplication (I_b+jQ_b)(I_c+jQ_c) = (I_bI_c - Q_bQ_c) + j(Q_bI_c + I_bQ_c) ++ //The imag part is often zero, we compute only the real part ++ bc_re_128 = _mm_sign_epi16(after_mf_01_128[0],*(__m128i*)&nr_conjug2[0]); ++ bc_re_128 = _mm_madd_epi16(bc_re_128,after_mf_10_128[0]); //Re: I_b0*I_c0 - Q_b1*Q_c1 ++ //bc_im_128 = _mm_shufflelo_epi16(after_mf_01_128[0],_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_b0 Q_b1 I_b2 Q_b3]_64bits to [Q_b1 I_b0 Q_b3 I_b2]_64bits ++ //bc_im_128 = _mm_shufflehi_epi16(bc_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_b0 Q_b1 I_b2 Q_b3]_64bits to [Q_b1 I_b0 Q_b3 I_b2]_64bits ++ //bc_im_128 = _mm_madd_epi16(bc_im_128,after_mf_10_128[0]);//Im: (Q_bI_c + I_bQ_c) ++ ++ det_re_128 = _mm_sub_epi32(ad_re_128, bc_re_128); ++ //det_im_128 = _mm_sub_epi32(ad_im_128, bc_im_128); ++ ++ //det in Q30 format ++ det_fin_128[0] = _mm_abs_epi32(det_re_128); ++ ++ ++#ifdef DEBUG_DLSCH_DEMOD ++ printf("\n Computing det_HhH_inv \n"); ++ //print_ints("det_re_128:",(int32_t*)&det_re_128); ++ //print_ints("det_im_128:",(int32_t*)&det_im_128); ++ print_ints("det_fin_128:",(int32_t*)&det_fin_128[0]); ++#endif ++ det_fin_128+=1; ++ after_mf_00_128+=1; ++ after_mf_01_128+=1; ++ after_mf_10_128+=1; ++ after_mf_11_128+=1; ++ } ++ _mm_empty(); ++ _m_empty(); ++} ++ ++/* Zero Forcing Rx function: nr_inv_comp_muli ++ * Complex number multi: z = x*y ++ * = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) ++ * */ ++__m128i nr_ulsch_inv_comp_muli(__m128i input_x, ++ __m128i input_y) ++{ ++ int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; ++ ++ __m128i xy_re_128, xy_im_128; ++ __m128i output_z, tmp_z0, tmp_z1; ++ ++ // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) ++ ++ // the real part ++ xy_re_128 = _mm_sign_epi16(input_x,*(__m128i*)&nr_conjug2[0]); ++ xy_re_128 = _mm_madd_epi16(xy_re_128,input_y); //Re: (x_re*y_re - x_im*y_im) ++ ++ // the imag part ++ xy_im_128 = _mm_shufflelo_epi16(input_x,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ xy_im_128 = _mm_shufflehi_epi16(xy_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ xy_im_128 = _mm_madd_epi16(xy_im_128,input_y);//Im: (x_im*y_re + x_re*y_im) ++ ++ //convert back to Q15 before packing ++ xy_re_128 = _mm_srai_epi32(xy_re_128,4);//(2^15/64*2*16) ++ xy_im_128 = _mm_srai_epi32(xy_im_128,4); ++ ++ tmp_z0 = _mm_unpacklo_epi32(xy_re_128,xy_im_128); ++ //print_ints("unpack lo:",&tmp_z0[0]); ++ tmp_z1 = _mm_unpackhi_epi32(xy_re_128,xy_im_128); ++ //print_ints("unpack hi:",&tmp_z1[0]); ++ output_z = _mm_packs_epi32(tmp_z0,tmp_z1); ++ ++ _mm_empty(); ++ _m_empty(); ++ return(output_z); ++} ++ ++/* Zero Forcing Rx function: nr_conjch0_mult_ch1() ++ * ++ * ++ * */ ++void nr_ulsch_conjch0_mult_ch1(int *ch0, ++ int *ch1, ++ int32_t *ch0conj_ch1, ++ unsigned short nb_rb, ++ unsigned char output_shift0) ++{ ++ //This function is used to compute multiplications in H_hermitian * H matrix ++ short nr_conjugate[8]__attribute__((aligned(16))) = {-1,1,-1,1,-1,1,-1,1}; ++ unsigned short rb; ++ __m128i *dl_ch0_128,*dl_ch1_128, *ch0conj_ch1_128, mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3; ++ ++ dl_ch0_128 = (__m128i *)ch0; ++ dl_ch1_128 = (__m128i *)ch1; ++ ++ ch0conj_ch1_128 = (__m128i *)ch0conj_ch1; ++ ++ for (rb=0; rb<3*nb_rb; rb++) { ++ ++ mmtmpD0 = _mm_madd_epi16(dl_ch0_128[0],dl_ch1_128[0]); ++ mmtmpD1 = _mm_shufflelo_epi16(dl_ch0_128[0],_MM_SHUFFLE(2,3,0,1)); ++ mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); ++ mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)&nr_conjugate[0]); ++ mmtmpD1 = _mm_madd_epi16(mmtmpD1,dl_ch1_128[0]); ++ mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift0); ++ mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift0); ++ mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); ++ mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); ++ ++ ch0conj_ch1_128[0] = _mm_packs_epi32(mmtmpD2,mmtmpD3); ++ ++ /*printf("\n Computing conjugates \n"); ++ print_shorts("ch0:",(int16_t*)&dl_ch0_128[0]); ++ print_shorts("ch1:",(int16_t*)&dl_ch1_128[0]); ++ print_shorts("pack:",(int16_t*)&ch0conj_ch1_128[0]);*/ ++ ++ dl_ch0_128+=1; ++ dl_ch1_128+=1; ++ ch0conj_ch1_128+=1; ++ } ++ _mm_empty(); ++ _m_empty(); ++} ++__m128i nr_ulsch_comp_muli_sum(__m128i input_x, ++ __m128i input_y, ++ __m128i input_w, ++ __m128i input_z, ++ __m128i det) ++{ ++ int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; ++ ++ __m128i xy_re_128, xy_im_128, wz_re_128, wz_im_128; ++ __m128i output, tmp_z0, tmp_z1; ++ ++ // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) ++ // the real part ++ xy_re_128 = _mm_sign_epi16(input_x,*(__m128i*)&nr_conjug2[0]); ++ xy_re_128 = _mm_madd_epi16(xy_re_128,input_y); //Re: (x_re*y_re - x_im*y_im) ++ ++ // the imag part ++ xy_im_128 = _mm_shufflelo_epi16(input_x,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ xy_im_128 = _mm_shufflehi_epi16(xy_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ xy_im_128 = _mm_madd_epi16(xy_im_128,input_y);//Im: (x_im*y_re + x_re*y_im) ++ ++ // complex multiplication (w_re + jw_im)*(z_re + jz_im) = (w_re*z_re - w_im*z_im) + j(w_im*z_re + w_re*z_im) ++ // the real part ++ wz_re_128 = _mm_sign_epi16(input_w,*(__m128i*)&nr_conjug2[0]); ++ wz_re_128 = _mm_madd_epi16(wz_re_128,input_z); //Re: (w_re*z_re - w_im*z_im) ++ ++ // the imag part ++ wz_im_128 = _mm_shufflelo_epi16(input_w,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ wz_im_128 = _mm_shufflehi_epi16(wz_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits ++ wz_im_128 = _mm_madd_epi16(wz_im_128,input_z);//Im: (w_im*z_re + w_re*z_im) ++ ++ ++ xy_re_128 = _mm_sub_epi32(xy_re_128, wz_re_128); ++ xy_im_128 = _mm_sub_epi32(xy_im_128, wz_im_128); ++ //print_ints("rx_re:",(int32_t*)&xy_re_128[0]); ++ //print_ints("rx_Img:",(int32_t*)&xy_im_128[0]); ++ //divide by matrix det and convert back to Q15 before packing ++ int sum_det =0; ++ for (int k=0; k<4;k++) { ++ sum_det += ((((int *)&det[0])[k])>>2); ++ //printf("det_%d = %d log2 =%d \n",k,(((int *)&det[0])[k]),log2_approx(((int *)&det[0])[k])); ++ } ++ ++ xy_re_128 = _mm_slli_epi32(xy_re_128,5); ++ xy_re_128 = _mm_srai_epi32(xy_re_128,log2_approx(sum_det)); ++ xy_re_128 = _mm_slli_epi32(xy_re_128,5); ++ ++ xy_im_128 = _mm_slli_epi32(xy_im_128,5); ++ xy_im_128 = _mm_srai_epi32(xy_im_128,log2_approx(sum_det)); ++ xy_im_128 = _mm_slli_epi32(xy_im_128,5); ++ ++ tmp_z0 = _mm_unpacklo_epi32(xy_re_128,xy_im_128); ++ //print_ints("unpack lo:",&tmp_z0[0]); ++ tmp_z1 = _mm_unpackhi_epi32(xy_re_128,xy_im_128); ++ //print_ints("unpack hi:",&tmp_z1[0]); ++ output = _mm_packs_epi32(tmp_z0,tmp_z1); ++ ++ _mm_empty(); ++ _m_empty(); ++ return(output); ++} ++/* Zero Forcing Rx function: nr_construct_HhH_elements() ++ * ++ * ++ * */ ++void nr_ulsch_construct_HhH_elements(int *conjch00_ch00, ++ int *conjch01_ch01, ++ int *conjch11_ch11, ++ int *conjch10_ch10,// ++ int *conjch20_ch20, ++ int *conjch21_ch21, ++ int *conjch30_ch30, ++ int *conjch31_ch31, ++ int *conjch00_ch01,//00_01 ++ int *conjch01_ch00,//01_00 ++ int *conjch10_ch11,//10_11 ++ int *conjch11_ch10,//11_10 ++ int *conjch20_ch21, ++ int *conjch21_ch20, ++ int *conjch30_ch31, ++ int *conjch31_ch30, ++ int32_t *after_mf_00, ++ int32_t *after_mf_01, ++ int32_t *after_mf_10, ++ int32_t *after_mf_11, ++ unsigned short nb_rb, ++ unsigned char symbol) ++{ ++ //This function is used to construct the (H_hermitian * H matrix) matrix elements ++ unsigned short rb; ++ __m128i *conjch00_ch00_128, *conjch01_ch01_128, *conjch11_ch11_128, *conjch10_ch10_128; ++ __m128i *conjch20_ch20_128, *conjch21_ch21_128, *conjch30_ch30_128, *conjch31_ch31_128; ++ __m128i *conjch00_ch01_128, *conjch01_ch00_128, *conjch10_ch11_128, *conjch11_ch10_128; ++ __m128i *conjch20_ch21_128, *conjch21_ch20_128, *conjch30_ch31_128, *conjch31_ch30_128; ++ __m128i *after_mf_00_128, *after_mf_01_128, *after_mf_10_128, *after_mf_11_128; ++ ++ conjch00_ch00_128 = (__m128i *)conjch00_ch00; ++ conjch01_ch01_128 = (__m128i *)conjch01_ch01; ++ conjch11_ch11_128 = (__m128i *)conjch11_ch11; ++ conjch10_ch10_128 = (__m128i *)conjch10_ch10; ++ ++ conjch20_ch20_128 = (__m128i *)conjch20_ch20; ++ conjch21_ch21_128 = (__m128i *)conjch21_ch21; ++ conjch30_ch30_128 = (__m128i *)conjch30_ch30; ++ conjch31_ch31_128 = (__m128i *)conjch31_ch31; ++ ++ conjch00_ch01_128 = (__m128i *)conjch00_ch01; ++ conjch01_ch00_128 = (__m128i *)conjch01_ch00; ++ conjch10_ch11_128 = (__m128i *)conjch10_ch11; ++ conjch11_ch10_128 = (__m128i *)conjch11_ch10; ++ ++ conjch20_ch21_128 = (__m128i *)conjch20_ch21; ++ conjch21_ch20_128 = (__m128i *)conjch21_ch20; ++ conjch30_ch31_128 = (__m128i *)conjch30_ch31; ++ conjch31_ch30_128 = (__m128i *)conjch31_ch30; ++ ++ after_mf_00_128 = (__m128i *)after_mf_00; ++ after_mf_01_128 = (__m128i *)after_mf_01; ++ after_mf_10_128 = (__m128i *)after_mf_10; ++ after_mf_11_128 = (__m128i *)after_mf_11; ++ ++ for (rb=0; rb<3*nb_rb; rb++) { ++ ++ after_mf_00_128[0] =_mm_adds_epi16(conjch00_ch00_128[0],conjch10_ch10_128[0]);//00_00 + 10_10 ++ if (conjch20_ch20 != NULL) after_mf_00_128[0] =_mm_adds_epi16(after_mf_00_128[0],conjch20_ch20_128[0]); ++ if (conjch30_ch30 != NULL) after_mf_00_128[0] =_mm_adds_epi16(after_mf_00_128[0],conjch30_ch30_128[0]); ++ ++ after_mf_11_128[0] =_mm_adds_epi16(conjch01_ch01_128[0], conjch11_ch11_128[0]); //01_01 + 11_11 ++ if (conjch21_ch21 != NULL) after_mf_11_128[0] =_mm_adds_epi16(after_mf_11_128[0],conjch21_ch21_128[0]); ++ if (conjch31_ch31 != NULL) after_mf_11_128[0] =_mm_adds_epi16(after_mf_11_128[0],conjch31_ch31_128[0]); ++ ++ after_mf_01_128[0] =_mm_adds_epi16(conjch00_ch01_128[0], conjch10_ch11_128[0]);//00_01 + 10_11 ++ if (conjch20_ch21 != NULL) after_mf_01_128[0] =_mm_adds_epi16(after_mf_01_128[0],conjch20_ch21_128[0]); ++ if (conjch30_ch31 != NULL) after_mf_01_128[0] =_mm_adds_epi16(after_mf_01_128[0],conjch30_ch31_128[0]); ++ ++ after_mf_10_128[0] =_mm_adds_epi16(conjch01_ch00_128[0], conjch11_ch10_128[0]);//01_00 + 11_10 ++ if (conjch21_ch20 != NULL) after_mf_10_128[0] =_mm_adds_epi16(after_mf_10_128[0],conjch21_ch20_128[0]); ++ if (conjch31_ch30 != NULL) after_mf_10_128[0] =_mm_adds_epi16(after_mf_10_128[0],conjch31_ch30_128[0]); ++ ++#ifdef DEBUG_DLSCH_DEMOD ++ if ((rb<=30)) ++ { ++ printf(" \n construct_HhH_elements \n"); ++ print_shorts("after_mf_00_128:",(int16_t*)&after_mf_00_128[0]); ++ print_shorts("after_mf_01_128:",(int16_t*)&after_mf_01_128[0]); ++ print_shorts("after_mf_10_128:",(int16_t*)&after_mf_10_128[0]); ++ print_shorts("after_mf_11_128:",(int16_t*)&after_mf_11_128[0]); ++ } ++#endif ++ conjch00_ch00_128+=1; ++ conjch10_ch10_128+=1; ++ conjch01_ch01_128+=1; ++ conjch11_ch11_128+=1; ++ ++ if (conjch20_ch20 != NULL) conjch20_ch20_128+=1; ++ if (conjch21_ch21 != NULL) conjch21_ch21_128+=1; ++ if (conjch30_ch30 != NULL) conjch30_ch30_128+=1; ++ if (conjch31_ch31 != NULL) conjch31_ch31_128+=1; ++ ++ conjch00_ch01_128+=1; ++ conjch01_ch00_128+=1; ++ conjch10_ch11_128+=1; ++ conjch11_ch10_128+=1; ++ ++ if (conjch20_ch21 != NULL) conjch20_ch21_128+=1; ++ if (conjch21_ch20 != NULL) conjch21_ch20_128+=1; ++ if (conjch30_ch31 != NULL) conjch30_ch31_128+=1; ++ if (conjch31_ch30 != NULL) conjch31_ch30_128+=1; ++ ++ after_mf_00_128 += 1; ++ after_mf_01_128 += 1; ++ after_mf_10_128 += 1; ++ after_mf_11_128 += 1; ++ } ++ _mm_empty(); ++ _m_empty(); ++} ++ ++/* Zero Forcing Rx function: nr_ulsch_zero_forcing_rx_2layers() ++ * ++ * ++ * */ ++uint8_t nr_ulsch_zero_forcing_rx_2layers(int **rxdataF_comp, ++ int **ul_ch_mag, ++ int **ul_ch_magb, ++ int **ul_ch_estimates_ext, ++ unsigned short nb_rb, ++ unsigned char n_rx, ++ unsigned char mod_order, ++ int shift, ++ unsigned char symbol, ++ int length) ++{ ++ int *ch00, *ch01, *ch10, *ch11; ++ int *ch20, *ch30, *ch21, *ch31; ++ uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); ++ ++ #ifdef __AVX2__ ++ int off = ((nb_rb&1) == 1)? 4:0; ++ #else ++ int off = 0; ++ #endif ++ ++ /* we need at least alignment to 16 bytes, let's put 32 to be sure ++ * (maybe not necessary but doesn't hurt) ++ */ ++ int32_t conjch00_ch01[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch01_ch00[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch10_ch11[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch11_ch10[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch00_ch00[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch01_ch01[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch10_ch10[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch11_ch11[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch20_ch20[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch21_ch21[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch30_ch30[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch31_ch31[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch20_ch21[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch30_ch31[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch21_ch20[12*nb_rb] __attribute__((aligned(32))); ++ int32_t conjch31_ch30[12*nb_rb] __attribute__((aligned(32))); ++ ++ int32_t af_mf_00[12*nb_rb] __attribute__((aligned(32))); ++ int32_t af_mf_01[12*nb_rb] __attribute__((aligned(32))); ++ int32_t af_mf_10[12*nb_rb] __attribute__((aligned(32))); ++ int32_t af_mf_11[12*nb_rb] __attribute__((aligned(32))); ++ int32_t determ_fin[12*nb_rb] __attribute__((aligned(32))); ++ ++ switch (n_rx) { ++ case 2:// ++ ch00 = (int *)&ul_ch_estimates_ext[0][symbol*(off+nb_rb*12)]; ++ ch01 = (int *)&ul_ch_estimates_ext[2][symbol*(off+nb_rb*12)]; ++ ch10 = (int *)&ul_ch_estimates_ext[1][symbol*(off+nb_rb*12)]; ++ ch11 = (int *)&ul_ch_estimates_ext[3][symbol*(off+nb_rb*12)]; ++ ch20 = NULL; ++ ch21 = NULL; ++ ch30 = NULL; ++ ch31 = NULL; ++ break; ++ ++ case 4:// ++ ch00 = (int *)&ul_ch_estimates_ext[0][symbol*(off+nb_rb*12)]; ++ ch01 = (int *)&ul_ch_estimates_ext[4][symbol*(off+nb_rb*12)]; ++ ch10 = (int *)&ul_ch_estimates_ext[1][symbol*(off+nb_rb*12)]; ++ ch11 = (int *)&ul_ch_estimates_ext[5][symbol*(off+nb_rb*12)]; ++ ch20 = (int *)&ul_ch_estimates_ext[2][symbol*(off+nb_rb*12)]; ++ ch21 = (int *)&ul_ch_estimates_ext[6][symbol*(off+nb_rb*12)]; ++ ch30 = (int *)&ul_ch_estimates_ext[3][symbol*(off+nb_rb*12)]; ++ ch31 = (int *)&ul_ch_estimates_ext[7][symbol*(off+nb_rb*12)]; ++ break; ++ ++ default: ++ return -1; ++ break; ++ } ++ ++ /* 1- Compute the rx channel matrix after compensation: (1/2^log2_max)x(H_herm x H) ++ * for n_rx = 2 ++ * |conj_H_00 conj_H_10| | H_00 H_01| |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| ++ * | | x | | = | | ++ * |conj_H_01 conj_H_11| | H_10 H_11| |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| ++ * ++ */ ++ ++ if (n_rx>=2){ ++ // (1/2^log2_maxh)*conj_H_00xH_00: (1/(64*2))conjH_00*H_00*2^15 ++ nr_ulsch_conjch0_mult_ch1(ch00, ++ ch00, ++ conjch00_ch00, ++ nb_rb_0, ++ shift); ++ // (1/2^log2_maxh)*conj_H_10xH_10: (1/(64*2))conjH_10*H_10*2^15 ++ nr_ulsch_conjch0_mult_ch1(ch10, ++ ch10, ++ conjch10_ch10, ++ nb_rb_0, ++ shift); ++ // conj_H_00xH_01 ++ nr_ulsch_conjch0_mult_ch1(ch00, ++ ch01, ++ conjch00_ch01, ++ nb_rb_0, ++ shift); // this shift is equal to the channel level log2_maxh ++ // conj_H_10xH_11 ++ nr_ulsch_conjch0_mult_ch1(ch10, ++ ch11, ++ conjch10_ch11, ++ nb_rb_0, ++ shift); ++ // conj_H_01xH_01 ++ nr_ulsch_conjch0_mult_ch1(ch01, ++ ch01, ++ conjch01_ch01, ++ nb_rb_0, ++ shift); ++ // conj_H_11xH_11 ++ nr_ulsch_conjch0_mult_ch1(ch11, ++ ch11, ++ conjch11_ch11, ++ nb_rb_0, ++ shift); ++ // conj_H_01xH_00 ++ nr_ulsch_conjch0_mult_ch1(ch01, ++ ch00, ++ conjch01_ch00, ++ nb_rb_0, ++ shift); ++ // conj_H_11xH_10 ++ nr_ulsch_conjch0_mult_ch1(ch11, ++ ch10, ++ conjch11_ch10, ++ nb_rb_0, ++ shift); ++ } ++ if (n_rx==4){ ++ // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2*16))conjH_20*H_20*2^15 ++ nr_ulsch_conjch0_mult_ch1(ch20, ++ ch20, ++ conjch20_ch20, ++ nb_rb_0, ++ shift); ++ ++ // (1/2^log2_maxh)*conj_H_30xH_30: (1/(64*2*4))conjH_30*H_30*2^15 ++ nr_ulsch_conjch0_mult_ch1(ch30, ++ ch30, ++ conjch30_ch30, ++ nb_rb_0, ++ shift); ++ ++ // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 ++ nr_ulsch_conjch0_mult_ch1(ch20, ++ ch21, ++ conjch20_ch21, ++ nb_rb_0, ++ shift); ++ ++ nr_ulsch_conjch0_mult_ch1(ch30, ++ ch31, ++ conjch30_ch31, ++ nb_rb_0, ++ shift); ++ ++ nr_ulsch_conjch0_mult_ch1(ch21, ++ ch21, ++ conjch21_ch21, ++ nb_rb_0, ++ shift); ++ ++ nr_ulsch_conjch0_mult_ch1(ch31, ++ ch31, ++ conjch31_ch31, ++ nb_rb_0, ++ shift); ++ ++ // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 ++ nr_ulsch_conjch0_mult_ch1(ch21, ++ ch20, ++ conjch21_ch20, ++ nb_rb_0, ++ shift); ++ ++ nr_ulsch_conjch0_mult_ch1(ch31, ++ ch30, ++ conjch31_ch30, ++ nb_rb_0, ++ shift); ++ ++ nr_ulsch_construct_HhH_elements(conjch00_ch00, ++ conjch01_ch01, ++ conjch11_ch11, ++ conjch10_ch10,// ++ conjch20_ch20, ++ conjch21_ch21, ++ conjch30_ch30, ++ conjch31_ch31, ++ conjch00_ch01, ++ conjch01_ch00, ++ conjch10_ch11, ++ conjch11_ch10,// ++ conjch20_ch21, ++ conjch21_ch20, ++ conjch30_ch31, ++ conjch31_ch30, ++ af_mf_00, ++ af_mf_01, ++ af_mf_10, ++ af_mf_11, ++ nb_rb_0, ++ symbol); ++ } ++ if (n_rx==2){ ++ nr_ulsch_construct_HhH_elements(conjch00_ch00, ++ conjch01_ch01, ++ conjch11_ch11, ++ conjch10_ch10,// ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ conjch00_ch01, ++ conjch01_ch00, ++ conjch10_ch11, ++ conjch11_ch10,// ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ af_mf_00, ++ af_mf_01, ++ af_mf_10, ++ af_mf_11, ++ nb_rb_0, ++ symbol); ++ } ++ //det_HhH = ad -bc ++ nr_ulsch_det_HhH(af_mf_00,//a ++ af_mf_01,//b ++ af_mf_10,//c ++ af_mf_11,//d ++ determ_fin, ++ nb_rb_0, ++ symbol, ++ shift); ++ /* 2- Compute the channel matrix inversion ********************************** ++ * ++ * |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| ++ * A= | | ++ * |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| ++ * ++ * ++ * ++ *inv(A) =(1/det)*[d -b ++ * -c a] ++ * ++ * ++ **************************************************************************/ ++ __m128i *rxdataF_comp128_0,*rxdataF_comp128_1,*ul_ch_mag128_0=NULL,*ul_ch_mag128b_0=NULL,*determ_fin_128;//*dl_ch_mag128_1,*dl_ch_mag128b_1,*dl_ch_mag128r_1 ++ __m128i mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3; ++ __m128i *after_mf_a_128,*after_mf_b_128, *after_mf_c_128, *after_mf_d_128; ++ __m128i QAM_amp128={0},QAM_amp128b={0}; ++ ++ determ_fin_128 = (__m128i *)&determ_fin[0]; ++ ++ rxdataF_comp128_0 = (__m128i *)&rxdataF_comp[0][symbol*(off+nb_rb*12)];//aatx=0 @ aarx =0 ++ rxdataF_comp128_1 = (__m128i *)&rxdataF_comp[n_rx][symbol*(off+nb_rb*12)];//aatx=1 @ aarx =0 ++ ++ after_mf_a_128 = (__m128i *)af_mf_00; ++ after_mf_b_128 = (__m128i *)af_mf_01; ++ after_mf_c_128 = (__m128i *)af_mf_10; ++ after_mf_d_128 = (__m128i *)af_mf_11; ++ ++ if (mod_order>2) { ++ if (mod_order == 4) { ++ QAM_amp128 = _mm_set1_epi16(QAM16_n1); //2/sqrt(10) ++ QAM_amp128b = _mm_setzero_si128(); ++ } else if (mod_order == 6) { ++ QAM_amp128 = _mm_set1_epi16(QAM64_n1); //4/sqrt{42} ++ QAM_amp128b = _mm_set1_epi16(QAM64_n2); //2/sqrt{42} ++ } ++ ul_ch_mag128_0 = (__m128i *)&ul_ch_mag[0][symbol*(off+nb_rb*12)]; ++ ul_ch_mag128b_0 = (__m128i *)&ul_ch_magb[0][symbol*(off+nb_rb*12)]; ++ } ++ ++ for (int rb=0; rb<3*nb_rb_0; rb++) { ++ if (mod_order>2) { ++ int sum_det =0; ++ for (int k=0; k<4;k++) { ++ sum_det += ((((int *)&determ_fin_128[0])[k])>>2); ++ //printf("det_%d = %d\n",k,sum_det); ++ } ++ ++ mmtmpD2 = _mm_slli_epi32(determ_fin_128[0],5); ++ mmtmpD2 = _mm_srai_epi32(mmtmpD2,log2_approx(sum_det)); ++ mmtmpD2 = _mm_slli_epi32(mmtmpD2,5); ++ ++ mmtmpD3 = _mm_unpacklo_epi32(mmtmpD2,mmtmpD2); ++ ++ mmtmpD2 = _mm_unpackhi_epi32(mmtmpD2,mmtmpD2); ++ ++ mmtmpD2 = _mm_packs_epi32(mmtmpD3,mmtmpD2); ++ ++ ul_ch_mag128_0[0] = mmtmpD2; ++ ul_ch_mag128b_0[0] = mmtmpD2; ++ ++ ul_ch_mag128_0[0] = _mm_mulhi_epi16(ul_ch_mag128_0[0],QAM_amp128); ++ ul_ch_mag128_0[0] = _mm_slli_epi16(ul_ch_mag128_0[0],1); ++ ++ ul_ch_mag128b_0[0] = _mm_mulhi_epi16(ul_ch_mag128b_0[0],QAM_amp128b); ++ ul_ch_mag128b_0[0] = _mm_slli_epi16(ul_ch_mag128b_0[0],1); ++ ++ //print_shorts("mag layer 1:",(int16_t*)&dl_ch_mag128_0[0]); ++ //print_shorts("mag layer 2:",(int16_t*)&dl_ch_mag128_1[0]); ++ //print_shorts("magb layer 1:",(int16_t*)&dl_ch_mag128b_0[0]); ++ //print_shorts("magb layer 2:",(int16_t*)&dl_ch_mag128b_1[0]); ++ //print_shorts("magr layer 1:",(int16_t*)&dl_ch_mag128r_0[0]); ++ //print_shorts("magr layer 2:",(int16_t*)&dl_ch_mag128r_1[0]); ++ } ++ // multiply by channel Inv ++ //rxdataF_zf128_0 = rxdataF_comp128_0*d - b*rxdataF_comp128_1 ++ //rxdataF_zf128_1 = rxdataF_comp128_1*a - c*rxdataF_comp128_0 ++ //printf("layer_1 \n"); ++ mmtmpD0 = nr_ulsch_comp_muli_sum(rxdataF_comp128_0[0], ++ after_mf_d_128[0], ++ rxdataF_comp128_1[0], ++ after_mf_b_128[0], ++ determ_fin_128[0]); ++ ++ //printf("layer_2 \n"); ++ mmtmpD1 = nr_ulsch_comp_muli_sum(rxdataF_comp128_1[0], ++ after_mf_a_128[0], ++ rxdataF_comp128_0[0], ++ after_mf_c_128[0], ++ determ_fin_128[0]); ++ ++ rxdataF_comp128_0[0] = mmtmpD0; ++ rxdataF_comp128_1[0] = mmtmpD1; ++#ifdef DEBUG_DLSCH_DEMOD ++ printf("\n Rx signal after ZF l%d rb%d\n",symbol,rb); ++ print_shorts(" Rx layer 1:",(int16_t*)&rxdataF_comp128_0[0]); ++ print_shorts(" Rx layer 2:",(int16_t*)&rxdataF_comp128_1[0]); ++#endif ++ determ_fin_128 += 1; ++ ul_ch_mag128_0 += 1; ++ ul_ch_mag128b_0 += 1; ++ rxdataF_comp128_0 += 1; ++ rxdataF_comp128_1 += 1; ++ after_mf_a_128 += 1; ++ after_mf_b_128 += 1; ++ after_mf_c_128 += 1; ++ after_mf_d_128 += 1; ++ } ++ _mm_empty(); ++ _m_empty(); ++ return(0); ++} ++ ++//============================================================================================== ++ ++/* Main Function */ + int nr_rx_pusch(PHY_VARS_gNB *gNB, + uint8_t ulsch_id, + uint32_t frame, +@@ -1184,21 +1969,29 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + gNB->pusch_vars[ulsch_id]->cl_done = 0; + + bwp_start_subcarrier = ((rel15_ul->rb_start + rel15_ul->bwp_start)*NR_NB_SC_PER_RB + frame_parms->first_carrier_offset) % frame_parms->ofdm_symbol_size; +- LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); +- LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); +- ++ LOG_I(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); ++ LOG_I(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); ++ LOG_I(PHY,"ulsch RX %x : start_rb %d nb_rb %d mcs %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_ports %d\n", ++ rel15_ul->rnti,rel15_ul->rb_start,rel15_ul->rb_size,rel15_ul->mcs_index, ++ rel15_ul->nrOfLayers,0,rel15_ul->bwp_start,0,rel15_ul->start_symbol_index,rel15_ul->nr_of_symbols, ++ rel15_ul->num_dmrs_cdm_grps_no_data,rel15_ul->ul_dmrs_symb_pos,rel15_ul->dmrs_ports); + //---------------------------------------------------------- + //--------------------- Channel estimation --------------------- + //---------------------------------------------------------- + start_meas(&gNB->ulsch_channel_estimation_stats); +- for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { ++ for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) ++ { + uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; + LOG_D(PHY, "symbol %d, dmrs_symbol_flag :%d\n", symbol, dmrs_symbol_flag); +- if (dmrs_symbol_flag == 1) { ++ ++ if (dmrs_symbol_flag == 1) ++ { + if (gNB->pusch_vars[ulsch_id]->dmrs_symbol == INVALID_VALUE) + gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; + + for (int nl=0; nlnrOfLayers; nl++) ++ { ++ + nr_pusch_channel_estimation(gNB, + slot, + get_dmrs_port(nl,rel15_ul->dmrs_ports), +@@ -1206,6 +1999,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + ulsch_id, + bwp_start_subcarrier, + rel15_ul); ++ } + + nr_gnb_measurements(gNB, ulsch_id, harq_pid, symbol,rel15_ul->nrOfLayers); + +@@ -1214,9 +2008,12 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] = 0; + gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] = 0; + } +- gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( +- &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aarx][symbol * frame_parms->ofdm_symbol_size], ++ for (aatx = 0; aatx < rel15_ul->nrOfLayers; aatx++) ++ { ++ gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( ++ &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aatx*gNB->frame_parms.nb_antennas_rx+aarx][symbol * frame_parms->ofdm_symbol_size], + rel15_ul->rb_size * 12); ++ } + for (int rb = 0; rb < rel15_ul->rb_size; rb++) { + gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] += + gNB->measurements.n0_subband_power[aarx][rel15_ul->bwp_start + rel15_ul->rb_start + rb] / +@@ -1234,23 +2031,29 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + #endif + uint32_t rxdataF_ext_offset = 0; + +- for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { ++ for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) ++ { + uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; +- if (dmrs_symbol_flag == 1) { ++ if (dmrs_symbol_flag == 1) ++ { + if ((rel15_ul->ul_dmrs_symb_pos >> ((symbol + 1) % frame_parms->symbols_per_slot)) & 0x01) + AssertFatal(1==0,"Double DMRS configuration is not yet supported\n"); + + gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; + +- if (rel15_ul->dmrs_config_type == 0) { ++ if (rel15_ul->dmrs_config_type == 0) ++ { + // if no data in dmrs cdm group is 1 only even REs have no data + // if no data in dmrs cdm group is 2 both odd and even REs have no data + nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*6)); + } +- else { ++ else ++ { + nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*4)); + } +- } else { ++ } ++ else ++ { + nb_re_pusch = rel15_ul->rb_size * NR_NB_SC_PER_RB; + } + +@@ -1260,16 +2063,16 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + //---------------------------------------------------------- + //--------------------- RBs extraction --------------------- + //---------------------------------------------------------- +- if (nb_re_pusch > 0) { +- ++ if (nb_re_pusch > 0) ++ { + start_meas(&gNB->ulsch_rbs_extraction_stats); +- nr_ulsch_extract_rbs_single(gNB->common_vars.rxdataF, +- gNB->pusch_vars[ulsch_id], +- slot, +- symbol, +- dmrs_symbol_flag, +- rel15_ul, +- frame_parms); ++ nr_ulsch_extract_rbs(gNB->common_vars.rxdataF, ++ gNB->pusch_vars[ulsch_id], ++ slot, ++ symbol, ++ dmrs_symbol_flag, ++ rel15_ul, ++ frame_parms); + stop_meas(&gNB->ulsch_rbs_extraction_stats); + + //---------------------------------------------------------- +@@ -1280,8 +2083,9 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + gNB->ulsch[ulsch_id], + symbol, + dmrs_symbol_flag, +- rel15_ul->rb_size, +- rel15_ul->dmrs_config_type); ++ nb_re_pusch, ++ rel15_ul->nrOfLayers, ++ rel15_ul->rb_size); + + if (gNB->pusch_vars[ulsch_id]->cl_done==0) { + nr_ulsch_channel_level(gNB->pusch_vars[ulsch_id]->ul_ch_estimates_ext, +@@ -1312,9 +2116,14 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + gNB->pusch_vars[ulsch_id]->ul_ch_mag0, + gNB->pusch_vars[ulsch_id]->ul_ch_magb0, + gNB->pusch_vars[ulsch_id]->rxdataF_comp, ++ #ifdef SUPPORT_PMI_MATRIC + (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, ++ #else ++ NULL, ++ #endif + frame_parms, + symbol, ++ nb_re_pusch, + dmrs_symbol_flag, + rel15_ul->qam_mod_order, + rel15_ul->nrOfLayers, +@@ -1327,19 +2136,37 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + gNB->pusch_vars[ulsch_id]->rxdataF_comp, + gNB->pusch_vars[ulsch_id]->ul_ch_mag0, + gNB->pusch_vars[ulsch_id]->ul_ch_magb0, ++ #ifdef SUPPORT_PMI_MATRIC ++ (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, ++ #else ++ NULL, ++ #endif ++ rel15_ul->nrOfLayers, + symbol, +- rel15_ul->rb_size); ++ rel15_ul->rb_size, ++ nb_re_pusch); ++ ++ if (rel15_ul->nrOfLayers == 2)//Apply zero forcing for 2 Tx layers ++ nr_ulsch_zero_forcing_rx_2layers(gNB->pusch_vars[ulsch_id]->rxdataF_comp, ++ gNB->pusch_vars[ulsch_id]->ul_ch_mag0, ++ gNB->pusch_vars[ulsch_id]->ul_ch_magb0, ++ gNB->pusch_vars[ulsch_id]->ul_ch_estimates_ext, ++ rel15_ul->rb_size, ++ frame_parms->nb_antennas_rx, ++ rel15_ul->qam_mod_order, ++ gNB->pusch_vars[ulsch_id]->log2_maxh, ++ symbol, ++ nb_re_pusch); + stop_meas(&gNB->ulsch_mrc_stats); + +- // transform precoding = 0 means enabled +- if (rel15_ul->transform_precoding == 0) { +- +- #ifdef __AVX2__ ++ if (rel15_ul->transformPrecoder == transformPrecoder_enabled) ++ { ++ #ifdef __AVX2__ + // For odd number of resource blocks need byte alignment to multiple of 8 + int nb_re_pusch2 = nb_re_pusch + (nb_re_pusch&7); +- #else ++ #else + int nb_re_pusch2 = nb_re_pusch; +- #endif ++ #endif + + // perform IDFT operation on the compensated rxdata if transform precoding is enabled + nr_idft(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[0][symbol * nb_re_pusch2], nb_re_pusch); +@@ -1351,7 +2178,8 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + //---------------------------------------------------------- + /* In case PTRS is enabled then LLR will be calculated after PTRS symbols are processed * + * otherwise LLR are calculated for each symbol based upon DMRS channel estimates only. */ +- if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { ++ if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) ++ { + start_meas(&gNB->ulsch_ptrs_processing_stats); + nr_pusch_ptrs_processing(gNB, + frame_parms, +@@ -1370,14 +2198,17 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, + /*-------------------- LLRs computation -------------------------------------------------------------*/ + /*-----------------------------------------------------------------------------------------------------*/ + start_meas(&gNB->ulsch_llr_stats); +- nr_ulsch_compute_llr(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[0][symbol * (off + rel15_ul->rb_size * NR_NB_SC_PER_RB)], +- gNB->pusch_vars[ulsch_id]->ul_ch_mag0, +- gNB->pusch_vars[ulsch_id]->ul_ch_magb0, +- &gNB->pusch_vars[ulsch_id]->llr[rxdataF_ext_offset * rel15_ul->qam_mod_order], +- rel15_ul->rb_size, +- gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol], +- symbol, +- rel15_ul->qam_mod_order); ++ for (aatx=0; aatx < rel15_ul->nrOfLayers; aatx++) ++ { ++ nr_ulsch_compute_llr(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[aatx*frame_parms->nb_antennas_rx][symbol * (off + rel15_ul->rb_size * NR_NB_SC_PER_RB)], ++ gNB->pusch_vars[ulsch_id]->ul_ch_mag0[aatx*frame_parms->nb_antennas_rx], ++ gNB->pusch_vars[ulsch_id]->ul_ch_magb0[aatx*frame_parms->nb_antennas_rx], ++ &gNB->pusch_vars[ulsch_id]->llr_layers[aatx][rxdataF_ext_offset * rel15_ul->qam_mod_order], ++ rel15_ul->rb_size, ++ gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol], ++ symbol, ++ rel15_ul->qam_mod_order); ++ } + stop_meas(&gNB->ulsch_llr_stats); + rxdataF_ext_offset += gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol]; + } +diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c +index bf96a76106..ad41ebf3d6 100644 +--- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c ++++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c +@@ -64,7 +64,7 @@ void nr_ulsch_qpsk_llr(int32_t *rxdataF_comp, + //---------------------------------------------------------------------------------------------- + + void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, +- int32_t **ul_ch_mag, ++ int32_t *ul_ch_mag, + int16_t *ulsch_llr, + uint32_t nb_rb, + uint32_t nb_re, +@@ -110,12 +110,12 @@ void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, + + #if defined(__x86_64__) || defined(__i386__) + #ifdef __AVX2__ +- ch_mag = (__m256i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; ++ ch_mag = (__m256i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; + #else +- ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; ++ ch_mag = (__m128i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; + #endif + #elif defined(__arm__) +- ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*nb_rb*12)]; ++ ch_mag = (int16x8_t*)&ul_ch_mag[(symbol*nb_rb*12)]; + #endif + + #ifdef __AVX2__ +@@ -231,8 +231,8 @@ void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, + //---------------------------------------------------------------------------------------------- + + void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, +- int32_t **ul_ch_mag, +- int32_t **ul_ch_magb, ++ int32_t *ul_ch_mag, ++ int32_t *ul_ch_magb, + int16_t *ulsch_llr, + uint32_t nb_rb, + uint32_t nb_re, +@@ -265,15 +265,15 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, + + #if defined(__x86_64__) || defined(__i386__) + #ifdef __AVX2__ +- ch_mag = (__m256i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; +- ch_magb = (__m256i*)&ul_ch_magb[0][(symbol*(off+(nb_rb*12)))]; ++ ch_mag = (__m256i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; ++ ch_magb = (__m256i*)&ul_ch_magb[(symbol*(off+(nb_rb*12)))]; + #else +- ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*nb_rb*12)]; +- ch_magb = (__m128i*)&ul_ch_magb[0][(symbol*nb_rb*12)]; ++ ch_mag = (__m128i*)&ul_ch_mag[(symbol*nb_rb*12)]; ++ ch_magb = (__m128i*)&ul_ch_magb[(symbol*nb_rb*12)]; + #endif + #elif defined(__arm__) +- ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*nb_rb*12)]; +- ch_magb = (int16x8_t*)&ul_ch_magb[0][(symbol*nb_rb*12)]; ++ ch_mag = (int16x8_t*)&ul_ch_mag[(symbol*nb_rb*12)]; ++ ch_magb = (int16x8_t*)&ul_ch_magb[(symbol*nb_rb*12)]; + #endif + + #ifdef __AVX2__ +@@ -471,8 +471,8 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, + + + void nr_ulsch_compute_llr(int32_t *rxdataF_comp, +- int32_t **ul_ch_mag, +- int32_t **ul_ch_magb, ++ int32_t *ul_ch_mag, ++ int32_t *ul_ch_magb, + int16_t *ulsch_llr, + uint32_t nb_rb, + uint32_t nb_re, +diff --git a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c +index 65ae288252..2cb7609d62 100644 +--- a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c ++++ b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c +@@ -148,7 +148,7 @@ short filt8_dcr0_h[8]= { + 0,4096,8192,12288,16384,0,0,0}; + + short filt8_l1[8] = { +-24576,16384,0,0,0,0,0,0}; ++24576,16384,8192,0,0,0,0,0}; + + short filt8_ml1[8] = { + -8192,0,8192,16384,8192,0,0,0}; +@@ -163,10 +163,10 @@ short filt8_mm1[8]= { + 0,0,0,0,8192,16384,8192,0}; + + short filt8_dcl1[8]= { +-0,0,0,16384,12288,8192,4096,0}; ++0,0,16384,12288,8192,4096,0,0}; + + short filt8_dcr1[8]= { +-0,0,0,0,4096,8192,12288,16384}; ++0,0,0,4096,8192,12288,16384,0}; + + short filt8_dcl1_h[8]= { + 0,16384,12288,8192,4096,0,0,0}; +diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +index 945dc51673..a293eec6d8 100644 +--- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c ++++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +@@ -258,6 +258,15 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, + Ilbrm = 0; + Tbslbrm = 950984; //max tbs + Coderate = 0.0; ++ ++ #if 0 ++ harq_process->a[0] = 0x31; ++ for (int i = 1; i < harq_process->pusch_pdu.pusch_data.tb_size; i++) ++ { ++ harq_process->a[i] = (i&0xff); ++ } ++ #endif ++ + trace_NRpdu(DIRECTION_UPLINK, harq_process->a, harq_process->pusch_pdu.pusch_data.tb_size, 0, WS_C_RNTI, 0, 0, 0,0, 0); + /////////// + ///////////////////////////////////////////////////////////////////////////////////////// +diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +index 99dc2b658e..5d76e654ba 100644 +--- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c ++++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +@@ -98,13 +98,12 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, + uint8_t thread_id, + int gNB_id) { + +- LOG_D(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); ++ LOG_I(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); + + uint32_t available_bits; + uint8_t cwd_index, l; + uint32_t scrambled_output[NR_MAX_NB_CODEWORDS][NR_MAX_PDSCH_ENCODED_LENGTH>>5]; +- int16_t **tx_layers; +- int32_t **txdataF; ++ + int8_t Wf[2], Wt[2], l_prime[2], delta; + uint8_t nb_dmrs_re_per_rb; + int ap, i; +@@ -112,6 +111,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, + + NR_DL_FRAME_PARMS *frame_parms = &UE->frame_parms; + NR_UE_PUSCH *pusch_ue = UE->pusch_vars[thread_id][gNB_id]; ++ int16_t **tx_layers = (int16_t **)pusch_ue->txdataF_layers; ++ int16_t **tx_precoding = (int16_t **)pusch_ue->txdataF_precoding; ++ int32_t **txdataF = UE->common_vars.txdataF; + + uint8_t num_of_codewords = 1; // tmp assumption + int Nid_cell = 0; +@@ -148,9 +150,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, + + nb_dmrs_re_per_rb = ((dmrs_type == pusch_dmrs_type1) ? 6:4)*cdm_grps_no_data; + +- LOG_D(PHY,"ulsch %x : start_rb %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", +- rnti,start_rb,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); +- ++ LOG_I(PHY,"ulsch TX %x : start_rb %d nb_rb %d mod_order %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", ++ rnti,start_rb,nb_rb,mod_order,Nl,pusch_pdu->Tpmi,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); ++ + // TbD num_of_mod_symbols is set but never used + N_RE_prime = NR_NB_SC_PER_RB*number_of_symbols - nb_dmrs_re_per_rb*number_dmrs_symbols - N_PRB_oh; + harq_process_ul_ue->num_of_mod_symbols = N_RE_prime*nb_rb*num_of_codewords; +@@ -236,8 +238,6 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, + /////////////////////////ULSCH layer mapping///////////////////////// + /////////// + +- tx_layers = (int16_t **)pusch_ue->txdataF_layers; +- + nr_ue_layer_mapping(UE->ulsch[thread_id][gNB_id], + Nl, + available_bits/mod_order, +@@ -257,7 +257,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, + int16_t *dmrs_seq = NULL; + + // if transform precoding is enbaled (value 0) +- if (pusch_pdu->transform_precoding == 0) { ++ if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { + + uint32_t nb_re_pusch=nb_rb * NR_NB_SC_PER_RB; + uint32_t y_offset = 0; +@@ -315,10 +315,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, + LOG_M("UE_DMRS_SEQ.m","UE_DMRS_SEQ", dmrs_seq,nb_re_pusch,1,1); + #endif + +- } +- else +- memcpy(ulsch_ue->y, tx_layers[0], (available_bits/mod_order)*sizeof(int32_t)); +- ++ } + + /////////// + //////////////////////////////////////////////////////////////////////// +@@ -328,145 +325,233 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, + /////////////////////////ULSCH RE mapping///////////////////////// + /////////// + +- txdataF = UE->common_vars.txdataF; +- +- for (ap=0; ap< Nl; ap++) { +- +- uint8_t k_prime = 0; +- uint16_t m = 0; +- +- +- #ifdef DEBUG_PUSCH_MAPPING ++ for (ap=0; ap< Nl; ap++) ++ { ++ uint8_t k_prime = 0; ++ uint16_t m = 0; ++ ++ #ifdef DEBUG_PUSCH_MAPPING + printf("NR_ULSCH_UE: Value of CELL ID %d /t, u %d \n", frame_parms->Nid_cell, u); +- #endif +- +- +- +- // DMRS params for this ap +- get_Wt(Wt, ap, dmrs_type); +- get_Wf(Wf, ap, dmrs_type); +- delta = get_delta(ap, dmrs_type); +- +- for (l=start_symbol; l> l) & 0x01) { +- is_dmrs_sym = 1; +- +- // transform precoding disabled (value 1) +- if (pusch_pdu->transform_precoding == 1){ +- +- if (dmrs_type == pusch_dmrs_type1) +- dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; +- else +- dmrs_idx = (pusch_pdu->bwp_start + start_rb)*4; +- +- // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part +- // Perform this on gold sequence, not required when SC FDMA operation is done, +- LOG_D(PHY,"DMRS in symbol %d\n",l); +- nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated +- +- } else { +- dmrs_idx = 0; +- } +- +- +- } else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { +- +- AssertFatal(pusch_pdu->transform_precoding == 1, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); ++ #endif + +- if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) { +- is_ptrs_sym = 1; +- nr_modulation(pusch_dmrs[l][0], nb_rb, DMRS_MOD_ORDER, mod_ptrs); ++ // DMRS params for this ap ++ get_Wt(Wt, ap, dmrs_type); ++ get_Wf(Wf, ap, dmrs_type); ++ delta = get_delta(ap, dmrs_type); ++ ++ for (l=start_symbol; l> l) & 0x01) ++ { ++ is_dmrs_sym = 1; ++ if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) ++ { ++ if (dmrs_type == pusch_dmrs_type1) ++ dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; ++ else ++ dmrs_idx = (pusch_pdu->bwp_start + start_rb)*4; ++ ++ // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part ++ // Perform this on gold sequence, not required when SC FDMA operation is done, ++ LOG_D(PHY,"DMRS in symbol %d\n",l); ++ nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated ++ } ++ else ++ { ++ dmrs_idx = 0; ++ } ++ } ++ else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) ++ { ++ AssertFatal(pusch_pdu->transformPrecoder == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); ++ ++ if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) ++ { ++ is_ptrs_sym = 1; ++ nr_modulation(pusch_dmrs[l][0], nb_rb, DMRS_MOD_ORDER, mod_ptrs); ++ } + } +- } + +- for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) { ++ for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) ++ { ++ uint8_t is_dmrs = 0; ++ uint8_t is_ptrs = 0; + +- uint8_t is_dmrs = 0; +- uint8_t is_ptrs = 0; ++ sample_offsetF = l*frame_parms->ofdm_symbol_size + k; + +- sample_offsetF = l*frame_parms->ofdm_symbol_size + k; +- +- if (is_dmrs_sym) { +- if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) +- is_dmrs = 1; +- } else if (is_ptrs_sym) { ++ if (is_dmrs_sym) ++ { ++ if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) ++ is_dmrs = 1; ++ } ++ else if (is_ptrs_sym) ++ { + is_ptrs = is_ptrs_subcarrier(k, +- rnti, +- ap, +- dmrs_type, +- K_ptrs, +- nb_rb, +- pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset, +- start_sc, +- frame_parms->ofdm_symbol_size); +- } +- +- if (is_dmrs == 1) { +- // if transform precoding is enabled +- if (pusch_pdu->transform_precoding == 0) { +- +- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; +- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; ++ rnti, ++ ap, ++ dmrs_type, ++ K_ptrs, ++ nb_rb, ++ pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset, ++ start_sc, ++ frame_parms->ofdm_symbol_size); ++ } ++ ++ if (is_dmrs == 1) ++ { ++ if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) ++ { ++ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; ++ } ++ else ++ { ++ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; ++ } ++ ++ #ifdef DEBUG_PUSCH_MAPPING ++ printf("DMRS: Layer: %d\t, dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", ++ ap, dmrs_idx, l, k, k_prime, n, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); ++ #endif ++ ++ dmrs_idx++; ++ k_prime++; ++ k_prime&=1; ++ n+=(k_prime)?0:1; ++ } ++ else if (is_ptrs == 1) ++ { ++ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; ++ ++ ptrs_idx++; ++ } ++ else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) ++ { ++ if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) ++ { ++ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[ap])[m<<1]; ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[ap])[(m<<1) + 1]; ++ } ++ else ++ { ++ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; ++ } ++ ++ #ifdef DEBUG_PUSCH_MAPPING ++ printf("DATA: layer %d\t m %d\t l %d \t k %d \t txdataF: %d %d\n", ++ ap, m, l, k, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); ++ #endif ++ ++ m++; ++ } ++ else ++ { ++ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = 0; ++ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = 0; ++ } ++ ++ if (++k >= frame_parms->ofdm_symbol_size) ++ { ++ k -= frame_parms->ofdm_symbol_size; ++ } ++ } //for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) ++ }//for (l=start_symbol; lnb_antennas_tx; ap++) ++ { ++ for (int l=start_symbol; lTpmi; + +- } else { +- +- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; +- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; +- ++ if (pmi == 0) {//unitary Precoding ++ if(ap< pusch_pdu->nrOfLayers) ++ memcpy((void*)&txdataF[ap][l*frame_parms->ofdm_symbol_size + k], ++ (void*)&tx_precoding[ap][2*(l*frame_parms->ofdm_symbol_size + k)], ++ NR_NB_SC_PER_RB*sizeof(int32_t)); ++ else ++ memset((void*)&txdataF[ap][l*frame_parms->ofdm_symbol_size + k], ++ 0, ++ NR_NB_SC_PER_RB*sizeof(int32_t)); ++ ++ k += NR_NB_SC_PER_RB; ++ if (k >= frame_parms->ofdm_symbol_size) { ++ k -= frame_parms->ofdm_symbol_size; ++ } ++ } ++ else ++ { ++ //get the precoding matrix weights: ++ char *W_prec; ++ switch (frame_parms->nb_antennas_tx) ++ { ++ case 1://1 antenna port ++ W_prec = nr_W_1l_2p[pmi][ap]; ++ break; ++ case 2://2 antenna ports ++ if (pusch_pdu->nrOfLayers == 1)//1 layer ++ W_prec = nr_W_1l_2p[pmi][ap]; ++ else//2 layers ++ W_prec = nr_W_2l_2p[pmi][ap]; ++ break; ++ case 4://4 antenna ports ++ if (pusch_pdu->nrOfLayers == 1)//1 layer ++ W_prec = nr_W_1l_4p[pmi][ap]; ++ else if (pusch_pdu->nrOfLayers == 2)//2 layers ++ W_prec = nr_W_2l_4p[pmi][ap]; ++ else if (pusch_pdu->nrOfLayers == 3)//3 layers ++ W_prec = nr_W_3l_4p[pmi][ap]; ++ else//4 layers ++ W_prec = nr_W_4l_4p[pmi][ap]; ++ break; ++ default: ++ LOG_D(PHY,"Precoding 1,2, or 4 antenna ports are currently supported\n"); ++ W_prec = nr_W_1l_2p[pmi][ap]; ++ break; + } + +- #ifdef DEBUG_PUSCH_MAPPING +- printf("dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", +- dmrs_idx, l, k, k_prime, n, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], +- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1]); +- #endif +- +- +- dmrs_idx++; +- k_prime++; +- k_prime&=1; +- n+=(k_prime)?0:1; +- +- } else if (is_ptrs == 1) { +- +- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; +- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; +- +- ptrs_idx++; +- +- } else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) { +- +- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; +- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; +- +- #ifdef DEBUG_PUSCH_MAPPING +- printf("m %d\t l %d \t k %d \t txdataF: %d %d\n", +- m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], +- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1]); +- #endif +- +- m++; +- +- } else { +- +- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = 0; +- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = 0; +- +- } ++ for (int i=0; iofdm_symbol_size + k; ++ int32_t precodatatx_F = nr_layer_precoder(tx_precoding, W_prec, pusch_pdu->nrOfLayers, re_offset); ++ ((int16_t*)txdataF[ap])[(re_offset<<1)] = ((int16_t *) &precodatatx_F)[0]; ++ ((int16_t*)txdataF[ap])[(re_offset<<1) + 1] = ((int16_t *) &precodatatx_F)[1]; ++ ++ if (++k >= frame_parms->ofdm_symbol_size) ++ { ++ k -= frame_parms->ofdm_symbol_size; ++ } ++ } ++ } ++ } //RB loop ++ } // symbol loop ++ }// port loop + +- if (++k >= frame_parms->ofdm_symbol_size) +- k -= frame_parms->ofdm_symbol_size; +- } +- } +- } +- } ++ } //for (cwd_index = 0;cwd_index < num_of_codewords; cwd_index++) + + NR_UL_UE_HARQ_t *harq_process_ulsch=NULL; + harq_process_ulsch = UE->ulsch[thread_id][gNB_id][0]->harq_processes[harq_pid]; +diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h +index 11b5112a6c..600ba07cb3 100644 +--- a/openair1/PHY/defs_gNB.h ++++ b/openair1/PHY/defs_gNB.h +@@ -479,7 +479,7 @@ typedef struct { + /// \brief Cross-correlation of two UE signals. + /// - first index: rx antenna [0..nb_antennas_rx[ + /// - second index: symbol [0..] +- int32_t **rho; ++ int32_t ***rho; + /// \f$\log_2(\max|H_i|^2)\f$ + int16_t log2_maxh; + /// \brief Magnitude of Uplink Channel first layer (16QAM level/First 64QAM level). +@@ -509,6 +509,10 @@ typedef struct { + /// \brief llr values. + /// - first index: ? [0..1179743] (hard coded) + int16_t *llr; ++ /// \brief llr values per layer. ++ /// - first index: ? [0..3] (hard coded) ++ /// - first index: ? [0..1179743] (hard coded) ++ int16_t **llr_layers; + /// DMRS symbol index, to be updated every DMRS symbol within a slot. + uint8_t dmrs_symbol; + // PTRS symbol index, to be updated every PTRS symbol within a slot. +diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h +index 69978f5548..02988b590a 100644 +--- a/openair1/PHY/defs_nr_UE.h ++++ b/openair1/PHY/defs_nr_UE.h +@@ -229,6 +229,9 @@ typedef struct { + typedef struct { + /// TX buffers for multiple layers + int32_t *txdataF_layers[NR_MAX_NB_LAYERS]; ++ ++ /// TX buffers for UE-spec transmission ++ int32_t *txdataF_precoding[NR_MAX_NB_LAYERS]; + } NR_UE_PUSCH; + + typedef struct { +diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h +index c385d323e3..fbe094f41c 100644 +--- a/openair1/PHY/defs_nr_common.h ++++ b/openair1/PHY/defs_nr_common.h +@@ -93,7 +93,7 @@ + #define NR_MAX_CSET_DURATION 3 + + #define NR_MAX_NB_RBG 18 +-#define NR_MAX_NB_LAYERS 2 // 8 // SU-MIMO (3GPP TS 38.211 V15.4.0 section 7.3.1.3) ++#define NR_MAX_NB_LAYERS 4 // 8 // SU-MIMO (3GPP TS 38.211 V15.4.0 section 7.3.1.3) + #define NR_MAX_NB_CODEWORDS 2 + #define NR_MAX_NB_HARQ_PROCESSES 16 + #define NR_MAX_PDSCH_ENCODED_LENGTH (NR_MAX_NB_RB*NR_SYMBOLS_PER_SLOT*NR_NB_SC_PER_RB*8*NR_MAX_NB_LAYERS) // 8 is the maximum modulation order (it was 950984 before !!) +@@ -104,11 +104,11 @@ + #define MAX_NUM_NR_DLSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) + #define MAX_NR_DLSCH_PAYLOAD_BYTES (MAX_NUM_NR_DLSCH_SEGMENTS*1056) + +-#define MAX_NUM_NR_ULSCH_SEGMENTS 34 ++#define MAX_NUM_NR_ULSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) + #define MAX_NR_ULSCH_PAYLOAD_BYTES (MAX_NUM_NR_ULSCH_SEGMENTS*1056) + +-#define MAX_NUM_NR_CHANNEL_BITS (14*273*12*8) // 14 symbols, 273 RB +-#define MAX_NUM_NR_RE (14*273*12) ++#define MAX_NUM_NR_CHANNEL_BITS (4*14*273*12*8) // 14 symbols, 273 RB ++#define MAX_NUM_NR_RE (4*14*273*12) + #define NR_RX_NB_TH 1 + #define NR_NB_TH_SLOT 2 + +diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c +index d8fa11d157..640a0740ef 100644 +--- a/openair1/SCHED_NR/nr_ru_procedures.c ++++ b/openair1/SCHED_NR/nr_ru_procedures.c +@@ -526,7 +526,8 @@ void nr_fep0(RU_t *ru, int first_half) { + end_symbol = NR_SYMBOLS_PER_SLOT; + } + +- LOG_D(PHY,"In fep0 for slot = %d, first_half = %d, start_symbol = %d, end_symbol = %d\n", proc->tti_rx, first_half, start_symbol, end_symbol); ++ LOG_D(PHY,"In fep0 for slot = %d, first_half = %d, start_symbol = %d, end_symbol = %d, nb_antennas_rx = %d,N_TA_offset = %d\n", ++ proc->tti_rx, first_half, start_symbol, end_symbol,fp->nb_antennas_rx,ru->N_TA_offset); + // printf("fep0: slot %d\n",slot); + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX+proc->tti_rx, 1); +diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c +index 1685e206ae..63c708e565 100644 +--- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c ++++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c +@@ -253,13 +253,13 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { + // if all segments are done + if (rdata->nbSegments == ulsch_harq->processedSegments) { + if (decodeSuccess) { +- LOG_D(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", ++ LOG_I(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", + gNB->Mod_id,ulsch_harq->frame,ulsch_harq->slot,rdata->harq_pid,pusch_pdu->pusch_data.new_data_indicator,ulsch_harq->status,ulsch_harq->round,ulsch_harq->TBS,rdata->decodeIterations); + ulsch_harq->status = SCH_IDLE; + ulsch_harq->round = 0; + ulsch->harq_mask &= ~(1 << rdata->harq_pid); + +- LOG_D(PHY, "ULSCH received ok \n"); ++ LOG_I(PHY, "ULSCH received ok \n"); + nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 0,0); + //dumpsig=1; + } else { +@@ -281,7 +281,7 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { + } + ulsch_harq->handled = 1; + +- LOG_D(PHY, "ULSCH %d in error\n",rdata->ulsch_id); ++ LOG_I(PHY, "ULSCH %d in error\n",rdata->ulsch_id); + nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 1,0); + } + /* +@@ -365,6 +365,14 @@ void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH + number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs + pusch_pdu->qam_mod_order, + pusch_pdu->nrOfLayers); ++ ++ ++ nr_ulsch_layer_demapping(gNB->pusch_vars[ULSCH_id]->llr, ++ pusch_pdu->nrOfLayers, ++ pusch_pdu->qam_mod_order, ++ G, ++ gNB->pusch_vars[ULSCH_id]->llr_layers); ++ + //---------------------------------------------------------- + //------------------- ULSCH unscrambling ------------------- + //---------------------------------------------------------- +@@ -731,7 +739,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { + (ulsch_harq->slot == slot_rx) && + (ulsch_harq->handled == 0)){ + +- LOG_D(PHY, "PUSCH detection started in frame %d slot %d\n", ++ LOG_I(PHY, "PUSCH detection started in frame %d slot %d\n", + frame_rx,slot_rx); + int num_dmrs=0; + for (int s=0;srx_pusch_stats); + no_sig = nr_rx_pusch(gNB, ULSCH_id, frame_rx, slot_rx, harq_pid); + if (no_sig) { +- LOG_D(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); ++ LOG_I(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); + nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, harq_pid, 1,1); + return 1; + } +@@ -783,7 +791,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { + dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot) + gNB->pusch_thres) { + NR_gNB_SCH_STATS_t *stats=get_ulsch_stats(gNB,ulsch); + +- LOG_D(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, ++ LOG_I(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, + dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot), + dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot),gNB->pusch_thres); + gNB->pusch_vars[ULSCH_id]->ulsch_power_tot = gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot; +diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +index f6e4d90bee..8e1df435c3 100644 +--- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c ++++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +@@ -157,7 +157,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ + for (i = 0; i < ul_config->number_pdus; ++i){ + + AssertFatal(ul_config->ul_config_list[i].pdu_type <= FAPI_NR_UL_CONFIG_TYPES,"pdu_type %d out of bounds\n",ul_config->ul_config_list[i].pdu_type); +- LOG_D(PHY, "In %s: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); ++ LOG_I(PHY, "In %s i %d: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, i, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); + + uint8_t pdu_type = ul_config->ul_config_list[i].pdu_type, current_harq_pid, gNB_id = 0; + /* PRACH */ +@@ -180,6 +180,8 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ + if (harq_process_ul_ue){ + + nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &harq_process_ul_ue->pusch_pdu; ++ ++ LOG_I(PHY, "In %s i %d: copy pusch_config_pdu nrOfLayers:%d, num_dmrs_cdm_grps_no_data:%d \n", __FUNCTION__, i, pusch_config_pdu->nrOfLayers,pusch_config_pdu->num_dmrs_cdm_grps_no_data); + + memcpy(pusch_pdu, pusch_config_pdu, sizeof(nfapi_nr_ue_pusch_pdu_t)); + +diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +index 940931694c..0a0a9cc2f9 100644 +--- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c ++++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +@@ -270,9 +270,12 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, + AssertFatal(ue->CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); + + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX,VCD_FUNCTION_IN); +- +- memset(ue->common_vars.txdataF[0], 0, sizeof(int)*14*ue->frame_parms.ofdm_symbol_size); +- ++ ++ for(int i=0; i< ue->frame_parms.nb_antennas_tx; ++i) ++ { ++ memset(ue->common_vars.txdataF[i], 0, sizeof(int)*14*ue->frame_parms.ofdm_symbol_size); ++ } ++ + LOG_D(PHY,"****** start TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx); + + +diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c +index 1eb6027936..d5a1054654 100644 +--- a/openair1/SIMULATION/NR_PHY/dlsim.c ++++ b/openair1/SIMULATION/NR_PHY/dlsim.c +@@ -768,7 +768,7 @@ int main(int argc, char **argv) + + prepare_scd(scd); + +- fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, 0, 0, 0); ++ fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, n_rx, 0, 0, 0); + + /* RRC parameter validation for secondaryCellGroup */ + fix_scd(scd); +diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c +index 94aea36a67..fb8b4295ae 100644 +--- a/openair1/SIMULATION/NR_PHY/ulschsim.c ++++ b/openair1/SIMULATION/NR_PHY/ulschsim.c +@@ -137,6 +137,7 @@ int main(int argc, char **argv) + uint16_t nb_symb_sch = 12; + uint16_t nb_rb = 50; + uint8_t Imcs = 9; ++ uint8_t Nl = 1; + + double DS_TDL = .03; + +@@ -150,7 +151,7 @@ int main(int argc, char **argv) + randominit(0); + + //while ((c = getopt(argc, argv, "df:hpg:i:j:n:l:m:r:s:S:y:z:M:N:F:R:P:")) != -1) { +- while ((c = getopt(argc, argv, "hg:n:s:S:py:z:M:N:R:F:m:l:r:")) != -1) { ++ while ((c = getopt(argc, argv, "hg:n:s:S:py:z:M:N:R:F:m:l:r:W:")) != -1) { + switch (c) { + /*case 'f': + write_output_file = 1; +@@ -251,7 +252,7 @@ int main(int argc, char **argv) + case 'y': + n_tx = atoi(optarg); + +- if ((n_tx == 0) || (n_tx > 2)) { ++ if ((n_tx == 0) || (n_tx > 4)) { + printf("Unsupported number of TX antennas %d. Exiting.\n", n_tx); + exit(-1); + } +@@ -261,7 +262,7 @@ int main(int argc, char **argv) + case 'z': + n_rx = atoi(optarg); + +- if ((n_rx == 0) || (n_rx > 2)) { ++ if ((n_rx == 0) || (n_rx > 4)) { + printf("Unsupported number of RX antennas %d. Exiting.\n", n_rx); + exit(-1); + } +@@ -299,6 +300,10 @@ int main(int argc, char **argv) + printf("Illegal PBCH phase (0-3) got %d\n", pbch_phase); + break;*/ + ++ case 'W': ++ Nl = atoi(optarg); ++ break; ++ + case 'm': + Imcs = atoi(optarg); + #ifdef DEBUG_NR_ULSCHSIM +@@ -335,13 +340,14 @@ int main(int argc, char **argv) + printf("-z Number of RX antennas used in UE\n"); + //printf("-i Relative strength of first intefering eNB (in dB) - cell_id mod 3 = 1\n"); + //printf("-j Relative strength of second intefering eNB (in dB) - cell_id mod 3 = 2\n"); ++ printf("-W number of layer\n"); + printf("-M Multiple SSB positions in burst\n"); + printf("-N Nid_cell\n"); + printf("-R N_RB_UL\n"); + printf("-F Input filename (.txt format) for RX conformance testing\n"); +- printf("-m\n"); +- printf("-l\n"); +- printf("-r\n"); ++ printf("-m MCS\n"); ++ printf("-l number of symbol\n"); ++ printf("-r number of RB\n"); + //printf("-O oversampling factor (1,2,4,8,16)\n"); + //printf("-A Interpolation_filname Run with Abstraction to generate Scatter plot using interpolation polynomial in file\n"); + //printf("-C Generate Calibration information for Abstraction (effective SNR adjustment to remove Pe bias w.r.t. AWGN)\n"); +@@ -359,8 +365,8 @@ int main(int argc, char **argv) + snr1 = snr0 + 10; + + gNB2UE = new_channel_desc_scm(n_tx, +- n_rx, +- channel_model, ++ n_rx, ++ channel_model, + 61.44e6, //N_RB2sampling_rate(N_RB_DL), + 40e6, //N_RB2channel_bandwidth(N_RB_DL), + DS_TDL, +@@ -426,8 +432,7 @@ int main(int argc, char **argv) + uint8_t length_dmrs = 1; + uint8_t N_PRB_oh; + uint16_t N_RE_prime,code_rate; +- unsigned char mod_order; +- uint8_t Nl = 1; ++ unsigned char mod_order; + uint8_t rvidx = 0; + uint8_t UE_id = 0; + +@@ -437,9 +442,14 @@ int main(int argc, char **argv) + + NR_UE_ULSCH_t *ulsch_ue = UE->ulsch[0][0][0]; + ++ if ((Nl==4)||(Nl==3)) ++ { ++ nb_re_dmrs = nb_re_dmrs*2; ++ } ++ + mod_order = nr_get_Qm_ul(Imcs, 0); + code_rate = nr_get_code_rate_ul(Imcs, 0); +- available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, 1); ++ available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, Nl); + TBS = nr_compute_tbs(mod_order,code_rate, nb_rb, nb_symb_sch, nb_re_dmrs*length_dmrs, 0, 0, Nl); + + printf("\nAvailable bits %u TBS %u mod_order %d\n", available_bits, TBS, mod_order); +diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c +index 69316d6b88..b6cfae3fe1 100644 +--- a/openair1/SIMULATION/NR_PHY/ulsim.c ++++ b/openair1/SIMULATION/NR_PHY/ulsim.c +@@ -282,7 +282,7 @@ int main(int argc, char **argv) + int gNB_id = 0; + int ap; + int tx_offset; +- int32_t txlev=0; ++ int32_t txlev_sum = 0, atxlev[4]; + int start_rb = 0; + int UE_id =0; // [hna] only works for UE_id = 0 because NUMBER_OF_NR_UE_MAX is set to 1 (phy_init_nr_gNB causes segmentation fault) + float target_error_rate = 0.01; +@@ -307,7 +307,7 @@ int main(int argc, char **argv) + uint16_t ptrsSymbPerSlot = 0; + uint16_t ptrsRePerSymb = 0; + +- uint8_t transform_precoding = 1; // 0 - ENABLE, 1 - DISABLE ++ uint8_t transformPrecoder = transformPrecoder_disabled; // 0 - ENABLE, 1 - DISABLE + uint8_t num_dmrs_cdm_grps_no_data = 1; + uint8_t mcs_table = 0; + +@@ -329,7 +329,7 @@ int main(int argc, char **argv) + /* initialize the sin-cos table */ + InitSinLUT(); + +- while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:ikl:m:n:p:r:s:u:w:y:z:F:G:H:M:N:PR:S:T:U:L:Z")) != -1) { ++ while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:i:kl:m:n:p:r:s:u:w:y:z:F:G:H:M:N:PR:S:T:U:L:Z:W:")) != -1) { + printf("handling optarg %c\n",c); + switch (c) { + +@@ -363,8 +363,8 @@ int main(int argc, char **argv) + scg_fd = fopen(optarg, "r"); + + if (scg_fd == NULL) { +- printf("Error opening %s\n", optarg); +- exit(-1); ++ printf("Error opening %s\n", optarg); ++ exit(-1); + } + + break; +@@ -437,6 +437,10 @@ int main(int argc, char **argv) + case 'm': + Imcs = atoi(optarg); + break; ++ ++ case 'W': ++ precod_nbr_layers = atoi(optarg); ++ break; + + case 'n': + n_trials = atoi(optarg); +@@ -485,9 +489,9 @@ int main(int argc, char **argv) + case 'y': + n_tx = atoi(optarg); + +- if ((n_tx == 0) || (n_tx > 2)) { +- printf("Unsupported number of tx antennas %d\n", n_tx); +- exit(-1); ++ if ((n_tx == 0) || (n_tx > 4)) { ++ printf("Unsupported number of tx antennas %d\n", n_tx); ++ exit(-1); + } + + break; +@@ -496,8 +500,8 @@ int main(int argc, char **argv) + n_rx = atoi(optarg); + + if ((n_rx == 0) || (n_rx > 8)) { +- printf("Unsupported number of rx antennas %d\n", n_rx); +- exit(-1); ++ printf("Unsupported number of rx antennas %d\n", n_rx); ++ exit(-1); + } + + break; +@@ -506,8 +510,8 @@ int main(int argc, char **argv) + input_fd = fopen(optarg, "r"); + + if (input_fd == NULL) { +- printf("Problem with filename %s\n", optarg); +- exit(-1); ++ printf("Problem with filename %s\n", optarg); ++ exit(-1); + } + + break; +@@ -568,11 +572,11 @@ int main(int argc, char **argv) + + case 'Z': + +- transform_precoding = 0; // enabled ++ transformPrecoder = transformPrecoder_enabled; + num_dmrs_cdm_grps_no_data = 2; + mcs_table = 3; + +- printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transform_precoding); ++ printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transformPrecoder); + + break; + +@@ -611,6 +615,7 @@ int main(int argc, char **argv) + printf("-U Change DMRS Config, arguments list DMRS TYPE{0=A,1=B} DMRS AddPos{0:3}, e.g. -U 2 0 2 \n"); + printf("-Q If -F used, read parameters from file\n"); + printf("-Z If -Z is used, SC-FDMA or transform precoding is enabled in Uplink \n"); ++ printf("-W Num of layer for PUSCH\n"); + exit(-1); + break; + +@@ -713,7 +718,7 @@ int main(int argc, char **argv) + + prepare_scd(scd); + +- fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, 0, 0, 0); ++ fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, n_rx, 0, 0, 0); + + // xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)secondaryCellGroup); + +@@ -724,9 +729,9 @@ int main(int argc, char **argv) + + gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; + // common configuration +- rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, scc, &rrc.carrier.mib,0, 0, NULL); ++ rrc_mac_config_req_gNB(0,0, n_tx, n_rx, n_tx, scc, &rrc.carrier.mib,0, 0, NULL); + // UE dedicated configuration +- rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); ++ rrc_mac_config_req_gNB(0,0, n_tx, n_rx, n_tx, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + frame_parms->nb_antennas_tx = n_tx; + frame_parms->nb_antennas_rx = n_rx; + nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; +@@ -870,14 +875,18 @@ int main(int argc, char **argv) + uint16_t number_dmrs_symbols = get_dmrs_symbols_in_slot(l_prime_mask, nb_symb_sch); + printf("num dmrs sym %d\n",number_dmrs_symbols); + uint8_t nb_re_dmrs = (dmrs_config_type == pusch_dmrs_type1) ? 6 : 4; +- +- // if transform precoding is enabled +- if (transform_precoding == 0) { ++ ++ if ((frame_parms->nb_antennas_tx==4)&&(precod_nbr_layers==4)) ++ { ++ num_dmrs_cdm_grps_no_data = 2; ++ } ++ ++ if (transformPrecoder == transformPrecoder_enabled) { + + AssertFatal(enable_ptrs == 0, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); + + int8_t index = get_index_for_dmrs_lowpapr_seq((NR_NB_SC_PER_RB/2) * nb_rb); +- AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); ++ AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); + + dmrs_config_type = pusch_dmrs_type1; + +@@ -886,7 +895,7 @@ int main(int argc, char **argv) + + nb_re_dmrs = nb_re_dmrs * num_dmrs_cdm_grps_no_data; + +- unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, mod_order, 1); ++ unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, mod_order, precod_nbr_layers); + unsigned int TBS = nr_compute_tbs(mod_order, code_rate, nb_rb, nb_symb_sch, nb_re_dmrs * number_dmrs_symbols, 0, 0, precod_nbr_layers); + + +@@ -1082,14 +1091,14 @@ int main(int argc, char **argv) + pusch_pdu->mcs_table = mcs_table; + pusch_pdu->target_code_rate = code_rate; + pusch_pdu->qam_mod_order = mod_order; +- pusch_pdu->transform_precoding = transform_precoding; ++ pusch_pdu->transformPrecoder = transformPrecoder; + pusch_pdu->data_scrambling_id = *scc->physCellId; +- pusch_pdu->nrOfLayers = 1; ++ pusch_pdu->nrOfLayers = precod_nbr_layers; + pusch_pdu->ul_dmrs_symb_pos = l_prime_mask; + pusch_pdu->dmrs_config_type = dmrs_config_type; + pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId; + pusch_pdu->scid = 0; +- pusch_pdu->dmrs_ports = 1; ++ pusch_pdu->dmrs_ports = ((1<num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; + pusch_pdu->resource_alloc = 1; + pusch_pdu->rb_start = start_rb; +@@ -1109,7 +1118,7 @@ int main(int argc, char **argv) + pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; + + // if transform precoding is enabled +- if (transform_precoding == 0) { ++ if (transformPrecoder == transformPrecoder_enabled) { + + pusch_pdu->dfts_ofdm.low_papr_group_number = *scc->physCellId % 30; // U as defined in 38.211 section 6.4.1.1.1.2 + pusch_pdu->dfts_ofdm.low_papr_sequence_number = 0; // V as defined in 38.211 section 6.4.1.1.1.2 +@@ -1167,10 +1176,10 @@ int main(int argc, char **argv) + ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list = (nfapi_nr_ue_ptrs_ports_t *) malloc(2*sizeof(nfapi_nr_ue_ptrs_ports_t)); + ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; + +- ul_config.ul_config_list[0].pusch_config_pdu.transform_precoding = transform_precoding; ++ ul_config.ul_config_list[0].pusch_config_pdu.transformPrecoder = transformPrecoder; + + // if transform precoding is enabled +- if (transform_precoding == 0) { ++ if (transformPrecoder == transformPrecoder_enabled) { + + ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_group_number = *scc->physCellId % 30;// U as defined in 38.211 section 6.4.1.1.1.2 + ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_sequence_number = 0;// V as defined in 38.211 section 6.4.1.1.1.2 +@@ -1198,28 +1207,54 @@ int main(int argc, char **argv) + LOG_D(PHY, "Sending Uplink data \n"); + nr_ue_pusch_common_procedures(UE, + slot, +- &UE->frame_parms,1); +- +- if (n_trials==1) { +- LOG_M("txsig0.m","txs0", UE->common_vars.txdata[0],frame_parms->samples_per_subframe*10,1,1); ++ &UE->frame_parms, ++ frame_parms->nb_antennas_tx); ++ ++ ++ if (n_trials==1) ++ { ++ LOG_M("txsig0.m","txs0", &UE->common_vars.txdata[0][slot_offset],slot_length,1,1); + LOG_M("txsig0F.m","txs0F", UE->common_vars.txdataF[0],frame_parms->ofdm_symbol_size*14,1,1); ++ if (precod_nbr_layers > 1) ++ { ++ LOG_M("txsig1.m","txs1", &UE->common_vars.txdata[1][slot_offset],slot_length,1,1); ++ LOG_M("txsig1F.m","txs1F", UE->common_vars.txdataF[1],frame_parms->ofdm_symbol_size*14,1,1); ++ if (precod_nbr_layers==4) ++ { ++ LOG_M("txsig2.m","txs2", &UE->common_vars.txdata[2][slot_offset],slot_length,1,1); ++ LOG_M("txsig3.m","txs3", &UE->common_vars.txdata[3][slot_offset],slot_length,1,1); ++ ++ LOG_M("txsig2F.m","txs2F", UE->common_vars.txdataF[2],frame_parms->ofdm_symbol_size*14,1,1); ++ LOG_M("txsig3F.m","txs3F", UE->common_vars.txdataF[3],frame_parms->ofdm_symbol_size*14,1,1); ++ } ++ } + } + /////////// + //////////////////////////////////////////////////// + tx_offset = frame_parms->get_samples_slot_timestamp(slot,frame_parms,0); +- +- txlev = signal_energy(&UE->common_vars.txdata[0][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], ++ txlev_sum = 0; ++ for (int aa=0; aanb_antennas_tx; aa++) ++ { ++ atxlev[aa] = signal_energy(&UE->common_vars.txdata[aa][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], + frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples); +- } +- else n_trials = 1; +- +- if (input_fd == NULL ) { ++ ++ txlev_sum += atxlev[aa]; ++ ++ if (n_trials==1) printf("txlev[%d] = %d (%f dB) txlev_sum %d\n",aa,atxlev[aa],10*log10((double)atxlev[aa]),txlev_sum); ++ } ++ } ++ else ++ { ++ n_trials = 1; ++ } + +- sigma_dB = 10 * log10((double)txlev * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; ++ if (input_fd == NULL ) ++ { ++ sigma_dB = 10 * log10((double)txlev_sum * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; + sigma = pow(10,sigma_dB/10); + + +- if(n_trials==1) printf("sigma %f (%f dB), txlev %f (factor %f)\n",sigma,sigma_dB,10*log10((double)txlev),(double)(double) ++ if(n_trials==1) printf("sigma %f (%f dB), txlev_sum %f (factor %f)\n",sigma,sigma_dB,10*log10((double)txlev_sum),(double)(double) + frame_parms->ofdm_symbol_size/(12*nb_rb)); + + for (i=0; imax_Doppler == 0) { ++ if (UE2gNB->max_Doppler == 0) ++ { + multipath_channel(UE2gNB, s_re, s_im, r_re, r_im, slot_length, 0, (n_trials==1)?1:0); + } else { + multipath_tv_channel(UE2gNB, s_re, s_im, r_re, r_im, 2*slot_length, 0); + } +- for (i=0; inb_antennas_rx; ap++) { ++ for (i=0; inb_antennas_rx; ap++) ++ { ++ if (channel_model == AWGN) ++ { ++ double H_awgn[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 ++ {0.0, 1.0, 0.0, 0.0}, //rx 1 ++ {0.0, 0.0, 1.0, 0.0}, //rx 2 ++ {0.0, 0.0, 0.0, 1.0}};//rx 3 ++ #if 0 ++ double H_awgn_mimo[4][4] ={{1.0, 0.5, 0.25, 0.125},//rx 0 ++ {0.5, 1.0, 0.5, 0.25}, //rx 1 ++ {0.25, 0.5, 1.0, 0.5}, //rx 2 ++ {0.125, 0.25, 0.5, 1.0}};//rx 3 ++ #endif ++ // sum up signals from different Tx antennas ++ r_re[ap][i] = 0; ++ r_im[ap][i] = 0; ++ ++ for (int aa=0; aaofdm_symbol_size/(12*nb_rb))) - SNR;; ++ sigma = pow(10,sigma_dB/10); ++ + ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); // convert to fixed point + ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); ++ ++ #if 0 ++ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i])); // convert to fixed point ++ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i])); ++ ++ ++ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) ] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i) ]; // convert to fixed point ++ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i)+1 ]; ++ #endif ++ + /* Add phase noise if enabled */ + if (pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { + phase_noise(ts, &((int16_t*)&gNB->common_vars.rxdata[ap][slot_offset])[(2*i)], +@@ -1250,7 +1324,8 @@ int main(int argc, char **argv) + } /*End input_fd */ + + +- if(pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { ++ if(pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) ++ { + set_ptrs_symb_idx(&ptrsSymPos, + pusch_pdu->nr_of_symbols, + pusch_pdu->start_symbol_index, +@@ -1268,24 +1343,37 @@ int main(int argc, char **argv) + gNB->UL_INFO.rx_ind.number_of_pdus = 0; + gNB->UL_INFO.crc_ind.number_crcs = 0; + +- phy_procedures_gNB_common_RX(gNB, frame, slot); ++ phy_procedures_gNB_common_RX(gNB, frame, slot); + +- ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot); ++ ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot); + +- if (n_trials==1 && round==0) { ++ if (n_trials==1 && round==0) ++ { + LOG_M("rxsig0.m","rx0",&gNB->common_vars.rxdata[0][slot_offset],slot_length,1,1); + +- LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0]+start_symbol*frame_parms->ofdm_symbol_size,nb_symb_sch*frame_parms->ofdm_symbol_size,1,1); +- ++ LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0],14*frame_parms->ofdm_symbol_size,1,1); ++ if (precod_nbr_layers > 1) ++ { ++ LOG_M("rxsig1.m","rx1",&gNB->common_vars.rxdata[1][slot_offset],slot_length,1,1); ++ LOG_M("rxsigF1.m","rxsF1",gNB->common_vars.rxdataF[1],14*frame_parms->ofdm_symbol_size,1,1); ++ if (precod_nbr_layers==4) ++ { ++ LOG_M("rxsig2.m","rx2",&gNB->common_vars.rxdata[2][slot_offset],slot_length,1,1); ++ LOG_M("rxsig3.m","rx3",&gNB->common_vars.rxdata[3][slot_offset],slot_length,1,1); ++ ++ LOG_M("rxsigF2.m","rxsF2",gNB->common_vars.rxdataF[2],14*frame_parms->ofdm_symbol_size,1,1); ++ LOG_M("rxsigF3.m","rxsF3",gNB->common_vars.rxdataF[3],14*frame_parms->ofdm_symbol_size,1,1); ++ } ++ } + } + + + if (n_trials == 1 && round==0) { +-#ifdef __AVX2__ ++ #ifdef __AVX2__ + int off = ((nb_rb&1) == 1)? 4:0; +-#else ++ #else + int off = 0; +-#endif ++ #endif + + LOG_M("rxsigF0_ext.m","rxsF0_ext", + &gNB->pusch_vars[0]->rxdataF_ext[0][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); +@@ -1298,11 +1386,77 @@ int main(int argc, char **argv) + (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF0_comp.m","rxsF0_comp", + &gNB->pusch_vars[0]->rxdataF_comp[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); +- LOG_M("chmagF0.m","chmF0", +- &gNB->pusch_vars[0]->ul_ch_mag[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); +- LOG_M("chmagbF0.m","chmbF0", +- &gNB->pusch_vars[0]->ul_ch_magb[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); +- if (n_rx == 2) { ++ LOG_M("chmagF0.m","chmF0", ++ &gNB->pusch_vars[0]->ul_ch_mag[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("chmagbF0.m","chmbF0", ++ &gNB->pusch_vars[0]->ul_ch_magb[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("rxsigF0_llrlayers0.m","rxsF0_llrlayers0", ++ &gNB->pusch_vars[0]->llr_layers[0][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); ++ ++ if (precod_nbr_layers==2) ++ { ++ LOG_M("rxsigF1_ext.m","rxsF1_ext", ++ &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ ++ LOG_M("chestF3.m","chF3", ++ &gNB->pusch_vars[0]->ul_ch_estimates[3][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); ++ ++ LOG_M("chestF3_ext.m","chF3_ext", ++ &gNB->pusch_vars[0]->ul_ch_estimates_ext[3][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], ++ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ ++ LOG_M("rxsigF2_comp.m","rxsF2_comp", ++ &gNB->pusch_vars[0]->rxdataF_comp[2][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ ++ LOG_M("rxsigF0_llrlayers1.m","rxsF0_llrlayers1", ++ &gNB->pusch_vars[0]->llr_layers[1][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); ++ ++ } ++ ++ if (precod_nbr_layers==4) ++ { ++ LOG_M("rxsigF1_ext.m","rxsF1_ext", ++ &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("rxsigF2_ext.m","rxsF2_ext", ++ &gNB->pusch_vars[0]->rxdataF_ext[2][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("rxsigF3_ext.m","rxsF3_ext", ++ &gNB->pusch_vars[0]->rxdataF_ext[3][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ ++ LOG_M("chestF5.m","chF5", ++ &gNB->pusch_vars[0]->ul_ch_estimates[5][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); ++ LOG_M("chestF10.m","chF10", ++ &gNB->pusch_vars[0]->ul_ch_estimates[10][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); ++ LOG_M("chestF15.m","chF15", ++ &gNB->pusch_vars[0]->ul_ch_estimates[15][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); ++ ++ ++ LOG_M("chestF5_ext.m","chF5_ext", ++ &gNB->pusch_vars[0]->ul_ch_estimates_ext[5][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], ++ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("chestF10_ext.m","chF10_ext", ++ &gNB->pusch_vars[0]->ul_ch_estimates_ext[10][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], ++ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("chestF15_ext.m","chF15_ext", ++ &gNB->pusch_vars[0]->ul_ch_estimates_ext[15][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], ++ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ ++ ++ LOG_M("rxsigF4_comp.m","rxsF4_comp", ++ &gNB->pusch_vars[0]->rxdataF_comp[4][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("rxsigF8_comp.m","rxsF8_comp", ++ &gNB->pusch_vars[0]->rxdataF_comp[8][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("rxsigF12_comp.m","rxsF12_comp", ++ &gNB->pusch_vars[0]->rxdataF_comp[12][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); ++ LOG_M("rxsigF0_llrlayers1.m","rxsF0_llrlayers1", ++ &gNB->pusch_vars[0]->llr_layers[1][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); ++ LOG_M("rxsigF0_llrlayers2.m","rxsF0_llrlayers2", ++ &gNB->pusch_vars[0]->llr_layers[2][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); ++ LOG_M("rxsigF0_llrlayers3.m","rxsF0_llrlayers3", ++ &gNB->pusch_vars[0]->llr_layers[3][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); ++ } ++ ++ #if 0 ++ if (n_rx == 2) { + LOG_MM("rxsigF0_comp.m","rxsF1_comp", + &gNB->pusch_vars[0]->rxdataF_comp[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_MM("rxsigF0_ext.m","rxsF1_ext", +@@ -1349,9 +1503,10 @@ int main(int argc, char **argv) + LOG_MM("chmagbF0.m","chmbF3", + &gNB->pusch_vars[0]->ul_ch_magb[3][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + } ++ #endif + + LOG_M("rxsigF0_llr.m","rxsF0_llr", +- &gNB->pusch_vars[0]->llr[0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); ++ &gNB->pusch_vars[0]->llr[0],precod_nbr_layers*(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + } + //////////////////////////////////////////////////////////// + +diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +index 3f4be9dd58..7e5be9eeba 100644 +--- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c ++++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +@@ -2708,6 +2708,9 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, + LOG_D(NR_MAC,"dci_pdu->srs_resource_indicator.nbits %d\n",dci_pdu->srs_resource_indicator.nbits); + // Precoding info and number of layers + long transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, ubwpd, (uint8_t*)&format, rnti_type, 0); ++ ++ pusch_antenna_ports = *pusch_Config->maxRank; ++ + dci_pdu->precoding_information.nbits=0; + if (pusch_Config && + pusch_Config->txConfig != NULL){ +diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +index c23d4bb073..963723725f 100644 +--- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c ++++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +@@ -190,7 +190,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con + else + transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; + } +- ++ pusch_config_pdu->transformPrecoder = transformPrecoder; + + /* PRECOD_NBR_LAYERS */ + if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_nonCodebook)); +@@ -198,11 +198,12 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con + + if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)){ + +- uint8_t n_antenna_port = 0; //FIXME!!! ++ uint8_t n_antenna_port = mac->phy_config.config_req.carrier_config.num_tx_ant; + + if (n_antenna_port == 1); // 1 antenna port and the higher layer parameter txConfig = codebook 0 bits + +- if (n_antenna_port == 4){ // 4 antenna port and the higher layer parameter txConfig = codebook ++ if (n_antenna_port == 4) ++ { // 4 antenna port and the higher layer parameter txConfig = codebook + + // Table 7.3.1.1.2-2: transformPrecoder=disabled and maxRank = 2 or 3 or 4 + if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) +@@ -212,54 +213,56 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][0]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1]; + } + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent){ + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][2]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3]; + } + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][4]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5]; + } + } + + // Table 7.3.1.1.2-3: transformPrecoder= enabled, or transformPrecoder=disabled and maxRank = 1 + if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) + || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) +- && (*pusch_Config->maxRank == 1)){ ++ && (*pusch_Config->maxRank == 1)) ++ { + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][6]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7]; + } + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent){ + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][8]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9]; + } + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][10]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11]; + } + } + } + +- if (n_antenna_port == 4){ // 2 antenna port and the higher layer parameter txConfig = codebook ++ if (n_antenna_port == 2) ++ { // 2 antenna port and the higher layer parameter txConfig = codebook + // Table 7.3.1.1.2-4: transformPrecoder=disabled and maxRank = 2 +- if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)){ +- ++ if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)) ++ { + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][12]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13]; + } + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][14]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15]; + } + + } +@@ -267,16 +270,17 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con + // Table 7.3.1.1.2-5: transformPrecoder= enabled, or transformPrecoder= disabled and maxRank = 1 + if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) + || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) +- && (*pusch_Config->maxRank == 1)){ ++ && (*pusch_Config->maxRank == 1)) ++ { + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][16]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17]; + } + + if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ + pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][18]; +- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19]; ++ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19]; + } + + } +@@ -285,9 +289,9 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con + + /*-------------------- Changed to enable Transform precoding in RF SIM------------------------------------------------*/ + +- /*if (pusch_config_pdu->transform_precoding == transform_precoder_enabled) { ++ /*if (pusch_config_pdu->transformPrecoder == transformPrecoder_enabled) { + +- pusch_config_dedicated->transform_precoder = transform_precoder_enabled; ++ pusch_config_dedicated->transform_precoder = transformPrecoder_enabled; + + if(pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA != NULL) { + +@@ -309,7 +313,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con + + } + } else +- pusch_config_dedicated->transform_precoder = transform_precoder_disabled;*/ ++ pusch_config_dedicated->transformPrecoder = ttransformPrecoder_disabled;*/ + } + + // todo: this function shall be reviewed completely because of the many comments left by the author +@@ -342,7 +346,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf + } + long *max_length = NULL; + long *dmrs_type = NULL; +- LOG_D(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); ++ LOG_I(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); + + if (pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA) { + max_length = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup->maxLength; +@@ -353,7 +357,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf + dmrs_type = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->dmrs_Type; + } + +- LOG_D(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n",pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); ++ LOG_I(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n",pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); + if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) && + (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-6 + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC +@@ -369,35 +373,32 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf + } + + if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && +- (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-8/9/10/11 ++ (dmrs_type == NULL) && (max_length == NULL)) ++ { // tables 7.3.1.1.2-8/9/10/11 ++ ++ if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)) ++ { ++ rank = pusch_config_pdu->nrOfLayers; ++ } + + if (rank == 1) { +- pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC +- pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val); //TBC ++ pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; ++ pusch_config_pdu->dmrs_ports =1<<((dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val)); + } + + if (rank == 2){ +- pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; //TBC +- pusch_config_pdu->dmrs_ports = 0; //FIXME +- //pusch_config_pdu->dmrs_ports[0] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?0:2):0; +- //pusch_config_pdu->dmrs_ports[1] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?2:3):1; ++ pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; ++ pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?((dci->antenna_ports.val> 2)?0x5:0xc):0x3; + } + + if (rank == 3){ +- pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC +- pusch_config_pdu->dmrs_ports = 0; //FIXME +- //pusch_config_pdu->dmrs_ports[0] = 0; +- //pusch_config_pdu->dmrs_ports[1] = 1; +- //pusch_config_pdu->dmrs_ports[2] = 2; ++ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; ++ pusch_config_pdu->dmrs_ports = ((1<num_dmrs_cdm_grps_no_data = 2; //TBC +- pusch_config_pdu->dmrs_ports = 0; //FIXME +- //pusch_config_pdu->dmrs_ports[0] = 0; +- //pusch_config_pdu->dmrs_ports[1] = 1; +- //pusch_config_pdu->dmrs_ports[2] = 2; +- //pusch_config_pdu->dmrs_ports[3] = 3; ++ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; ++ pusch_config_pdu->dmrs_ports = ((1<n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME + } + } +- LOG_D(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); ++ LOG_I(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); + } + + // Configuration of Msg3 PDU according to clauses: +@@ -542,6 +543,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, + pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; + pusch_config_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; + pusch_config_pdu->nrOfLayers = 1; ++ pusch_config_pdu->Tpmi = 0; + pusch_config_pdu->rnti = rnti; + NR_BWP_UplinkCommon_t *initialUplinkBWP; + if (mac->scc) initialUplinkBWP = mac->scc->uplinkConfigCommon->initialUplinkBWP; +@@ -634,7 +636,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, + pusch_config_pdu->scid = 0; + + // Transform precoding according to 6.1.3 UE procedure for applying transform precoding on PUSCH in 38.214 +- pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out ++ pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out + + // Resource allocation in frequency domain according to 6.1.2.2 in TS 38.214 + pusch_config_pdu->resource_alloc = (mac->cg) ? pusch_Config->resourceAllocation : 1; +@@ -680,7 +682,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, + + /* Transform precoding */ + if (rnti_type != NR_RNTI_CS || (rnti_type == NR_RNTI_CS && dci->ndi == 1)) { +- pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); ++ pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); + } + + /*DCI format-related configuration*/ +@@ -716,7 +718,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, + + /* TRANSFORM PRECODING ------------------------------------------------------------------------------------------*/ + +- if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled) { ++ if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) { + + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; + +@@ -761,7 +763,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, + pusch_config_pdu->mcs_index = dci->mcs; + + /* MCS TABLE */ +- if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { ++ if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { + pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_Table : NULL, 0, *dci_format, rnti_type, target_ss, false); + } else { + pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_TableTransformPrecoder : NULL, 1, *dci_format, rnti_type, target_ss, false); +@@ -798,9 +800,14 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, + mappingtype, add_pos, dmrslength, + pusch_config_pdu->start_symbol_index, + mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); +- if ((mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled)) +- pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; +- else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled)) ++ if (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) ++ { ++ if (*dci_format != NR_UL_DCI_FORMAT_0_1) ++ { ++ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; ++ } ++ } ++ else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled)) + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; + + // Num PRB Overhead from PUSCH-ServingCellConfig +@@ -821,7 +828,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, + mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup && + mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && + mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { +- if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { ++ if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { + nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; + pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; + valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, +@@ -909,6 +916,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in + + fill_scheduled_response(&scheduled_response, &dcireq.dl_config_req, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); + if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) ++ LOG_I(NR_MAC,"1# scheduled_response transmitted, %d, %d\n", rx_frame, rx_slot); + mac->if_module->scheduled_response(&scheduled_response); + } + else { +@@ -1022,6 +1030,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in + + fill_scheduled_response(&scheduled_response, NULL, ul_config, &tx_req, mod_id, cc_id, rx_frame, rx_slot, ul_info->thread_id); + if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){ ++ LOG_I(NR_MAC,"3# scheduled_response transmitted,%d, %d\n", frame_tx, slot_tx); + mac->if_module->scheduled_response(&scheduled_response); + } + pthread_mutex_lock(&ul_config->mutex_ul_config); +diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c +index 1cb3fdb708..d9c98852a3 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/config.c ++++ b/openair2/LAYER2/NR_MAC_gNB/config.c +@@ -388,7 +388,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts + cfg->carrier_config.num_rx_ant.value = pusch_AntennaPorts; + AssertFatal(pusch_AntennaPorts > 0 && pusch_AntennaPorts < 13, "pusch_AntennaPorts in 1...12\n"); + cfg->carrier_config.num_rx_ant.tl.tag = NFAPI_NR_CONFIG_NUM_RX_ANT_TAG; +- LOG_I(NR_MAC,"Set TX/RX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); ++ LOG_I(NR_MAC,"Set RX antenna number to %d, Set TX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,cfg->carrier_config.num_rx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); + AssertFatal(cfg->carrier_config.num_tx_ant.value > 0,"carrier_config.num_tx_ant.value %d !\n",cfg->carrier_config.num_tx_ant.value ); + cfg->num_tlv++; + cfg->num_tlv++; +diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +index 6ac7f3f82e..5de36bb2db 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c ++++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +@@ -977,9 +977,9 @@ void fill_msg3_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, + pusch_pdu->target_code_rate = nr_get_code_rate_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); + pusch_pdu->qam_mod_order = nr_get_Qm_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) +- pusch_pdu->transform_precoding = 1; ++ pusch_pdu->transformPrecoder = 1; + else +- pusch_pdu->transform_precoding = 0; ++ pusch_pdu->transformPrecoder = 0; + pusch_pdu->data_scrambling_id = *scc->physCellId; + pusch_pdu->nrOfLayers = 1; + pusch_pdu->ul_dmrs_symb_pos = get_l_prime(nr_of_symbols,mappingtype,pusch_dmrs_pos2,pusch_len1,start_symbol_index, scc->dmrs_TypeA_Position); +diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +index abc007197d..9cbbe7d9a1 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c ++++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +@@ -1139,13 +1139,14 @@ void nr_schedule_ue_spec(module_id_t module_id, + dci_payload.dmrs_sequence_initialization.val = pdsch_pdu->SCID; + LOG_D(NR_MAC, + "%4d.%2d DCI type 1 payload: freq_alloc %d (%d,%d,%d), " +- "time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", ++ "nrOfLayers %d, time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", + frame, + slot, + dci_payload.frequency_domain_assignment.val, + pdsch_pdu->rbStart, + pdsch_pdu->rbSize, + pdsch_pdu->BWPSize, ++ pdsch_pdu->nrOfLayers, + dci_payload.time_domain_assignment.val, + dci_payload.vrb_to_prb_mapping.val, + dci_payload.mcs, +diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +index f1005cea70..8f7c856a30 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c ++++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +@@ -405,6 +405,7 @@ void nr_preprocessor_phytest(module_id_t module_id, + + uint32_t target_ul_mcs = 9; + uint32_t target_ul_bw = 50; ++uint32_t target_ul_Nl = 1; + uint64_t ulsch_slot_bitmap = (1 << 8); + bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_t slot) + { +@@ -446,15 +447,21 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ + + const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; + const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; +- const uint8_t num_dmrs_cdm_grps_no_data = 1; ++ uint8_t num_dmrs_cdm_grps_no_data = 1; ++ if ((target_ul_Nl==4)||(target_ul_Nl==3)) ++ { ++ num_dmrs_cdm_grps_no_data = 2; ++ } ++ + /* we want to avoid a lengthy deduction of DMRS and other parameters in + * every TTI if we can save it, so check whether dci_format, TDA, or + * num_dmrs_cdm_grps_no_data has changed and only then recompute */ + NR_pusch_semi_static_t *ps = &sched_ctrl->pusch_semi_static; + if (ps->time_domain_allocation != tda + || ps->dci_format != dci_format ++ || ps->nrOfLayers != target_ul_Nl + || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) +- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, NULL,dci_format, tda, num_dmrs_cdm_grps_no_data, ps); ++ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, NULL,dci_format, tda, num_dmrs_cdm_grps_no_data,target_ul_Nl,ps); + + uint16_t rbStart = 0; + uint16_t rbSize; +@@ -525,6 +532,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ + sched_pusch->ul_harq_pid = sched_ctrl->retrans_ul_harq.head; + + /* Calculate TBS from MCS */ ++ ps->nrOfLayers = target_ul_Nl; + sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table); + sched_pusch->Qm = nr_get_Qm_ul(mcs, ps->mcs_table); + if (ps->pusch_Config->tp_pi2BPSK +@@ -539,7 +547,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ + ps->N_PRB_DMRS * ps->num_dmrs_symb, + 0, // nb_rb_oh + 0, +- 1 /* NrOfLayers */) ++ ps->nrOfLayers /* NrOfLayers */) + >> 3; + + /* mark the corresponding RBs as used */ +diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +index 1c0cb0353e..4583da876f 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c ++++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +@@ -355,6 +355,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, + long dci_format, + int tda, + uint8_t num_dmrs_cdm_grps_no_data, ++ uint8_t nrOfLayers, + NR_pusch_semi_static_t *ps) + { + ps->dci_format = dci_format; +@@ -371,11 +372,11 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, + + ps->pusch_Config = ubwp?ubwp->bwp_Dedicated->pusch_Config->choice.setup:(ubwpd ? ubwpd->pusch_Config->choice.setup : NULL); + if (ps->pusch_Config == NULL || !ps->pusch_Config->transformPrecoder) +- ps->transform_precoding = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; ++ ps->transformPrecoder = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; + else +- ps->transform_precoding = *ps->pusch_Config->transformPrecoder; ++ ps->transformPrecoder = *ps->pusch_Config->transformPrecoder; + const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; +- if (ps->transform_precoding) ++ if (ps->transformPrecoder) + ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config ? ps->pusch_Config->mcs_Table : NULL, + 0, + ps->dci_format, +@@ -392,6 +393,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, + num_dmrs_cdm_grps_no_data = 2; // in case of transform precoding - no Data sent in DMRS symbol + } + ++ ps->nrOfLayers = nrOfLayers; + ps->num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; + + /* DMRS calculations */ +@@ -689,8 +691,21 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, + "Non Codebook configuration non supported\n"); + dci_pdu_rel15->srs_resource_indicator.val = 0; // taking resource 0 for SRS + } ++ AssertFatal((pusch_pdu->Tpmi==0), "unsupport Tpmi\n"); ++ dci_pdu_rel15->precoding_information.val= 0; ++ if (pusch_pdu->nrOfLayers == 2) ++ { ++ dci_pdu_rel15->precoding_information.val = 4; ++ } ++ else if (pusch_pdu->nrOfLayers == 4) ++ { ++ dci_pdu_rel15->precoding_information.val = 11; ++ } ++ ++ // antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4 + // Antenna Ports +- dci_pdu_rel15->antenna_ports.val = 0; // TODO for now it is hardcoded, it should depends on cdm group no data and rank ++ dci_pdu_rel15->antenna_ports.val = 0; ++ + // DMRS sequence initialization + dci_pdu_rel15->dmrs_sequence_initialization.val = pusch_pdu->scid; + break; +@@ -699,11 +714,14 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, + } + + LOG_D(NR_MAC, +- "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n", ++ "%s() ULDCI type 0 payload: dci_format %d, freq_alloc %d, time_alloc %d, freq_hop_flag %d, precoding_information.val %d antenna_ports.val %d mcs %d tpc %d ndi %d rv %d\n", + __func__, ++ dci_format, + dci_pdu_rel15->frequency_domain_assignment.val, + dci_pdu_rel15->time_domain_assignment.val, + dci_pdu_rel15->frequency_hopping_flag.val, ++ dci_pdu_rel15->precoding_information.val, ++ dci_pdu_rel15->antenna_ports.val, + dci_pdu_rel15->mcs, + dci_pdu_rel15->tpc, + dci_pdu_rel15->ndi, +diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +index 9a6fa32b5f..269d6c5359 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c ++++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +@@ -905,7 +905,7 @@ bool allocate_ul_retransmission(module_id_t module_id, + NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters : &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + int rbStart = 0; // wrt BWP start + const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); +- ++ const uint8_t nrOfLayers = 1; + const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_bwp || ubwpd) ? 1 : 2; + const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + LOG_D(NR_MAC,"retInfo->time_domain_allocation = %d, tda = %d\n", retInfo->time_domain_allocation, tda); +@@ -926,8 +926,9 @@ bool allocate_ul_retransmission(module_id_t module_id, + + if (ps->time_domain_allocation != tda + || ps->dci_format != dci_format ++ || ps->nrOfLayers != nrOfLayers + || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) +- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); ++ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers,ps); + LOG_D(NR_MAC, "%s(): retransmission keeping TDA %d and TBS %d\n", __func__, tda, retInfo->tb_size); + } else { + /* the retransmission will use a different time domain allocation, check +@@ -939,7 +940,7 @@ bool allocate_ul_retransmission(module_id_t module_id, + rbSize++; + NR_pusch_semi_static_t temp_ps; + int dci_format = get_dci_format(sched_ctrl); +- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp,ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, &temp_ps); ++ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp,ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers, &temp_ps); + uint32_t new_tbs; + uint16_t new_rbSize; + bool success = nr_find_nb_rb(retInfo->Qm, +@@ -1094,7 +1095,7 @@ void pf_ul(module_id_t module_id, + if (max_num_ue < 0) + return; + +- LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d\n", min_rb, rbStart); ++ LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d num_dmrs_cdm_grps_no_data %d\n", min_rb, rbStart, ps->num_dmrs_cdm_grps_no_data); + while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++; + if (rbStart + min_rb >= bwpSize) { + LOG_W(NR_MAC, "cannot allocate continuous UL data for UE %d/RNTI %04x: no resources (rbStart %d, min_rb %d, bwpSize %d\n", +@@ -1106,13 +1107,15 @@ void pf_ul(module_id_t module_id, + /* we want to avoid a lengthy deduction of DMRS and other parameters in + * every TTI if we can save it, so check whether dci_format, TDA, or + * num_dmrs_cdm_grps_no_data has changed and only then recompute */ ++ const uint8_t nrOfLayers = 1; + const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_ubwp || ubwpd) ? 1 : 2; + int dci_format = get_dci_format(sched_ctrl); + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + if (ps->time_domain_allocation != tda + || ps->dci_format != dci_format ++ || ps->nrOfLayers != nrOfLayers + || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) +- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); ++ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data,nrOfLayers, ps); + NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; + sched_pusch->mcs = 9; + update_ul_ue_R_Qm(sched_pusch, ps); +@@ -1125,7 +1128,7 @@ void pf_ul(module_id_t module_id, + ps->N_PRB_DMRS * ps->num_dmrs_symb, + 0, // nb_rb_oh + 0, +- 1 /* NrOfLayers */) ++ ps->nrOfLayers) + >> 3; + + /* Mark the corresponding RBs as used */ +@@ -1205,13 +1208,15 @@ void pf_ul(module_id_t module_id, + /* we want to avoid a lengthy deduction of DMRS and other parameters in + * every TTI if we can save it, so check whether dci_format, TDA, or + * num_dmrs_cdm_grps_no_data has changed and only then recompute */ ++ const uint8_t nrOfLayers = 1; + const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_ubwp || ubwpd) ? 1 : 2; + int dci_format = get_dci_format(sched_ctrl); + const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + if (ps->time_domain_allocation != tda + || ps->dci_format != dci_format ++ || ps->nrOfLayers != nrOfLayers + || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) +- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); ++ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers,ps); + update_ul_ue_R_Qm(sched_pusch, ps); + + /* Calculate the current scheduling bytes and the necessary RBs */ +@@ -1473,7 +1478,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) + sched_ctrl->last_ul_slot = sched_pusch->slot; + + LOG_D(NR_MAC, +- "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d startSymbol %2d nb_symbol %2d dmrs_pos %x MCS %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched %6d est BSR %6d TPC %d\n", ++ "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d startSymbol %2d nb_symbol %2d dmrs_pos %x MCS %2d nrOfLayers %2d num_dmrs_cdm_grps_no_data %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched %6d est BSR %6d TPC %d\n", + frame, + slot, + rnti, +@@ -1486,6 +1491,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) + ps->nrOfSymbols, + ps->ul_dmrs_symb_pos, + sched_pusch->mcs, ++ ps->nrOfLayers, ++ ps->num_dmrs_cdm_grps_no_data, + sched_pusch->tb_size, + harq_id, + cur_harq->round, +@@ -1532,17 +1539,18 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) + pusch_pdu->qam_mod_order = sched_pusch->Qm; + pusch_pdu->mcs_index = sched_pusch->mcs; + pusch_pdu->mcs_table = ps->mcs_table; +- pusch_pdu->transform_precoding = ps->transform_precoding; ++ pusch_pdu->transformPrecoder = ps->transformPrecoder; + if (ps->pusch_Config && ps->pusch_Config->dataScramblingIdentityPUSCH) + pusch_pdu->data_scrambling_id = *ps->pusch_Config->dataScramblingIdentityPUSCH; + else + pusch_pdu->data_scrambling_id = *scc->physCellId; +- pusch_pdu->nrOfLayers = 1; ++ pusch_pdu->nrOfLayers = ps->nrOfLayers; ++ pusch_pdu->num_dmrs_cdm_grps_no_data = ps->num_dmrs_cdm_grps_no_data; + + /* FAPI: DMRS */ + pusch_pdu->ul_dmrs_symb_pos = ps->ul_dmrs_symb_pos; + pusch_pdu->dmrs_config_type = ps->dmrs_config_type; +- if (pusch_pdu->transform_precoding) { // transform precoding disabled ++ if (pusch_pdu->transformPrecoder) { // transform precoding disabled + long *scramblingid=NULL; + if (ps->NR_DMRS_UplinkConfig && pusch_pdu->scid == 0) + scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0; +@@ -1562,7 +1570,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) + } + pusch_pdu->scid = 0; // DMRS sequence initialization [TS38.211, sec 6.4.1.1.1] + pusch_pdu->num_dmrs_cdm_grps_no_data = ps->num_dmrs_cdm_grps_no_data; +- pusch_pdu->dmrs_ports = 1; ++ pusch_pdu->dmrs_ports = ((1<nrOfLayers) - 1); + + /* FAPI: Pusch Allocation in frequency domain */ + pusch_pdu->resource_alloc = 1; //type 1 +@@ -1588,7 +1596,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) + LOG_D(NR_MAC,"PUSCH PDU : data_scrambling_identity %x, dmrs_scrambling_id %x\n",pusch_pdu->data_scrambling_id,pusch_pdu->ul_dmrs_scrambling_id); + /* TRANSFORM PRECODING --------------------------------------------------------*/ + +- if (pusch_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled){ ++ if (pusch_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled){ + + // U as specified in section 6.4.1.1.1.2 in 38.211, if sequence hopping and group hopping are disabled + pusch_pdu->dfts_ofdm.low_papr_group_number = pusch_pdu->pusch_identity % 30; +diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +index 91221276b0..8a560580c9 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h ++++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +@@ -298,6 +298,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, + long dci_format, + int tda, + uint8_t num_dmrs_cdm_grps_no_data, ++ uint8_t nrOfLayers, + NR_pusch_semi_static_t *ps); + + uint8_t nr_get_tpc(int target, uint8_t cqi, int incr); +diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +index 27f42f4b1d..66b10dad42 100644 +--- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h ++++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +@@ -318,13 +318,14 @@ typedef struct NR_sched_pucch { + typedef struct NR_pusch_semi_static_t { + int dci_format; + int time_domain_allocation; ++ uint8_t nrOfLayers; + uint8_t num_dmrs_cdm_grps_no_data; + + int startSymbolIndex; + int nrOfSymbols; + + NR_PUSCH_Config_t *pusch_Config; +- uint8_t transform_precoding; ++ uint8_t transformPrecoder; + uint8_t mcs_table; + + long mapping_type; +diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c +index ff6923429e..52d03b5e56 100755 +--- a/openair2/RRC/NR/MESSAGES/asn1_msg.c ++++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c +@@ -1867,6 +1867,7 @@ int16_t do_RRCReconfiguration( + // 1, + // 1, + // carrier->pdsch_AntennaPorts, ++ // carrier->pusch_AntennaPorts, + // carrier->initial_csi_index[ue_context_p->local_uid + 1], + // ue_context_pP->local_uid); + +diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h +index 9465b9b4b0..6a94450450 100644 +--- a/openair2/RRC/NR/nr_rrc_proto.h ++++ b/openair2/RRC/NR/nr_rrc_proto.h +@@ -79,6 +79,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco + int scg_id, + int servCellIndex, + int dl_antenna_ports, ++ int ul_antenna_ports, + int do_csirs, + int initial_csi_index, + int uid); +@@ -90,6 +91,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon + NR_RRCReconfiguration_IEs_t *reconfig, + NR_CellGroupConfig_t *secondaryCellGroup, + int dl_antenna_ports, ++ int ul_antenna_ports, + int do_csirs, + int initial_csi_index, + int uid); +diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c +index f9fce48f41..22e43cbb6a 100644 +--- a/openair2/RRC/NR/rrc_gNB_nsa.c ++++ b/openair2/RRC/NR/rrc_gNB_nsa.c +@@ -241,6 +241,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ + reconfig_ies, + ue_context_p->ue_context.secondaryCellGroup, + carrier->pdsch_AntennaPorts, ++ carrier->pusch_AntennaPorts, + carrier->do_CSIRS, + carrier->initial_csi_index[ue_context_p->local_uid + 1], + ue_context_p->local_uid); +@@ -250,6 +251,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ + reconfig_ies, + ue_context_p->ue_context.secondaryCellGroup, + carrier->pdsch_AntennaPorts, ++ carrier->pusch_AntennaPorts, + carrier->do_CSIRS, + carrier->initial_csi_index[ue_context_p->local_uid + 1], + ue_context_p->local_uid); +@@ -348,7 +350,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ + rrc_mac_config_req_gNB(rrc->module_id, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, +- rrc->carrier.pusch_AntennaPorts, ++ rrc->carrier.pusch_AntennaPorts, + rrc->carrier.sib1_tda, + rrc->carrier.servingcellconfigcommon, + &rrc->carrier.mib, +diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c +index 980bd39c37..d03f361502 100644 +--- a/openair2/RRC/NR/rrc_gNB_reconfig.c ++++ b/openair2/RRC/NR/rrc_gNB_reconfig.c +@@ -55,6 +55,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco + int scg_id, + int servCellIndex, + int dl_antenna_ports, ++ int ul_antenna_ports, + int do_csirs, + int initial_csi_index, + int uid) { +@@ -745,14 +746,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco + pusch_Config->codebookSubset=calloc(1,sizeof(*pusch_Config->codebookSubset)); + *pusch_Config->codebookSubset = NR_PUSCH_Config__codebookSubset_nonCoherent; + pusch_Config->maxRank=calloc(1,sizeof(*pusch_Config->maxRank)); +- *pusch_Config->maxRank= 1; ++ *pusch_Config->maxRank= ul_antenna_ports; + pusch_Config->rbg_Size=NULL; + pusch_Config->uci_OnPUSCH=NULL; + pusch_Config->tp_pi2BPSK=NULL; + + /*------------------------------TRANSFORM PRECODING- -----------------------------------------------------------------------*/ + +- uint8_t transform_precoding = NR_PUSCH_Config__transformPrecoder_disabled; ++ uint8_t transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; + + // TBD: configure this from .conf file, Dedicated params cannot yet be configured in .conf file. + // Enable this to test transform precoding enabled from dedicated config. +@@ -764,13 +765,13 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco + + if (pusch_Config->transformPrecoder == NULL) { + if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder != NULL) +- transform_precoding = NR_PUSCH_Config__transformPrecoder_enabled; ++ transformPrecoder = NR_PUSCH_Config__transformPrecoder_enabled; + } + else +- transform_precoding = *pusch_Config->transformPrecoder; ++ transformPrecoder = *pusch_Config->transformPrecoder; + + +- if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled ) { ++ if (transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled ) { + /* Enable DMRS uplink config for transform precoding enabled */ + NR_DMRS_UplinkConfig->transformPrecodingEnabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingEnabled)); + NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity = NULL; +@@ -1348,6 +1349,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon + NR_RRCReconfiguration_IEs_t *reconfig, + NR_CellGroupConfig_t *secondaryCellGroup, + int dl_antenna_ports, ++ int ul_antenna_ports, + int do_csirs, + int initial_csi_index, + int uid) { +@@ -1363,6 +1365,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon + 1, + 1, + dl_antenna_ports, ++ ul_antenna_ports, + do_csirs, + initial_csi_index, + uid); +diff --git a/targets/ARCH/rfsimulator/simulator.c b/targets/ARCH/rfsimulator/simulator.c +index 13945998d7..20faa86591 100644 +--- a/targets/ARCH/rfsimulator/simulator.c ++++ b/targets/ARCH/rfsimulator/simulator.c +@@ -429,7 +429,7 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp + if (!alreadyLocked) + pthread_mutex_lock(&Sockmutex); + +- LOG_D(HW,"sending %d samples at time: %ld\n", nsamps, timestamp); ++ LOG_D(HW,"sending %d samples at time: %ld, nbAnt %d\n", nsamps, timestamp, nbAnt); + + for (int i=0; ibuf[i]; +@@ -623,7 +623,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest + } + + rfsimulator_state_t *t = device->priv; +- LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld\n", nsamps, t->nextTimestamp+nsamps); ++ LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld, nbAnt %d\n", nsamps, t->nextTimestamp+nsamps, nbAnt); + // deliver data from received data + // check if a UE is connected + int first_sock; +@@ -728,6 +728,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest + + for (int a=0; achannel_model != NULL ) // apply a channel model ++ { + rxAddInput( ptr->circularBuf, (struct complex16 *) samplesVoid[a], + a, + ptr->channel_model, +@@ -735,11 +736,21 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest + t->nextTimestamp, + CirSize + ); +- else { // no channel modeling ++ } ++ else ++ { // no channel modeling ++ ++ #if 0 + double H_awgn_mimo[4][4] ={{1.0, 0.5, 0.25, 0.125},//rx 0 + {0.5, 1.0, 0.5, 0.25}, //rx 1 + {0.25, 0.5, 1.0, 0.5}, //rx 2 + {0.125, 0.25, 0.5, 1.0}};//rx 3 ++ #else ++ double H_awgn_mimo[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 ++ {0.0, 1.0, 0.0, 0.0},//rx 1 ++ {0.0, 0.0, 1.0, 0.0},//rx 2 ++ {0.0, 0.0, 0.0, 1.0}};//rx 3 ++ #endif + + sample_t *out=(sample_t *)samplesVoid[a]; + int nbAnt_tx = ptr->th.nbAnt;//number of Tx antennas diff --git a/executables/nr-softmodem-common.h b/executables/nr-softmodem-common.h index c02155326b..1238b43c27 100644 --- a/executables/nr-softmodem-common.h +++ b/executables/nr-softmodem-common.h @@ -67,6 +67,7 @@ #define CONFIG_HLP_ITTIL "Generate ITTI analyzser logs (similar to wireshark logs but with more details)\n" #define CONFIG_HLP_DLMCS_PHYTEST "Set the downlink MCS for PHYTEST mode\n" #define CONFIG_HLP_DLNL_PHYTEST "Set the downlink nrOfLayers for PHYTEST mode\n" +#define CONFIG_HLP_ULNL_PHYTEST "Set the uplink nrOfLayers for PHYTEST mode\n" #define CONFIG_HLP_STMON "Enable processing timing measurement of lte softmodem on per subframe basis \n" #define CONFIG_HLP_MSLOTS "Skip the missed slots/subframes \n" #define CONFIG_HLP_ULMCS_PHYTEST "Set the uplink MCS for PHYTEST mode\n" diff --git a/executables/nr-softmodem.h b/executables/nr-softmodem.h index 896c363cbf..a11572c0e1 100644 --- a/executables/nr-softmodem.h +++ b/executables/nr-softmodem.h @@ -23,6 +23,7 @@ {"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, i8ptr:&threequarter_fs, defintval:0, TYPE_INT8, 0}, \ {"m" , CONFIG_HLP_DLMCS_PHYTEST,0, uptr:&target_dl_mcs, defintval:0, TYPE_UINT, 0}, \ {"l" , CONFIG_HLP_DLNL_PHYTEST,0, uptr:&target_dl_Nl, defintval:0, TYPE_UINT, 0}, \ + {"L" , CONFIG_HLP_ULNL_PHYTEST,0, uptr:&target_ul_Nl, defintval:0, TYPE_UINT, 0}, \ {"t" , CONFIG_HLP_ULMCS_PHYTEST,0, uptr:&target_ul_mcs, defintval:0, TYPE_UINT, 0}, \ {"M" , CONFIG_HLP_DLBW_PHYTEST,0, uptr:&target_dl_bw, defintval:0, TYPE_UINT, 0}, \ {"T" , CONFIG_HLP_ULBW_PHYTEST,0, uptr:&target_ul_bw, defintval:0, TYPE_UINT, 0}, \ @@ -36,6 +37,7 @@ extern threads_t threads; extern uint32_t target_dl_mcs; extern uint32_t target_dl_Nl; +extern uint32_t target_ul_Nl; extern uint32_t target_ul_mcs; extern uint32_t target_dl_bw; extern uint32_t target_ul_bw; diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 60ff40abeb..a23f91b1dd 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -278,7 +278,7 @@ void processSlotTX(void *arg) { int tx_slot_type = nr_ue_slot_select(cfg, proc->frame_tx, proc->nr_slot_tx); uint8_t gNB_id = 0; - LOG_D(PHY,"%d.%d => slot type %d\n",proc->frame_tx,proc->nr_slot_tx,tx_slot_type); + LOG_D(PHY,"processSlotTX %d.%d => slot type %d\n",proc->frame_tx,proc->nr_slot_tx,tx_slot_type); if (tx_slot_type == NR_UPLINK_SLOT || tx_slot_type == NR_MIXED_SLOT){ // trigger L2 to run ue_scheduler thru IF module @@ -372,7 +372,8 @@ void processSlotRX(void *arg) { LOG_D(PHY, "Sending Uplink data \n"); nr_ue_pusch_common_procedures(UE, proc->nr_slot_tx, - &UE->frame_parms,1); + &UE->frame_parms, + UE->frame_parms.nb_antennas_tx); } if (UE->UE_mode[gNB_id] > NOT_SYNCHED && UE->UE_mode[gNB_id] < PUSCH) { diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index ad5ea982c9..57fd284601 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -465,8 +465,11 @@ int main( int argc, char **argv ) { memset(UE[CC_id],0,sizeof(PHY_VARS_NR_UE)); set_options(CC_id, UE[CC_id]); - NR_UE_MAC_INST_t *mac = get_mac_inst(0); - + NR_DL_FRAME_PARMS *fp = &UE[CC_id]->frame_parms; + NR_UE_MAC_INST_t *mac = get_mac_inst(0); + + mac->phy_config.config_req.carrier_config.num_tx_ant = fp->nb_antennas_tx; + if (get_softmodem_params()->sa) { uint16_t nr_band = get_band(downlink_frequency[CC_id][0],uplink_frequency_offset[CC_id][0]); mac->nr_band = nr_band; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index 0e923931ad..8b93417cb0 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -298,9 +298,10 @@ typedef struct uint8_t qam_mod_order; uint8_t mcs_index; uint8_t mcs_table; - uint8_t transform_precoding; + uint8_t transformPrecoder; uint16_t data_scrambling_id; uint8_t nrOfLayers; + uint8_t Tpmi; //DMRS uint16_t ul_dmrs_symb_pos; uint8_t dmrs_config_type; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index 9d245446c8..58ff9037ce 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -1208,9 +1208,10 @@ typedef struct uint8_t qam_mod_order; uint8_t mcs_index; uint8_t mcs_table; - uint8_t transform_precoding; + uint8_t transformPrecoder; uint16_t data_scrambling_id; uint8_t nrOfLayers; + uint8_t Tpmi; //DMRS uint16_t ul_dmrs_symb_pos; uint8_t dmrs_config_type; diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c index 7cb74f8562..473b0900bc 100644 --- a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c +++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c @@ -941,9 +941,10 @@ static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, ui push8(pusch_pdu->qam_mod_order, ppWritePackedMsg, end) && push8(pusch_pdu->mcs_index, ppWritePackedMsg, end) && push8(pusch_pdu->mcs_table, ppWritePackedMsg, end) && - push8(pusch_pdu->transform_precoding, ppWritePackedMsg, end) && + push8(pusch_pdu->transformPrecoder, ppWritePackedMsg, end) && push16(pusch_pdu->data_scrambling_id, ppWritePackedMsg, end) && push8(pusch_pdu->nrOfLayers, ppWritePackedMsg, end) && + push8(pusch_pdu->Tpmi, ppWritePackedMsg, end) && push16(pusch_pdu->ul_dmrs_symb_pos, ppWritePackedMsg, end) && push8(pusch_pdu->dmrs_config_type, ppWritePackedMsg, end) && push16(pusch_pdu->ul_dmrs_scrambling_id, ppWritePackedMsg, end) && @@ -4445,9 +4446,10 @@ static uint8_t unpack_ul_tti_request_pusch_pdu(void *tlv, uint8_t **ppReadPacked pull8(ppReadPackedMsg, &pusch_pdu->qam_mod_order, end) && pull8(ppReadPackedMsg, &pusch_pdu->mcs_index, end) && pull8(ppReadPackedMsg, &pusch_pdu->mcs_table, end) && - pull8(ppReadPackedMsg, &pusch_pdu->transform_precoding, end) && + pull8(ppReadPackedMsg, &pusch_pdu->transformPrecoder, end) && pull16(ppReadPackedMsg, &pusch_pdu->data_scrambling_id, end) && pull8(ppReadPackedMsg, &pusch_pdu->nrOfLayers, end) && + pull8(ppReadPackedMsg, &pusch_pdu->Tpmi, end) && pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_symb_pos, end) && pull8(ppReadPackedMsg, &pusch_pdu->dmrs_config_type, end) && pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_scrambling_id, end) && diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index cd4848ea8c..2ac3230215 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -276,11 +276,21 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, pusch_vars[ULSCH_id]->ul_ch_magb0 = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); pusch_vars[ULSCH_id]->ul_ch_mag = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); pusch_vars[ULSCH_id]->ul_ch_magb = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); - pusch_vars[ULSCH_id]->rho = (int32_t **)malloc16_clear(n_buf*sizeof(int32_t*) ); + pusch_vars[ULSCH_id]->rho = (int32_t ***)malloc16_clear(Prx*sizeof(int32_t**) ); + pusch_vars[ULSCH_id]->llr_layers = (int16_t **)malloc16(max_ul_mimo_layers*sizeof(int32_t *) ); for (i=0; irxdataF_ext[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); pusch_vars[ULSCH_id]->rxdataF_ext2[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); + pusch_vars[ULSCH_id]->rho[i] = (int32_t **)malloc16_clear( NR_MAX_NB_LAYERS*NR_MAX_NB_LAYERS*sizeof(int32_t*)); + + for (int j=0; j< max_ul_mimo_layers; j++) + { + for (int k=0; krho[i][j*max_ul_mimo_layers+k]=(int32_t *)malloc16_clear( sizeof(int32_t) * fp->N_RB_UL*12*7*2 ); + } + } } for (i=0; iul_ch_estimates[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*fp->ofdm_symbol_size*2*fp->symbols_per_slot ); @@ -293,8 +303,12 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, pusch_vars[ULSCH_id]->ul_ch_mag0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); pusch_vars[ULSCH_id]->ul_ch_magb0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); pusch_vars[ULSCH_id]->ul_ch_mag[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); - pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); - pusch_vars[ULSCH_id]->rho[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*(fp->N_RB_UL*12*7*2) ); + pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); + } + + for (i=0; i< max_ul_mimo_layers; i++) + { + pusch_vars[ULSCH_id]->llr_layers[i] = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear } pusch_vars[ULSCH_id]->llr = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear pusch_vars[ULSCH_id]->ul_valid_re_per_slot = (int16_t *)malloc16_clear( sizeof(int16_t)*fp->symbols_per_slot); diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 4024ed1617..169db48e69 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -41,7 +41,7 @@ int nr_phy_init_RU(RU_t *ru) { int p; int re; - LOG_I(PHY,"Initializing RU signal buffers (if_south %s) nb_tx %d\n",ru_if_types[ru->if_south],ru->nb_tx); + LOG_I(PHY,"Initializing RU signal buffers (if_south %s) nb_tx %d, nb_rx %d\n",ru_if_types[ru->if_south],ru->nb_tx, ru->nb_rx); nfapi_nr_config_request_scf_t *cfg; ru->nb_log_antennas=0; diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index b60e77e814..11fb6e7d61 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -124,7 +124,8 @@ void phy_init_nr_ue_PUSCH(NR_UE_PUSCH *const pusch, AssertFatal( pusch, "pusch==0" ); for (int i=0; itxdataF_layers[i] = (int32_t *)malloc16_clear(NR_MAX_PUSCH_ENCODED_LENGTH*sizeof(int32_t)); + pusch->txdataF_layers[i] = (int32_t *)malloc16_clear((NR_MAX_PUSCH_ENCODED_LENGTH)*sizeof(int32_t *)); + pusch->txdataF_precoding[i] = (int32_t *)malloc16_clear((NR_MAX_PUSCH_ENCODED_LENGTH)*sizeof(int32_t *)); } } @@ -143,7 +144,7 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, uint16_t N_n_scid[2] = {0,1}; // [HOTFIX] This is a temporary implementation of scramblingID0 and scramblingID1 which are given by DMRS-UplinkConfig int n_scid; abstraction_flag = 0; - LOG_I(PHY, "Initializing UE vars (abstraction %u) for gNB TXant %u, UE RXant %u\n", abstraction_flag, fp->nb_antennas_tx, fp->nb_antennas_rx); + LOG_I(PHY, "Initializing UE vars (abstraction %u) for UE TXant %u, UE RXant %u\n", abstraction_flag, fp->nb_antennas_tx, fp->nb_antennas_rx); //LOG_D(PHY,"[MSC_NEW][FRAME 00000][PHY_UE][MOD %02u][]\n", ue->Mod_id+NB_gNB_INST); phy_init_nr_top(ue); // many memory allocation sizes are hard coded diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 1f164748bf..9a432e8615 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -110,7 +110,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, fr = filt8_r1; fmm = filt8_mm1; fml = filt8_ml1; - fmr = filt8_m1; + fmr = filt8_mm1; fdcl = filt8_dcl1; fdcr = filt8_dcr1; fdclh = filt8_dcl1_h; @@ -129,9 +129,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, //------------------generate DMRS------------------// - // transform precoding = 1 means disabled - if (pusch_pdu->transform_precoding == 1) { - nr_pusch_dmrs_rx(gNB, Ns, gNB->nr_gold_pusch_dmrs[pusch_pdu->scid][Ns][symbol], &pilot[0], 1000, 0, nb_rb_pusch, + if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { + nr_pusch_dmrs_rx(gNB, Ns, gNB->nr_gold_pusch_dmrs[pusch_pdu->scid][Ns][symbol], &pilot[0], (1000+p), 0, nb_rb_pusch, (pusch_pdu->bwp_start + pusch_pdu->rb_start)*NR_NB_SC_PER_RB, pusch_pdu->dmrs_config_type); } else { // if transform precoding or SC-FDMA is enabled in Uplink @@ -291,7 +290,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, printf("pilot %u : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); #endif - multadd_real_vector_complex_scalar(fml, + multadd_real_vector_complex_scalar(fm, ch, ul_ch, 8); diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 4fed3a468f..87e4d57362 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -137,21 +137,22 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, @param nb_rb_pusch The number of RBs allocated (used for Resource Allocation Type 1 in NR) @param frame_parms, Pointer to frame descriptor structure */ -void nr_ulsch_extract_rbs_single(int32_t **rxdataF, - NR_gNB_PUSCH *pusch_vars, - int slot, - unsigned char symbol, - uint8_t is_dmrs_symbol, - nfapi_nr_pusch_pdu_t *pusch_pdu, - NR_DL_FRAME_PARMS *frame_parms); +void nr_ulsch_extract_rbs(int32_t **rxdataF, + NR_gNB_PUSCH *pusch_vars, + int slot, + unsigned char symbol, + uint8_t is_dmrs_symbol, + nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_DL_FRAME_PARMS *frame_parms); void nr_ulsch_scale_channel(int32_t **ul_ch_estimates_ext, NR_DL_FRAME_PARMS *frame_parms, NR_gNB_ULSCH_t **ulsch_gNB, - uint8_t symbol, - uint8_t start_symbol, - uint16_t nb_rb, - pusch_dmrs_type_t pusch_dmrs_type); + uint8_t symbol, + uint8_t is_dmrs_symbol, + uint32_t len, + uint8_t nrOfLayers, + uint16_t nb_rb); /** \brief This function computes the average channel level over all allocated RBs and antennas (TX/RX) in order to compute output shift for compensated signal @param ul_ch_estimates_ext Channel estimates in allocated RBs @@ -185,9 +186,10 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, int **ul_ch_mag, int **ul_ch_magb, int **rxdataF_comp, - int **rho, + int ***rho, NR_DL_FRAME_PARMS *frame_parms, unsigned char symbol, + int length, uint8_t is_dmrs_symbol, unsigned char mod_order, uint8_t nrOfLayers, @@ -255,8 +257,8 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, @param mod_order modulation order */ void nr_ulsch_compute_llr(int32_t *rxdataF_comp, - int32_t **ul_ch_mag, - int32_t **ul_ch_magb, + int32_t *ul_ch_mag, + int32_t *ul_ch_magb, int16_t *ulsch_llr, uint32_t nb_rb, uint32_t nb_re, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index 4278338dbc..3ea19a9970 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -137,6 +137,38 @@ void nr_ulsch_unscrambling_optim(int16_t* llr, #endif } +void nr_ulsch_layer_demapping(int16_t *llr_cw, + uint8_t Nl, + uint8_t mod_order, + uint32_t length, + int16_t **llr_layers) +{ + + switch (Nl) { + + case 1: + memcpy((void*)llr_cw, (void*)llr_layers[0], (length)*sizeof(int16_t)); + break; + + case 2: + case 3: + case 4: + for (int i=0; i<(length/Nl/mod_order); i++) + { + for (int l=0; lnumber_of_nr_ulsch_max;i++) { diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h index 80c10d74b1..0e660351ae 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h @@ -82,6 +82,12 @@ void nr_ulsch_unscrambling_optim(int16_t* llr, uint32_t Nid, uint32_t n_RNTI); +void nr_ulsch_layer_demapping(int16_t *llr_cw, + uint8_t Nl, + uint8_t mod_order, + uint32_t length, + int16_t **llr_layers); + void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 28dd5ab219..8314c83ad9 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -302,26 +302,27 @@ void nr_idft(int32_t *z, uint32_t Msc_PUSCH) } -void nr_ulsch_extract_rbs_single(int32_t **rxdataF, - NR_gNB_PUSCH *pusch_vars, - int slot, - unsigned char symbol, - uint8_t is_dmrs_symbol, - nfapi_nr_pusch_pdu_t *pusch_pdu, - NR_DL_FRAME_PARMS *frame_parms) +void nr_ulsch_extract_rbs(int32_t **rxdataF, + NR_gNB_PUSCH *pusch_vars, + int slot, + unsigned char symbol, + uint8_t is_dmrs_symbol, + nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_DL_FRAME_PARMS *frame_parms) { unsigned short start_re, re, nb_re_pusch; - unsigned char aarx; + unsigned char aarx, aatx; uint32_t rxF_ext_index = 0; uint32_t ul_ch0_ext_index = 0; uint32_t ul_ch0_index = 0; - uint8_t k_prime; - uint16_t n; + //uint8_t k_prime; + //uint16_t n; int16_t *rxF,*rxF_ext; int *ul_ch0,*ul_ch0_ext; - uint8_t delta = 0; + //uint8_t delta = 0; int soffset = (slot&3)*frame_parms->symbols_per_slot*frame_parms->ofdm_symbol_size; + #ifdef DEBUG_RB_EXT printf("--------------------symbol = %d-----------------------\n", symbol); @@ -329,7 +330,7 @@ void nr_ulsch_extract_rbs_single(int32_t **rxdataF, #endif - uint8_t is_dmrs_re; + uint8_t is_data_re; start_re = (frame_parms->first_carrier_offset + (pusch_pdu->rb_start + pusch_pdu->bwp_start) * NR_NB_SC_PER_RB)%frame_parms->ofdm_symbol_size; nb_re_pusch = NR_NB_SC_PER_RB * pusch_pdu->rb_size; #ifdef __AVX2__ @@ -338,64 +339,95 @@ void nr_ulsch_extract_rbs_single(int32_t **rxdataF, int nb_re_pusch2 = nb_re_pusch; #endif - for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { + for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) + { rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol * frame_parms->ofdm_symbol_size)]; rxF_ext = (int16_t *)&pusch_vars->rxdataF_ext[aarx][symbol * nb_re_pusch2]; // [hna] rxdataF_ext isn't contiguous in order to solve an alignment problem ib llr computation in case of mod_order = 4, 6 - - ul_ch0 = &pusch_vars->ul_ch_estimates[aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available - - ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aarx][symbol*nb_re_pusch2]; - - n = 0; - k_prime = 0; - rxF_ext_index = 0; - ul_ch0_ext_index = 0; - ul_ch0_index = 0; - - if (is_dmrs_symbol == 0) { + + if (is_dmrs_symbol == 0) + { // //rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); - if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) { + if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) + { memcpy1((void*)rxF_ext, (void*)&rxF[start_re*2], nb_re_pusch*sizeof(int32_t)); - } else { - int neg_length = frame_parms->ofdm_symbol_size-start_re; - int pos_length = nb_re_pusch-neg_length; + } + else + { + int neg_length = frame_parms->ofdm_symbol_size-start_re; + int pos_length = nb_re_pusch-neg_length; + memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); + memcpy1((void*)&rxF_ext[2*neg_length],(void*)rxF,pos_length*sizeof(int32_t)); + } - memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); - memcpy1((void*)&rxF_ext[2*neg_length],(void*)rxF,pos_length*sizeof(int32_t)); + #ifdef SUPPORT_PMI_MATRIC + for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) + #else + aatx = aarx; + #endif + { + ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available + ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; + + memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); } - memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); } - else { - for (re = 0; re < nb_re_pusch; re++) { + else + { + #ifdef SUPPORT_PMI_MATRIC + for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) + #else + aatx = aarx; + #endif + { + ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available + ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; - is_dmrs_re = (re == get_dmrs_freq_idx_ul(n, k_prime, delta, pusch_pdu->dmrs_config_type)); + //n = 0; + //k_prime = 0; + rxF_ext_index = 0; + ul_ch0_ext_index = 0; + ul_ch0_index = 0; + + for (re = 0; re < nb_re_pusch; re++) + { + uint16_t k= start_re+re; + + is_data_re = allowed_xlsch_re_in_dmrs_symbol(k, start_re, frame_parms->ofdm_symbol_size, pusch_pdu->num_dmrs_cdm_grps_no_data, pusch_pdu->dmrs_config_type); + + if (++k >= frame_parms->ofdm_symbol_size) + { + k -= frame_parms->ofdm_symbol_size; + } -#ifdef DEBUG_RB_EXT - printf("re = %d, kprime %d, n %d, is_dmrs_symbol = %d, symbol = %d\n", re, k_prime, n, is_dmrs_symbol, symbol); -#endif + #ifdef DEBUG_RB_EXT + printf("re = %d, kprime %d, n %d, is_dmrs_symbol = %d, symbol = %d\n", re, k_prime, n, is_dmrs_symbol, symbol); + #endif - /* save only data and respective channel estimates */ - if (is_dmrs_re == 0) { - rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); - rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); - ul_ch0_ext[ul_ch0_ext_index] = ul_ch0[ul_ch0_index]; + /* save only data and respective channel estimates */ + if (is_data_re == 1) + { + if (aatx == aarx) + { + rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); + rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); + rxF_ext_index +=2; + } + + ul_ch0_ext[ul_ch0_ext_index] = ul_ch0[ul_ch0_index]; + ul_ch0_ext_index++; -#ifdef DEBUG_RB_EXT - printf("dmrs symb %d: rxF_ext[%d] = (%d,%d), ul_ch0_ext[%d] = (%d,%d)\n", + #ifdef DEBUG_RB_EXT + printf("dmrs symb %d: rxF_ext[%d] = (%d,%d), ul_ch0_ext[%d] = (%d,%d)\n", is_dmrs_symbol,rxF_ext_index>>1, rxF_ext[rxF_ext_index],rxF_ext[rxF_ext_index+1], ul_ch0_ext_index, ((int16_t*)&ul_ch0_ext[ul_ch0_ext_index])[0], ((int16_t*)&ul_ch0_ext[ul_ch0_ext_index])[1]); -#endif - ul_ch0_ext_index++; - rxF_ext_index +=2; - } else { - n += k_prime; - k_prime ^= 1; + #endif + } + ul_ch0_index++; } - ul_ch0_index++; } } } @@ -406,16 +438,19 @@ void nr_ulsch_scale_channel(int **ul_ch_estimates_ext, NR_gNB_ULSCH_t **ulsch_gNB, uint8_t symbol, uint8_t is_dmrs_symbol, - unsigned short nb_rb, - pusch_dmrs_type_t pusch_dmrs_type) + uint32_t len, + uint8_t nrOfLayers, + unsigned short nb_rb) { #if defined(__x86_64__)||defined(__i386__) short rb, ch_amp; - unsigned char aarx; + unsigned char aarx,aatx; __m128i *ul_ch128, ch_amp128; + uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); + // Determine scaling amplitude based the symbol ch_amp = 1024*8; //((pilots) ? (ulsch_gNB[0]->sqrt_rho_b) : (ulsch_gNB[0]->sqrt_rho_a)); @@ -431,35 +466,26 @@ void nr_ulsch_scale_channel(int **ul_ch_estimates_ext, int off = 0; #endif - for (aarx=0; aarx < frame_parms->nb_antennas_rx; aarx++) { - - ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aarx][symbol*(off+(nb_rb*NR_NB_SC_PER_RB))]; - - if (is_dmrs_symbol==1){ - if (pusch_dmrs_type == pusch_dmrs_type1) - nb_rb = nb_rb>>1; - else - nb_rb = (2*nb_rb)/3; - } - - for (rb=0;rbnb_antennas_rx; aarx++) + { + ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*NR_NB_SC_PER_RB))]; + for (rb=0;rb < nb_rb_0;rb++) + { ul_ch128[0] = _mm_mulhi_epi16(ul_ch128[0], ch_amp128); ul_ch128[0] = _mm_slli_epi16(ul_ch128[0], 3); ul_ch128[1] = _mm_mulhi_epi16(ul_ch128[1], ch_amp128); ul_ch128[1] = _mm_slli_epi16(ul_ch128[1], 3); - if (is_dmrs_symbol) { - ul_ch128+=2; - } else { - ul_ch128[2] = _mm_mulhi_epi16(ul_ch128[2], ch_amp128); - ul_ch128[2] = _mm_slli_epi16(ul_ch128[2], 3); - ul_ch128+=3; - - } + ul_ch128[2] = _mm_mulhi_epi16(ul_ch128[2], ch_amp128); + ul_ch128[2] = _mm_slli_epi16(ul_ch128[2], 3); + ul_ch128+=3; } - } + } +} #endif } @@ -481,6 +507,8 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, int16_t x = factor2(len); int16_t y = (len)>>x; + + uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); #ifdef __AVX2__ int off = ((nb_rb&1) == 1)? 4:0; @@ -489,13 +517,16 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, #endif for (aatx = 0; aatx < nrOfLayers; aatx++) - for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { + { + for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) + { //clear average level avg128U = _mm_setzero_si128(); ul_ch128=(__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - for (rb = 0; rb < len/12; rb++) { + for (rb = 0; rb < nb_rb_0; rb++) + { avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[0], ul_ch128[0]), x)); avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[1], ul_ch128[1]), x)); avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[2], ul_ch128[2]), x)); @@ -508,7 +539,8 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, ((int32_t*)&avg128U)[3]) / y; } - + } + _mm_empty(); _m_empty(); @@ -520,7 +552,7 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, int16x4_t *ul_ch128; symbol_mod = (symbol>=(7-frame_parms->Ncp)) ? symbol-(7-frame_parms->Ncp) : symbol; - + uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); for (aatx=0; aatxnb_antennas_rx; aarx++) { //clear average level @@ -529,7 +561,7 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, ul_ch128 = (int16x4_t *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*frame_parms->N_RB_UL*12]; - for (rb = 0; rb < nb_rb; rb++) { + for (rb = 0; rb < nb_rb_0; rb++) { // printf("rb %d : ",rb); // print_shorts("ch",&ul_ch128[0]); avg128U = vqaddq_s32(avg128U, vmull_s16(ul_ch128[0], ul_ch128[0])); @@ -568,14 +600,20 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, #endif } + + +//============================================================================================== +// Pre-processing for LLR computation +//============================================================================================== void nr_ulsch_channel_compensation(int **rxdataF_ext, int **ul_ch_estimates_ext, int **ul_ch_mag, int **ul_ch_magb, int **rxdataF_comp, - int **rho, + int ***rho, NR_DL_FRAME_PARMS *frame_parms, unsigned char symbol, + int length, uint8_t is_dmrs_symbol, unsigned char mod_order, uint8_t nrOfLayers, @@ -637,20 +675,24 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, __m128i mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3,QAM_amp128={0},QAM_amp128b={0}; QAM_amp128b = _mm_setzero_si128(); - for (aatx=0; aatxnb_antennas_rx; aarx++) { - + for (aarx=0; aarxnb_antennas_rx; aarx++) + { ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; ul_ch_mag128 = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; ul_ch_mag128b = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; @@ -658,8 +700,10 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, rxdataF_comp128 = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - for (rb=0; rb2) { + for (rb=0; rb2) + { // get channel amplitude if not QPSK //print_shorts("ch:",(int16_t*)&ul_ch128[0]); @@ -679,6 +723,8 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, ul_ch_mag128[0] = _mm_mulhi_epi16(ul_ch_mag128[0],QAM_amp128); ul_ch_mag128[0] = _mm_slli_epi16(ul_ch_mag128[0],1); + ul_ch_mag128b[0] = _mm_mulhi_epi16(ul_ch_mag128b[0],QAM_amp128b); + ul_ch_mag128b[0] = _mm_slli_epi16(ul_ch_mag128b[0],1); // print_ints("ch: = ",(int32_t*)&mmtmpD0); // print_shorts("QAM_amp:",(int16_t*)&QAM_amp128); // print_shorts("mag:",(int16_t*)&ul_ch_mag128[0]); @@ -687,30 +733,24 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, ul_ch_mag128b[1] = ul_ch_mag128[1]; ul_ch_mag128[1] = _mm_mulhi_epi16(ul_ch_mag128[1],QAM_amp128); ul_ch_mag128[1] = _mm_slli_epi16(ul_ch_mag128[1],1); - - if (is_dmrs_symbol==0) { - mmtmpD0 = _mm_madd_epi16(ul_ch128[2],ul_ch128[2]); - mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); - mmtmpD1 = _mm_packs_epi32(mmtmpD0,mmtmpD0); - - ul_ch_mag128[2] = _mm_unpacklo_epi16(mmtmpD1,mmtmpD1); - ul_ch_mag128b[2] = ul_ch_mag128[2]; - - ul_ch_mag128[2] = _mm_mulhi_epi16(ul_ch_mag128[2],QAM_amp128); - ul_ch_mag128[2] = _mm_slli_epi16(ul_ch_mag128[2],1); - } - - ul_ch_mag128b[0] = _mm_mulhi_epi16(ul_ch_mag128b[0],QAM_amp128b); - ul_ch_mag128b[0] = _mm_slli_epi16(ul_ch_mag128b[0],1); - - - ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128b); + + ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128); ul_ch_mag128b[1] = _mm_slli_epi16(ul_ch_mag128b[1],1); - if (is_dmrs_symbol==0) { - ul_ch_mag128b[2] = _mm_mulhi_epi16(ul_ch_mag128b[2],QAM_amp128b); - ul_ch_mag128b[2] = _mm_slli_epi16(ul_ch_mag128b[2],1); - } + mmtmpD0 = _mm_madd_epi16(ul_ch128[2],ul_ch128[2]); + mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); + mmtmpD1 = _mm_packs_epi32(mmtmpD0,mmtmpD0); + + ul_ch_mag128[2] = _mm_unpacklo_epi16(mmtmpD1,mmtmpD1); + ul_ch_mag128b[2] = ul_ch_mag128[2]; + + ul_ch_mag128[2] = _mm_mulhi_epi16(ul_ch_mag128[2],QAM_amp128); + ul_ch_mag128[2] = _mm_slli_epi16(ul_ch_mag128[2],1); + + + ul_ch_mag128b[2] = _mm_mulhi_epi16(ul_ch_mag128b[2],QAM_amp128b); + ul_ch_mag128b[2] = _mm_slli_epi16(ul_ch_mag128b[2],1); + } // multiply by conjugated channel @@ -755,117 +795,157 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, // print_shorts("ch:",ul_ch128+1); // print_shorts("pack:",rxdataF_comp128+1); - if (is_dmrs_symbol==0) { - // multiply by conjugated channel - mmtmpD0 = _mm_madd_epi16(ul_ch128[2],rxdataF128[2]); - // mmtmpD0 contains real part of 4 consecutive outputs (32-bit) - mmtmpD1 = _mm_shufflelo_epi16(ul_ch128[2],_MM_SHUFFLE(2,3,0,1)); - mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); - mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)conjugate); - mmtmpD1 = _mm_madd_epi16(mmtmpD1,rxdataF128[2]); - // mmtmpD1 contains imag part of 4 consecutive outputs (32-bit) - mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); - mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift); - mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); - mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); + // multiply by conjugated channel + mmtmpD0 = _mm_madd_epi16(ul_ch128[2],rxdataF128[2]); + // mmtmpD0 contains real part of 4 consecutive outputs (32-bit) + mmtmpD1 = _mm_shufflelo_epi16(ul_ch128[2],_MM_SHUFFLE(2,3,0,1)); + mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); + mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)conjugate); + mmtmpD1 = _mm_madd_epi16(mmtmpD1,rxdataF128[2]); + // mmtmpD1 contains imag part of 4 consecutive outputs (32-bit) + mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); + mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift); + mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); + mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); - rxdataF_comp128[2] = _mm_packs_epi32(mmtmpD2,mmtmpD3); - // print_shorts("rx:",rxdataF128+2); - // print_shorts("ch:",ul_ch128+2); - // print_shorts("pack:",rxdataF_comp128+2); - - ul_ch128+=3; - ul_ch_mag128+=3; - ul_ch_mag128b+=3; - rxdataF128+=3; - rxdataF_comp128+=3; - } else { // we have a smaller PUSCH in symbols with pilots so skip last group of 4 REs and increment less - ul_ch128+=2; - ul_ch_mag128+=2; - ul_ch_mag128b+=2; - rxdataF128+=2; - rxdataF_comp128+=2; - } + rxdataF_comp128[2] = _mm_packs_epi32(mmtmpD2,mmtmpD3); + //print_shorts("rx:",rxdataF128+2); + //print_shorts("ch:",ul_ch128+2); + //print_shorts("pack:",rxdataF_comp128+2); + ul_ch128+=3; + ul_ch_mag128+=3; + ul_ch_mag128b+=3; + rxdataF128+=3; + rxdataF_comp128+=3; } } } if (rho) { + //we compute the Tx correlation matrix for each Rx antenna + //As an example the 2x2 MIMO case requires + //rho[aarx][nb_aatx*nb_aatx] = [cov(H_aarx_0,H_aarx_0) cov(H_aarx_0,H_aarx_1) + // cov(H_aarx_1,H_aarx_0) cov(H_aarx_1,H_aarx_1)], aarx=0,...,nb_antennas_rx-1 - for (aarx=0; aarxnb_antennas_rx; aarx++) { - rho128 = (__m128i *)&rho[aarx][symbol*frame_parms->N_RB_UL*12]; - ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aarx][symbol*frame_parms->N_RB_UL*12]; - ul_ch128_2 = (__m128i *)&ul_ch_estimates_ext[2+aarx][symbol*frame_parms->N_RB_UL*12]; + int avg_rho_re[frame_parms->nb_antennas_rx][nrOfLayers*nrOfLayers]; + int avg_rho_im[frame_parms->nb_antennas_rx][nrOfLayers*nrOfLayers]; - for (rb=0; rbnb_antennas_rx; aarx++) + { + for (aatx=0; aatx < nrOfLayers; aatx++) + { + ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; + + for (int atx=0; atx< nrOfLayers; atx++) + { + avg_rho_re[aarx][aatx*nrOfLayers+atx] = 0; + avg_rho_im[aarx][aatx*nrOfLayers+atx] = 0; + rho128 = (__m128i *)&rho[aarx][aatx*nrOfLayers+atx][symbol*(off+(nb_rb*12))]; + ul_ch128_2 = (__m128i *)&ul_ch_estimates_ext[atx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - // mmtmpD0 contains real part of 4 consecutive outputs (32-bit) - mmtmpD1 = _mm_shufflelo_epi16(ul_ch128[0],_MM_SHUFFLE(2,3,0,1)); - mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); - mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)&conjugate[0]); - // print_ints("im",&mmtmpD1); - mmtmpD1 = _mm_madd_epi16(mmtmpD1,ul_ch128_2[0]); - // mmtmpD1 contains imag part of 4 consecutive outputs (32-bit) - mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); - // print_ints("re(shift)",&mmtmpD0); - mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift); - // print_ints("im(shift)",&mmtmpD1); - mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); - mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); - // print_ints("c0",&mmtmpD2); - // print_ints("c1",&mmtmpD3); - rho128[0] = _mm_packs_epi32(mmtmpD2,mmtmpD3); + for (rb=0; rbrx_correlation[0][0][aarx] = signal_energy(&rho[aarx][aatx*nb_aatx+atx][symbol*nb_rb*12],rb*12); + avg_rho_re[aarx][aatx*nrOfLayers+atx] = 16*avg_rho_re[aarx][aatx*nrOfLayers+atx]/(nb_rb*12); + avg_rho_im[aarx][aatx*nrOfLayers+atx] = 16*avg_rho_im[aarx][aatx*nrOfLayers+atx]/(nb_rb*12); + //printf("rho[rx]%d tx%d tx%d = Re: %d Im: %d\n",aarx, aatx,atx, avg_rho_re[aarx][aatx*nb_aatx+atx], avg_rho_im[aarx][aatx*nb_aatx+atx]); + } + } } - } } @@ -1105,19 +1185,23 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, } void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, - int32_t **rxdataF_comp, - int32_t **ul_ch_mag, - int32_t **ul_ch_magb, - uint8_t symbol, - uint16_t nb_rb) { + int32_t **rxdataF_comp, + int32_t **ul_ch_mag, + int32_t **ul_ch_magb, + int32_t ***rho, + uint8_t nrOfLayers, + uint8_t symbol, + uint16_t nb_rb, + int length) { int n_rx = frame_parms->nb_antennas_rx; #if defined(__x86_64__) || defined(__i386__) - __m128i *rxdataF_comp128[1+n_rx],*ul_ch_mag128[1+n_rx],*ul_ch_mag128b[1+n_rx]; + __m128i *rxdataF_comp128[2],*ul_ch_mag128[2],*ul_ch_mag128b[2]; #elif defined(__arm__) int16x8_t *rxdataF_comp128_0,*ul_ch_mag128_0,*ul_ch_mag128_0b; int16x8_t *rxdataF_comp128_1,*ul_ch_mag128_1,*ul_ch_mag128_1b; #endif int32_t i; + uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); #ifdef __AVX2__ int off = ((nb_rb&1) == 1)? 4:0; @@ -1125,23 +1209,34 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, int off = 0; #endif - if (frame_parms->nb_antennas_rx>1) { -#if defined(__x86_64__) || defined(__i386__) - int nb_re = nb_rb*12; - for (int aa=0;aanb_antennas_rx;aa++) { - rxdataF_comp128[aa] = (__m128i *)&rxdataF_comp[aa][(symbol*(nb_re + off))]; - ul_ch_mag128[aa] = (__m128i *)&ul_ch_mag[aa][(symbol*(nb_re + off))]; - ul_ch_mag128b[aa] = (__m128i *)&ul_ch_magb[aa][(symbol*(nb_re + off))]; - } - for (int aa=1;aanb_antennas_rx;aa++) { - // MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM llr computation) - for (i=0; i 1) + { + #if defined(__x86_64__) || defined(__i386__) + for (int aatx=0; aatxnb_antennas_rx][(symbol*(nb_re + off))]; + ul_ch_mag128[0] = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx][(symbol*(nb_re + off))]; + ul_ch_mag128b[0] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx][(symbol*(nb_re + off))]; + + for (int aa=1;aa < n_rx;aa++) + { + rxdataF_comp128[1] = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; + ul_ch_mag128[1] = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; + ul_ch_mag128b[1] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; + + // MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM llr computation) + for (i=0; iN_RB_DL*12]; rxdataF_comp128_1 = (int16x8_t *)&rxdataF_comp[1][symbol*frame_parms->N_RB_DL*12]; ul_ch_mag128_0 = (int16x8_t *)&ul_ch_mag[0][symbol*frame_parms->N_RB_DL*12]; @@ -1156,7 +1251,7 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, ul_ch_mag128_0b[i] = vhaddq_s16(ul_ch_mag128_0b[i],ul_ch_mag128_1b[i]); rxdataF_comp128_0[i] = vqaddq_s16(rxdataF_comp128_0[i],(*(int16x8_t *)&jitterc[0])); } -#endif + #endif } #if defined(__x86_64__) || defined(__i386__) @@ -1165,6 +1260,696 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, #endif } +/* Zero Forcing Rx function: nr_det_HhH() + * + * + * */ +void nr_ulsch_det_HhH(int32_t *after_mf_00,//a + int32_t *after_mf_01,//b + int32_t *after_mf_10,//c + int32_t *after_mf_11,//d + int32_t *det_fin,//1/ad-bc + unsigned short nb_rb, + unsigned char symbol, + int32_t shift) +{ + int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; + unsigned short rb; + __m128i *after_mf_00_128,*after_mf_01_128, *after_mf_10_128, *after_mf_11_128, ad_re_128, bc_re_128; //ad_im_128, bc_im_128; + __m128i *det_fin_128, det_re_128; //det_im_128, tmp_det0, tmp_det1; + + after_mf_00_128 = (__m128i *)after_mf_00; + after_mf_01_128 = (__m128i *)after_mf_01; + after_mf_10_128 = (__m128i *)after_mf_10; + after_mf_11_128 = (__m128i *)after_mf_11; + + det_fin_128 = (__m128i *)det_fin; + + for (rb=0; rb<3*nb_rb; rb++) { + + //complex multiplication (I_a+jQ_a)(I_d+jQ_d) = (I_aI_d - Q_aQ_d) + j(Q_aI_d + I_aQ_d) + //The imag part is often zero, we compute only the real part + ad_re_128 = _mm_sign_epi16(after_mf_00_128[0],*(__m128i*)&nr_conjug2[0]); + ad_re_128 = _mm_madd_epi16(ad_re_128,after_mf_11_128[0]); //Re: I_a0*I_d0 - Q_a1*Q_d1 + //ad_im_128 = _mm_shufflelo_epi16(after_mf_00_128[0],_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + //ad_im_128 = _mm_shufflehi_epi16(ad_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + //ad_im_128 = _mm_madd_epi16(ad_im_128,after_mf_11_128[0]);//Im: (Q_aI_d + I_aQ_d) + + //complex multiplication (I_b+jQ_b)(I_c+jQ_c) = (I_bI_c - Q_bQ_c) + j(Q_bI_c + I_bQ_c) + //The imag part is often zero, we compute only the real part + bc_re_128 = _mm_sign_epi16(after_mf_01_128[0],*(__m128i*)&nr_conjug2[0]); + bc_re_128 = _mm_madd_epi16(bc_re_128,after_mf_10_128[0]); //Re: I_b0*I_c0 - Q_b1*Q_c1 + //bc_im_128 = _mm_shufflelo_epi16(after_mf_01_128[0],_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_b0 Q_b1 I_b2 Q_b3]_64bits to [Q_b1 I_b0 Q_b3 I_b2]_64bits + //bc_im_128 = _mm_shufflehi_epi16(bc_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_b0 Q_b1 I_b2 Q_b3]_64bits to [Q_b1 I_b0 Q_b3 I_b2]_64bits + //bc_im_128 = _mm_madd_epi16(bc_im_128,after_mf_10_128[0]);//Im: (Q_bI_c + I_bQ_c) + + det_re_128 = _mm_sub_epi32(ad_re_128, bc_re_128); + //det_im_128 = _mm_sub_epi32(ad_im_128, bc_im_128); + + //det in Q30 format + det_fin_128[0] = _mm_abs_epi32(det_re_128); + + +#ifdef DEBUG_DLSCH_DEMOD + printf("\n Computing det_HhH_inv \n"); + //print_ints("det_re_128:",(int32_t*)&det_re_128); + //print_ints("det_im_128:",(int32_t*)&det_im_128); + print_ints("det_fin_128:",(int32_t*)&det_fin_128[0]); +#endif + det_fin_128+=1; + after_mf_00_128+=1; + after_mf_01_128+=1; + after_mf_10_128+=1; + after_mf_11_128+=1; + } + _mm_empty(); + _m_empty(); +} + +/* Zero Forcing Rx function: nr_inv_comp_muli + * Complex number multi: z = x*y + * = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) + * */ +__m128i nr_ulsch_inv_comp_muli(__m128i input_x, + __m128i input_y) +{ + int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; + + __m128i xy_re_128, xy_im_128; + __m128i output_z, tmp_z0, tmp_z1; + + // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) + + // the real part + xy_re_128 = _mm_sign_epi16(input_x,*(__m128i*)&nr_conjug2[0]); + xy_re_128 = _mm_madd_epi16(xy_re_128,input_y); //Re: (x_re*y_re - x_im*y_im) + + // the imag part + xy_im_128 = _mm_shufflelo_epi16(input_x,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + xy_im_128 = _mm_shufflehi_epi16(xy_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + xy_im_128 = _mm_madd_epi16(xy_im_128,input_y);//Im: (x_im*y_re + x_re*y_im) + + //convert back to Q15 before packing + xy_re_128 = _mm_srai_epi32(xy_re_128,4);//(2^15/64*2*16) + xy_im_128 = _mm_srai_epi32(xy_im_128,4); + + tmp_z0 = _mm_unpacklo_epi32(xy_re_128,xy_im_128); + //print_ints("unpack lo:",&tmp_z0[0]); + tmp_z1 = _mm_unpackhi_epi32(xy_re_128,xy_im_128); + //print_ints("unpack hi:",&tmp_z1[0]); + output_z = _mm_packs_epi32(tmp_z0,tmp_z1); + + _mm_empty(); + _m_empty(); + return(output_z); +} + +/* Zero Forcing Rx function: nr_conjch0_mult_ch1() + * + * + * */ +void nr_ulsch_conjch0_mult_ch1(int *ch0, + int *ch1, + int32_t *ch0conj_ch1, + unsigned short nb_rb, + unsigned char output_shift0) +{ + //This function is used to compute multiplications in H_hermitian * H matrix + short nr_conjugate[8]__attribute__((aligned(16))) = {-1,1,-1,1,-1,1,-1,1}; + unsigned short rb; + __m128i *dl_ch0_128,*dl_ch1_128, *ch0conj_ch1_128, mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3; + + dl_ch0_128 = (__m128i *)ch0; + dl_ch1_128 = (__m128i *)ch1; + + ch0conj_ch1_128 = (__m128i *)ch0conj_ch1; + + for (rb=0; rb<3*nb_rb; rb++) { + + mmtmpD0 = _mm_madd_epi16(dl_ch0_128[0],dl_ch1_128[0]); + mmtmpD1 = _mm_shufflelo_epi16(dl_ch0_128[0],_MM_SHUFFLE(2,3,0,1)); + mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); + mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)&nr_conjugate[0]); + mmtmpD1 = _mm_madd_epi16(mmtmpD1,dl_ch1_128[0]); + mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift0); + mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift0); + mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); + mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); + + ch0conj_ch1_128[0] = _mm_packs_epi32(mmtmpD2,mmtmpD3); + + /*printf("\n Computing conjugates \n"); + print_shorts("ch0:",(int16_t*)&dl_ch0_128[0]); + print_shorts("ch1:",(int16_t*)&dl_ch1_128[0]); + print_shorts("pack:",(int16_t*)&ch0conj_ch1_128[0]);*/ + + dl_ch0_128+=1; + dl_ch1_128+=1; + ch0conj_ch1_128+=1; + } + _mm_empty(); + _m_empty(); +} +__m128i nr_ulsch_comp_muli_sum(__m128i input_x, + __m128i input_y, + __m128i input_w, + __m128i input_z, + __m128i det) +{ + int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; + + __m128i xy_re_128, xy_im_128, wz_re_128, wz_im_128; + __m128i output, tmp_z0, tmp_z1; + + // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) + // the real part + xy_re_128 = _mm_sign_epi16(input_x,*(__m128i*)&nr_conjug2[0]); + xy_re_128 = _mm_madd_epi16(xy_re_128,input_y); //Re: (x_re*y_re - x_im*y_im) + + // the imag part + xy_im_128 = _mm_shufflelo_epi16(input_x,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + xy_im_128 = _mm_shufflehi_epi16(xy_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + xy_im_128 = _mm_madd_epi16(xy_im_128,input_y);//Im: (x_im*y_re + x_re*y_im) + + // complex multiplication (w_re + jw_im)*(z_re + jz_im) = (w_re*z_re - w_im*z_im) + j(w_im*z_re + w_re*z_im) + // the real part + wz_re_128 = _mm_sign_epi16(input_w,*(__m128i*)&nr_conjug2[0]); + wz_re_128 = _mm_madd_epi16(wz_re_128,input_z); //Re: (w_re*z_re - w_im*z_im) + + // the imag part + wz_im_128 = _mm_shufflelo_epi16(input_w,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + wz_im_128 = _mm_shufflehi_epi16(wz_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits + wz_im_128 = _mm_madd_epi16(wz_im_128,input_z);//Im: (w_im*z_re + w_re*z_im) + + + xy_re_128 = _mm_sub_epi32(xy_re_128, wz_re_128); + xy_im_128 = _mm_sub_epi32(xy_im_128, wz_im_128); + //print_ints("rx_re:",(int32_t*)&xy_re_128[0]); + //print_ints("rx_Img:",(int32_t*)&xy_im_128[0]); + //divide by matrix det and convert back to Q15 before packing + int sum_det =0; + for (int k=0; k<4;k++) { + sum_det += ((((int *)&det[0])[k])>>2); + //printf("det_%d = %d log2 =%d \n",k,(((int *)&det[0])[k]),log2_approx(((int *)&det[0])[k])); + } + + xy_re_128 = _mm_slli_epi32(xy_re_128,5); + xy_re_128 = _mm_srai_epi32(xy_re_128,log2_approx(sum_det)); + xy_re_128 = _mm_slli_epi32(xy_re_128,5); + + xy_im_128 = _mm_slli_epi32(xy_im_128,5); + xy_im_128 = _mm_srai_epi32(xy_im_128,log2_approx(sum_det)); + xy_im_128 = _mm_slli_epi32(xy_im_128,5); + + tmp_z0 = _mm_unpacklo_epi32(xy_re_128,xy_im_128); + //print_ints("unpack lo:",&tmp_z0[0]); + tmp_z1 = _mm_unpackhi_epi32(xy_re_128,xy_im_128); + //print_ints("unpack hi:",&tmp_z1[0]); + output = _mm_packs_epi32(tmp_z0,tmp_z1); + + _mm_empty(); + _m_empty(); + return(output); +} +/* Zero Forcing Rx function: nr_construct_HhH_elements() + * + * + * */ +void nr_ulsch_construct_HhH_elements(int *conjch00_ch00, + int *conjch01_ch01, + int *conjch11_ch11, + int *conjch10_ch10,// + int *conjch20_ch20, + int *conjch21_ch21, + int *conjch30_ch30, + int *conjch31_ch31, + int *conjch00_ch01,//00_01 + int *conjch01_ch00,//01_00 + int *conjch10_ch11,//10_11 + int *conjch11_ch10,//11_10 + int *conjch20_ch21, + int *conjch21_ch20, + int *conjch30_ch31, + int *conjch31_ch30, + int32_t *after_mf_00, + int32_t *after_mf_01, + int32_t *after_mf_10, + int32_t *after_mf_11, + unsigned short nb_rb, + unsigned char symbol) +{ + //This function is used to construct the (H_hermitian * H matrix) matrix elements + unsigned short rb; + __m128i *conjch00_ch00_128, *conjch01_ch01_128, *conjch11_ch11_128, *conjch10_ch10_128; + __m128i *conjch20_ch20_128, *conjch21_ch21_128, *conjch30_ch30_128, *conjch31_ch31_128; + __m128i *conjch00_ch01_128, *conjch01_ch00_128, *conjch10_ch11_128, *conjch11_ch10_128; + __m128i *conjch20_ch21_128, *conjch21_ch20_128, *conjch30_ch31_128, *conjch31_ch30_128; + __m128i *after_mf_00_128, *after_mf_01_128, *after_mf_10_128, *after_mf_11_128; + + conjch00_ch00_128 = (__m128i *)conjch00_ch00; + conjch01_ch01_128 = (__m128i *)conjch01_ch01; + conjch11_ch11_128 = (__m128i *)conjch11_ch11; + conjch10_ch10_128 = (__m128i *)conjch10_ch10; + + conjch20_ch20_128 = (__m128i *)conjch20_ch20; + conjch21_ch21_128 = (__m128i *)conjch21_ch21; + conjch30_ch30_128 = (__m128i *)conjch30_ch30; + conjch31_ch31_128 = (__m128i *)conjch31_ch31; + + conjch00_ch01_128 = (__m128i *)conjch00_ch01; + conjch01_ch00_128 = (__m128i *)conjch01_ch00; + conjch10_ch11_128 = (__m128i *)conjch10_ch11; + conjch11_ch10_128 = (__m128i *)conjch11_ch10; + + conjch20_ch21_128 = (__m128i *)conjch20_ch21; + conjch21_ch20_128 = (__m128i *)conjch21_ch20; + conjch30_ch31_128 = (__m128i *)conjch30_ch31; + conjch31_ch30_128 = (__m128i *)conjch31_ch30; + + after_mf_00_128 = (__m128i *)after_mf_00; + after_mf_01_128 = (__m128i *)after_mf_01; + after_mf_10_128 = (__m128i *)after_mf_10; + after_mf_11_128 = (__m128i *)after_mf_11; + + for (rb=0; rb<3*nb_rb; rb++) { + + after_mf_00_128[0] =_mm_adds_epi16(conjch00_ch00_128[0],conjch10_ch10_128[0]);//00_00 + 10_10 + if (conjch20_ch20 != NULL) after_mf_00_128[0] =_mm_adds_epi16(after_mf_00_128[0],conjch20_ch20_128[0]); + if (conjch30_ch30 != NULL) after_mf_00_128[0] =_mm_adds_epi16(after_mf_00_128[0],conjch30_ch30_128[0]); + + after_mf_11_128[0] =_mm_adds_epi16(conjch01_ch01_128[0], conjch11_ch11_128[0]); //01_01 + 11_11 + if (conjch21_ch21 != NULL) after_mf_11_128[0] =_mm_adds_epi16(after_mf_11_128[0],conjch21_ch21_128[0]); + if (conjch31_ch31 != NULL) after_mf_11_128[0] =_mm_adds_epi16(after_mf_11_128[0],conjch31_ch31_128[0]); + + after_mf_01_128[0] =_mm_adds_epi16(conjch00_ch01_128[0], conjch10_ch11_128[0]);//00_01 + 10_11 + if (conjch20_ch21 != NULL) after_mf_01_128[0] =_mm_adds_epi16(after_mf_01_128[0],conjch20_ch21_128[0]); + if (conjch30_ch31 != NULL) after_mf_01_128[0] =_mm_adds_epi16(after_mf_01_128[0],conjch30_ch31_128[0]); + + after_mf_10_128[0] =_mm_adds_epi16(conjch01_ch00_128[0], conjch11_ch10_128[0]);//01_00 + 11_10 + if (conjch21_ch20 != NULL) after_mf_10_128[0] =_mm_adds_epi16(after_mf_10_128[0],conjch21_ch20_128[0]); + if (conjch31_ch30 != NULL) after_mf_10_128[0] =_mm_adds_epi16(after_mf_10_128[0],conjch31_ch30_128[0]); + +#ifdef DEBUG_DLSCH_DEMOD + if ((rb<=30)) + { + printf(" \n construct_HhH_elements \n"); + print_shorts("after_mf_00_128:",(int16_t*)&after_mf_00_128[0]); + print_shorts("after_mf_01_128:",(int16_t*)&after_mf_01_128[0]); + print_shorts("after_mf_10_128:",(int16_t*)&after_mf_10_128[0]); + print_shorts("after_mf_11_128:",(int16_t*)&after_mf_11_128[0]); + } +#endif + conjch00_ch00_128+=1; + conjch10_ch10_128+=1; + conjch01_ch01_128+=1; + conjch11_ch11_128+=1; + + if (conjch20_ch20 != NULL) conjch20_ch20_128+=1; + if (conjch21_ch21 != NULL) conjch21_ch21_128+=1; + if (conjch30_ch30 != NULL) conjch30_ch30_128+=1; + if (conjch31_ch31 != NULL) conjch31_ch31_128+=1; + + conjch00_ch01_128+=1; + conjch01_ch00_128+=1; + conjch10_ch11_128+=1; + conjch11_ch10_128+=1; + + if (conjch20_ch21 != NULL) conjch20_ch21_128+=1; + if (conjch21_ch20 != NULL) conjch21_ch20_128+=1; + if (conjch30_ch31 != NULL) conjch30_ch31_128+=1; + if (conjch31_ch30 != NULL) conjch31_ch30_128+=1; + + after_mf_00_128 += 1; + after_mf_01_128 += 1; + after_mf_10_128 += 1; + after_mf_11_128 += 1; + } + _mm_empty(); + _m_empty(); +} + +/* Zero Forcing Rx function: nr_ulsch_zero_forcing_rx_2layers() + * + * + * */ +uint8_t nr_ulsch_zero_forcing_rx_2layers(int **rxdataF_comp, + int **ul_ch_mag, + int **ul_ch_magb, + int **ul_ch_estimates_ext, + unsigned short nb_rb, + unsigned char n_rx, + unsigned char mod_order, + int shift, + unsigned char symbol, + int length) +{ + int *ch00, *ch01, *ch10, *ch11; + int *ch20, *ch30, *ch21, *ch31; + uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); + + #ifdef __AVX2__ + int off = ((nb_rb&1) == 1)? 4:0; + #else + int off = 0; + #endif + + /* we need at least alignment to 16 bytes, let's put 32 to be sure + * (maybe not necessary but doesn't hurt) + */ + int32_t conjch00_ch01[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch01_ch00[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch10_ch11[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch11_ch10[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch00_ch00[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch01_ch01[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch10_ch10[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch11_ch11[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch20_ch20[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch21_ch21[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch30_ch30[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch31_ch31[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch20_ch21[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch30_ch31[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch21_ch20[12*nb_rb] __attribute__((aligned(32))); + int32_t conjch31_ch30[12*nb_rb] __attribute__((aligned(32))); + + int32_t af_mf_00[12*nb_rb] __attribute__((aligned(32))); + int32_t af_mf_01[12*nb_rb] __attribute__((aligned(32))); + int32_t af_mf_10[12*nb_rb] __attribute__((aligned(32))); + int32_t af_mf_11[12*nb_rb] __attribute__((aligned(32))); + int32_t determ_fin[12*nb_rb] __attribute__((aligned(32))); + + switch (n_rx) { + case 2:// + ch00 = (int *)&ul_ch_estimates_ext[0][symbol*(off+nb_rb*12)]; + ch01 = (int *)&ul_ch_estimates_ext[2][symbol*(off+nb_rb*12)]; + ch10 = (int *)&ul_ch_estimates_ext[1][symbol*(off+nb_rb*12)]; + ch11 = (int *)&ul_ch_estimates_ext[3][symbol*(off+nb_rb*12)]; + ch20 = NULL; + ch21 = NULL; + ch30 = NULL; + ch31 = NULL; + break; + + case 4:// + ch00 = (int *)&ul_ch_estimates_ext[0][symbol*(off+nb_rb*12)]; + ch01 = (int *)&ul_ch_estimates_ext[4][symbol*(off+nb_rb*12)]; + ch10 = (int *)&ul_ch_estimates_ext[1][symbol*(off+nb_rb*12)]; + ch11 = (int *)&ul_ch_estimates_ext[5][symbol*(off+nb_rb*12)]; + ch20 = (int *)&ul_ch_estimates_ext[2][symbol*(off+nb_rb*12)]; + ch21 = (int *)&ul_ch_estimates_ext[6][symbol*(off+nb_rb*12)]; + ch30 = (int *)&ul_ch_estimates_ext[3][symbol*(off+nb_rb*12)]; + ch31 = (int *)&ul_ch_estimates_ext[7][symbol*(off+nb_rb*12)]; + break; + + default: + return -1; + break; + } + + /* 1- Compute the rx channel matrix after compensation: (1/2^log2_max)x(H_herm x H) + * for n_rx = 2 + * |conj_H_00 conj_H_10| | H_00 H_01| |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| + * | | x | | = | | + * |conj_H_01 conj_H_11| | H_10 H_11| |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| + * + */ + + if (n_rx>=2){ + // (1/2^log2_maxh)*conj_H_00xH_00: (1/(64*2))conjH_00*H_00*2^15 + nr_ulsch_conjch0_mult_ch1(ch00, + ch00, + conjch00_ch00, + nb_rb_0, + shift); + // (1/2^log2_maxh)*conj_H_10xH_10: (1/(64*2))conjH_10*H_10*2^15 + nr_ulsch_conjch0_mult_ch1(ch10, + ch10, + conjch10_ch10, + nb_rb_0, + shift); + // conj_H_00xH_01 + nr_ulsch_conjch0_mult_ch1(ch00, + ch01, + conjch00_ch01, + nb_rb_0, + shift); // this shift is equal to the channel level log2_maxh + // conj_H_10xH_11 + nr_ulsch_conjch0_mult_ch1(ch10, + ch11, + conjch10_ch11, + nb_rb_0, + shift); + // conj_H_01xH_01 + nr_ulsch_conjch0_mult_ch1(ch01, + ch01, + conjch01_ch01, + nb_rb_0, + shift); + // conj_H_11xH_11 + nr_ulsch_conjch0_mult_ch1(ch11, + ch11, + conjch11_ch11, + nb_rb_0, + shift); + // conj_H_01xH_00 + nr_ulsch_conjch0_mult_ch1(ch01, + ch00, + conjch01_ch00, + nb_rb_0, + shift); + // conj_H_11xH_10 + nr_ulsch_conjch0_mult_ch1(ch11, + ch10, + conjch11_ch10, + nb_rb_0, + shift); + } + if (n_rx==4){ + // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2*16))conjH_20*H_20*2^15 + nr_ulsch_conjch0_mult_ch1(ch20, + ch20, + conjch20_ch20, + nb_rb_0, + shift); + + // (1/2^log2_maxh)*conj_H_30xH_30: (1/(64*2*4))conjH_30*H_30*2^15 + nr_ulsch_conjch0_mult_ch1(ch30, + ch30, + conjch30_ch30, + nb_rb_0, + shift); + + // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 + nr_ulsch_conjch0_mult_ch1(ch20, + ch21, + conjch20_ch21, + nb_rb_0, + shift); + + nr_ulsch_conjch0_mult_ch1(ch30, + ch31, + conjch30_ch31, + nb_rb_0, + shift); + + nr_ulsch_conjch0_mult_ch1(ch21, + ch21, + conjch21_ch21, + nb_rb_0, + shift); + + nr_ulsch_conjch0_mult_ch1(ch31, + ch31, + conjch31_ch31, + nb_rb_0, + shift); + + // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 + nr_ulsch_conjch0_mult_ch1(ch21, + ch20, + conjch21_ch20, + nb_rb_0, + shift); + + nr_ulsch_conjch0_mult_ch1(ch31, + ch30, + conjch31_ch30, + nb_rb_0, + shift); + + nr_ulsch_construct_HhH_elements(conjch00_ch00, + conjch01_ch01, + conjch11_ch11, + conjch10_ch10,// + conjch20_ch20, + conjch21_ch21, + conjch30_ch30, + conjch31_ch31, + conjch00_ch01, + conjch01_ch00, + conjch10_ch11, + conjch11_ch10,// + conjch20_ch21, + conjch21_ch20, + conjch30_ch31, + conjch31_ch30, + af_mf_00, + af_mf_01, + af_mf_10, + af_mf_11, + nb_rb_0, + symbol); + } + if (n_rx==2){ + nr_ulsch_construct_HhH_elements(conjch00_ch00, + conjch01_ch01, + conjch11_ch11, + conjch10_ch10,// + NULL, + NULL, + NULL, + NULL, + conjch00_ch01, + conjch01_ch00, + conjch10_ch11, + conjch11_ch10,// + NULL, + NULL, + NULL, + NULL, + af_mf_00, + af_mf_01, + af_mf_10, + af_mf_11, + nb_rb_0, + symbol); + } + //det_HhH = ad -bc + nr_ulsch_det_HhH(af_mf_00,//a + af_mf_01,//b + af_mf_10,//c + af_mf_11,//d + determ_fin, + nb_rb_0, + symbol, + shift); + /* 2- Compute the channel matrix inversion ********************************** + * + * |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| + * A= | | + * |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| + * + * + * + *inv(A) =(1/det)*[d -b + * -c a] + * + * + **************************************************************************/ + __m128i *rxdataF_comp128_0,*rxdataF_comp128_1,*ul_ch_mag128_0=NULL,*ul_ch_mag128b_0=NULL,*determ_fin_128;//*dl_ch_mag128_1,*dl_ch_mag128b_1,*dl_ch_mag128r_1 + __m128i mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3; + __m128i *after_mf_a_128,*after_mf_b_128, *after_mf_c_128, *after_mf_d_128; + __m128i QAM_amp128={0},QAM_amp128b={0}; + + determ_fin_128 = (__m128i *)&determ_fin[0]; + + rxdataF_comp128_0 = (__m128i *)&rxdataF_comp[0][symbol*(off+nb_rb*12)];//aatx=0 @ aarx =0 + rxdataF_comp128_1 = (__m128i *)&rxdataF_comp[n_rx][symbol*(off+nb_rb*12)];//aatx=1 @ aarx =0 + + after_mf_a_128 = (__m128i *)af_mf_00; + after_mf_b_128 = (__m128i *)af_mf_01; + after_mf_c_128 = (__m128i *)af_mf_10; + after_mf_d_128 = (__m128i *)af_mf_11; + + if (mod_order>2) { + if (mod_order == 4) { + QAM_amp128 = _mm_set1_epi16(QAM16_n1); //2/sqrt(10) + QAM_amp128b = _mm_setzero_si128(); + } else if (mod_order == 6) { + QAM_amp128 = _mm_set1_epi16(QAM64_n1); //4/sqrt{42} + QAM_amp128b = _mm_set1_epi16(QAM64_n2); //2/sqrt{42} + } + ul_ch_mag128_0 = (__m128i *)&ul_ch_mag[0][symbol*(off+nb_rb*12)]; + ul_ch_mag128b_0 = (__m128i *)&ul_ch_magb[0][symbol*(off+nb_rb*12)]; + } + + for (int rb=0; rb<3*nb_rb_0; rb++) { + if (mod_order>2) { + int sum_det =0; + for (int k=0; k<4;k++) { + sum_det += ((((int *)&determ_fin_128[0])[k])>>2); + //printf("det_%d = %d\n",k,sum_det); + } + + mmtmpD2 = _mm_slli_epi32(determ_fin_128[0],5); + mmtmpD2 = _mm_srai_epi32(mmtmpD2,log2_approx(sum_det)); + mmtmpD2 = _mm_slli_epi32(mmtmpD2,5); + + mmtmpD3 = _mm_unpacklo_epi32(mmtmpD2,mmtmpD2); + + mmtmpD2 = _mm_unpackhi_epi32(mmtmpD2,mmtmpD2); + + mmtmpD2 = _mm_packs_epi32(mmtmpD3,mmtmpD2); + + ul_ch_mag128_0[0] = mmtmpD2; + ul_ch_mag128b_0[0] = mmtmpD2; + + ul_ch_mag128_0[0] = _mm_mulhi_epi16(ul_ch_mag128_0[0],QAM_amp128); + ul_ch_mag128_0[0] = _mm_slli_epi16(ul_ch_mag128_0[0],1); + + ul_ch_mag128b_0[0] = _mm_mulhi_epi16(ul_ch_mag128b_0[0],QAM_amp128b); + ul_ch_mag128b_0[0] = _mm_slli_epi16(ul_ch_mag128b_0[0],1); + + //print_shorts("mag layer 1:",(int16_t*)&dl_ch_mag128_0[0]); + //print_shorts("mag layer 2:",(int16_t*)&dl_ch_mag128_1[0]); + //print_shorts("magb layer 1:",(int16_t*)&dl_ch_mag128b_0[0]); + //print_shorts("magb layer 2:",(int16_t*)&dl_ch_mag128b_1[0]); + //print_shorts("magr layer 1:",(int16_t*)&dl_ch_mag128r_0[0]); + //print_shorts("magr layer 2:",(int16_t*)&dl_ch_mag128r_1[0]); + } + // multiply by channel Inv + //rxdataF_zf128_0 = rxdataF_comp128_0*d - b*rxdataF_comp128_1 + //rxdataF_zf128_1 = rxdataF_comp128_1*a - c*rxdataF_comp128_0 + //printf("layer_1 \n"); + mmtmpD0 = nr_ulsch_comp_muli_sum(rxdataF_comp128_0[0], + after_mf_d_128[0], + rxdataF_comp128_1[0], + after_mf_b_128[0], + determ_fin_128[0]); + + //printf("layer_2 \n"); + mmtmpD1 = nr_ulsch_comp_muli_sum(rxdataF_comp128_1[0], + after_mf_a_128[0], + rxdataF_comp128_0[0], + after_mf_c_128[0], + determ_fin_128[0]); + + rxdataF_comp128_0[0] = mmtmpD0; + rxdataF_comp128_1[0] = mmtmpD1; +#ifdef DEBUG_DLSCH_DEMOD + printf("\n Rx signal after ZF l%d rb%d\n",symbol,rb); + print_shorts(" Rx layer 1:",(int16_t*)&rxdataF_comp128_0[0]); + print_shorts(" Rx layer 2:",(int16_t*)&rxdataF_comp128_1[0]); +#endif + determ_fin_128 += 1; + ul_ch_mag128_0 += 1; + ul_ch_mag128b_0 += 1; + rxdataF_comp128_0 += 1; + rxdataF_comp128_1 += 1; + after_mf_a_128 += 1; + after_mf_b_128 += 1; + after_mf_c_128 += 1; + after_mf_d_128 += 1; + } + _mm_empty(); + _m_empty(); + return(0); +} + +//============================================================================================== + +/* Main Function */ int nr_rx_pusch(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, @@ -1184,21 +1969,29 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->cl_done = 0; bwp_start_subcarrier = ((rel15_ul->rb_start + rel15_ul->bwp_start)*NR_NB_SC_PER_RB + frame_parms->first_carrier_offset) % frame_parms->ofdm_symbol_size; - LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); - LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); - + LOG_I(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); + LOG_I(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); + LOG_I(PHY,"ulsch RX %x : start_rb %d nb_rb %d mcs %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_ports %d\n", + rel15_ul->rnti,rel15_ul->rb_start,rel15_ul->rb_size,rel15_ul->mcs_index, + rel15_ul->nrOfLayers,0,rel15_ul->bwp_start,0,rel15_ul->start_symbol_index,rel15_ul->nr_of_symbols, + rel15_ul->num_dmrs_cdm_grps_no_data,rel15_ul->ul_dmrs_symb_pos,rel15_ul->dmrs_ports); //---------------------------------------------------------- //--------------------- Channel estimation --------------------- //---------------------------------------------------------- start_meas(&gNB->ulsch_channel_estimation_stats); - for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { + for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) + { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; LOG_D(PHY, "symbol %d, dmrs_symbol_flag :%d\n", symbol, dmrs_symbol_flag); - if (dmrs_symbol_flag == 1) { + + if (dmrs_symbol_flag == 1) + { if (gNB->pusch_vars[ulsch_id]->dmrs_symbol == INVALID_VALUE) gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; for (int nl=0; nlnrOfLayers; nl++) + { + nr_pusch_channel_estimation(gNB, slot, get_dmrs_port(nl,rel15_ul->dmrs_ports), @@ -1206,6 +1999,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, ulsch_id, bwp_start_subcarrier, rel15_ul); + } nr_gnb_measurements(gNB, ulsch_id, harq_pid, symbol,rel15_ul->nrOfLayers); @@ -1214,9 +2008,12 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] = 0; gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] = 0; } - gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( - &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aarx][symbol * frame_parms->ofdm_symbol_size], + for (aatx = 0; aatx < rel15_ul->nrOfLayers; aatx++) + { + gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( + &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aatx*gNB->frame_parms.nb_antennas_rx+aarx][symbol * frame_parms->ofdm_symbol_size], rel15_ul->rb_size * 12); + } for (int rb = 0; rb < rel15_ul->rb_size; rb++) { gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] += gNB->measurements.n0_subband_power[aarx][rel15_ul->bwp_start + rel15_ul->rb_start + rb] / @@ -1234,23 +2031,29 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, #endif uint32_t rxdataF_ext_offset = 0; - for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { + for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) + { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; - if (dmrs_symbol_flag == 1) { + if (dmrs_symbol_flag == 1) + { if ((rel15_ul->ul_dmrs_symb_pos >> ((symbol + 1) % frame_parms->symbols_per_slot)) & 0x01) AssertFatal(1==0,"Double DMRS configuration is not yet supported\n"); gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; - if (rel15_ul->dmrs_config_type == 0) { + if (rel15_ul->dmrs_config_type == 0) + { // if no data in dmrs cdm group is 1 only even REs have no data // if no data in dmrs cdm group is 2 both odd and even REs have no data nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*6)); } - else { + else + { nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*4)); } - } else { + } + else + { nb_re_pusch = rel15_ul->rb_size * NR_NB_SC_PER_RB; } @@ -1260,16 +2063,16 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, //---------------------------------------------------------- //--------------------- RBs extraction --------------------- //---------------------------------------------------------- - if (nb_re_pusch > 0) { - + if (nb_re_pusch > 0) + { start_meas(&gNB->ulsch_rbs_extraction_stats); - nr_ulsch_extract_rbs_single(gNB->common_vars.rxdataF, - gNB->pusch_vars[ulsch_id], - slot, - symbol, - dmrs_symbol_flag, - rel15_ul, - frame_parms); + nr_ulsch_extract_rbs(gNB->common_vars.rxdataF, + gNB->pusch_vars[ulsch_id], + slot, + symbol, + dmrs_symbol_flag, + rel15_ul, + frame_parms); stop_meas(&gNB->ulsch_rbs_extraction_stats); //---------------------------------------------------------- @@ -1280,8 +2083,9 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->ulsch[ulsch_id], symbol, dmrs_symbol_flag, - rel15_ul->rb_size, - rel15_ul->dmrs_config_type); + nb_re_pusch, + rel15_ul->nrOfLayers, + rel15_ul->rb_size); if (gNB->pusch_vars[ulsch_id]->cl_done==0) { nr_ulsch_channel_level(gNB->pusch_vars[ulsch_id]->ul_ch_estimates_ext, @@ -1312,9 +2116,14 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->ul_ch_mag0, gNB->pusch_vars[ulsch_id]->ul_ch_magb0, gNB->pusch_vars[ulsch_id]->rxdataF_comp, + #ifdef SUPPORT_PMI_MATRIC (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, + #else + NULL, + #endif frame_parms, symbol, + nb_re_pusch, dmrs_symbol_flag, rel15_ul->qam_mod_order, rel15_ul->nrOfLayers, @@ -1327,19 +2136,37 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->rxdataF_comp, gNB->pusch_vars[ulsch_id]->ul_ch_mag0, gNB->pusch_vars[ulsch_id]->ul_ch_magb0, + #ifdef SUPPORT_PMI_MATRIC + (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, + #else + NULL, + #endif + rel15_ul->nrOfLayers, symbol, - rel15_ul->rb_size); + rel15_ul->rb_size, + nb_re_pusch); + + if (rel15_ul->nrOfLayers == 2)//Apply zero forcing for 2 Tx layers + nr_ulsch_zero_forcing_rx_2layers(gNB->pusch_vars[ulsch_id]->rxdataF_comp, + gNB->pusch_vars[ulsch_id]->ul_ch_mag0, + gNB->pusch_vars[ulsch_id]->ul_ch_magb0, + gNB->pusch_vars[ulsch_id]->ul_ch_estimates_ext, + rel15_ul->rb_size, + frame_parms->nb_antennas_rx, + rel15_ul->qam_mod_order, + gNB->pusch_vars[ulsch_id]->log2_maxh, + symbol, + nb_re_pusch); stop_meas(&gNB->ulsch_mrc_stats); - // transform precoding = 0 means enabled - if (rel15_ul->transform_precoding == 0) { - - #ifdef __AVX2__ + if (rel15_ul->transformPrecoder == transformPrecoder_enabled) + { + #ifdef __AVX2__ // For odd number of resource blocks need byte alignment to multiple of 8 int nb_re_pusch2 = nb_re_pusch + (nb_re_pusch&7); - #else + #else int nb_re_pusch2 = nb_re_pusch; - #endif + #endif // perform IDFT operation on the compensated rxdata if transform precoding is enabled nr_idft(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[0][symbol * nb_re_pusch2], nb_re_pusch); @@ -1351,7 +2178,8 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, //---------------------------------------------------------- /* In case PTRS is enabled then LLR will be calculated after PTRS symbols are processed * * otherwise LLR are calculated for each symbol based upon DMRS channel estimates only. */ - if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { + if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) + { start_meas(&gNB->ulsch_ptrs_processing_stats); nr_pusch_ptrs_processing(gNB, frame_parms, @@ -1370,14 +2198,17 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, /*-------------------- LLRs computation -------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------------------------*/ start_meas(&gNB->ulsch_llr_stats); - nr_ulsch_compute_llr(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[0][symbol * (off + rel15_ul->rb_size * NR_NB_SC_PER_RB)], - gNB->pusch_vars[ulsch_id]->ul_ch_mag0, - gNB->pusch_vars[ulsch_id]->ul_ch_magb0, - &gNB->pusch_vars[ulsch_id]->llr[rxdataF_ext_offset * rel15_ul->qam_mod_order], - rel15_ul->rb_size, - gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol], - symbol, - rel15_ul->qam_mod_order); + for (aatx=0; aatx < rel15_ul->nrOfLayers; aatx++) + { + nr_ulsch_compute_llr(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[aatx*frame_parms->nb_antennas_rx][symbol * (off + rel15_ul->rb_size * NR_NB_SC_PER_RB)], + gNB->pusch_vars[ulsch_id]->ul_ch_mag0[aatx*frame_parms->nb_antennas_rx], + gNB->pusch_vars[ulsch_id]->ul_ch_magb0[aatx*frame_parms->nb_antennas_rx], + &gNB->pusch_vars[ulsch_id]->llr_layers[aatx][rxdataF_ext_offset * rel15_ul->qam_mod_order], + rel15_ul->rb_size, + gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol], + symbol, + rel15_ul->qam_mod_order); + } stop_meas(&gNB->ulsch_llr_stats); rxdataF_ext_offset += gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol]; } diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c index bf96a76106..ad41ebf3d6 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c @@ -64,7 +64,7 @@ void nr_ulsch_qpsk_llr(int32_t *rxdataF_comp, //---------------------------------------------------------------------------------------------- void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, - int32_t **ul_ch_mag, + int32_t *ul_ch_mag, int16_t *ulsch_llr, uint32_t nb_rb, uint32_t nb_re, @@ -110,12 +110,12 @@ void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, #if defined(__x86_64__) || defined(__i386__) #ifdef __AVX2__ - ch_mag = (__m256i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; + ch_mag = (__m256i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; #else - ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; + ch_mag = (__m128i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; #endif #elif defined(__arm__) - ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*nb_rb*12)]; + ch_mag = (int16x8_t*)&ul_ch_mag[(symbol*nb_rb*12)]; #endif #ifdef __AVX2__ @@ -231,8 +231,8 @@ void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, //---------------------------------------------------------------------------------------------- void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, - int32_t **ul_ch_mag, - int32_t **ul_ch_magb, + int32_t *ul_ch_mag, + int32_t *ul_ch_magb, int16_t *ulsch_llr, uint32_t nb_rb, uint32_t nb_re, @@ -265,15 +265,15 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, #if defined(__x86_64__) || defined(__i386__) #ifdef __AVX2__ - ch_mag = (__m256i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; - ch_magb = (__m256i*)&ul_ch_magb[0][(symbol*(off+(nb_rb*12)))]; + ch_mag = (__m256i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; + ch_magb = (__m256i*)&ul_ch_magb[(symbol*(off+(nb_rb*12)))]; #else - ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*nb_rb*12)]; - ch_magb = (__m128i*)&ul_ch_magb[0][(symbol*nb_rb*12)]; + ch_mag = (__m128i*)&ul_ch_mag[(symbol*nb_rb*12)]; + ch_magb = (__m128i*)&ul_ch_magb[(symbol*nb_rb*12)]; #endif #elif defined(__arm__) - ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*nb_rb*12)]; - ch_magb = (int16x8_t*)&ul_ch_magb[0][(symbol*nb_rb*12)]; + ch_mag = (int16x8_t*)&ul_ch_mag[(symbol*nb_rb*12)]; + ch_magb = (int16x8_t*)&ul_ch_magb[(symbol*nb_rb*12)]; #endif #ifdef __AVX2__ @@ -471,8 +471,8 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, void nr_ulsch_compute_llr(int32_t *rxdataF_comp, - int32_t **ul_ch_mag, - int32_t **ul_ch_magb, + int32_t *ul_ch_mag, + int32_t *ul_ch_magb, int16_t *ulsch_llr, uint32_t nb_rb, uint32_t nb_re, diff --git a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c index 65ae288252..2cb7609d62 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c +++ b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c @@ -148,7 +148,7 @@ short filt8_dcr0_h[8]= { 0,4096,8192,12288,16384,0,0,0}; short filt8_l1[8] = { -24576,16384,0,0,0,0,0,0}; +24576,16384,8192,0,0,0,0,0}; short filt8_ml1[8] = { -8192,0,8192,16384,8192,0,0,0}; @@ -163,10 +163,10 @@ short filt8_mm1[8]= { 0,0,0,0,8192,16384,8192,0}; short filt8_dcl1[8]= { -0,0,0,16384,12288,8192,4096,0}; +0,0,16384,12288,8192,4096,0,0}; short filt8_dcr1[8]= { -0,0,0,0,4096,8192,12288,16384}; +0,0,0,4096,8192,12288,16384,0}; short filt8_dcl1_h[8]= { 0,16384,12288,8192,4096,0,0,0}; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index 945dc51673..a293eec6d8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -258,6 +258,15 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, Ilbrm = 0; Tbslbrm = 950984; //max tbs Coderate = 0.0; + + #if 0 + harq_process->a[0] = 0x31; + for (int i = 1; i < harq_process->pusch_pdu.pusch_data.tb_size; i++) + { + harq_process->a[i] = (i&0xff); + } + #endif + trace_NRpdu(DIRECTION_UPLINK, harq_process->a, harq_process->pusch_pdu.pusch_data.tb_size, 0, WS_C_RNTI, 0, 0, 0,0, 0); /////////// ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 99dc2b658e..5d76e654ba 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -98,13 +98,12 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, uint8_t thread_id, int gNB_id) { - LOG_D(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); + LOG_I(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); uint32_t available_bits; uint8_t cwd_index, l; uint32_t scrambled_output[NR_MAX_NB_CODEWORDS][NR_MAX_PDSCH_ENCODED_LENGTH>>5]; - int16_t **tx_layers; - int32_t **txdataF; + int8_t Wf[2], Wt[2], l_prime[2], delta; uint8_t nb_dmrs_re_per_rb; int ap, i; @@ -112,6 +111,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, NR_DL_FRAME_PARMS *frame_parms = &UE->frame_parms; NR_UE_PUSCH *pusch_ue = UE->pusch_vars[thread_id][gNB_id]; + int16_t **tx_layers = (int16_t **)pusch_ue->txdataF_layers; + int16_t **tx_precoding = (int16_t **)pusch_ue->txdataF_precoding; + int32_t **txdataF = UE->common_vars.txdataF; uint8_t num_of_codewords = 1; // tmp assumption int Nid_cell = 0; @@ -148,9 +150,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, nb_dmrs_re_per_rb = ((dmrs_type == pusch_dmrs_type1) ? 6:4)*cdm_grps_no_data; - LOG_D(PHY,"ulsch %x : start_rb %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", - rnti,start_rb,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); - + LOG_I(PHY,"ulsch TX %x : start_rb %d nb_rb %d mod_order %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", + rnti,start_rb,nb_rb,mod_order,Nl,pusch_pdu->Tpmi,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); + // TbD num_of_mod_symbols is set but never used N_RE_prime = NR_NB_SC_PER_RB*number_of_symbols - nb_dmrs_re_per_rb*number_dmrs_symbols - N_PRB_oh; harq_process_ul_ue->num_of_mod_symbols = N_RE_prime*nb_rb*num_of_codewords; @@ -236,8 +238,6 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH layer mapping///////////////////////// /////////// - tx_layers = (int16_t **)pusch_ue->txdataF_layers; - nr_ue_layer_mapping(UE->ulsch[thread_id][gNB_id], Nl, available_bits/mod_order, @@ -257,7 +257,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, int16_t *dmrs_seq = NULL; // if transform precoding is enbaled (value 0) - if (pusch_pdu->transform_precoding == 0) { + if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { uint32_t nb_re_pusch=nb_rb * NR_NB_SC_PER_RB; uint32_t y_offset = 0; @@ -315,10 +315,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, LOG_M("UE_DMRS_SEQ.m","UE_DMRS_SEQ", dmrs_seq,nb_re_pusch,1,1); #endif - } - else - memcpy(ulsch_ue->y, tx_layers[0], (available_bits/mod_order)*sizeof(int32_t)); - + } /////////// //////////////////////////////////////////////////////////////////////// @@ -328,145 +325,233 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH RE mapping///////////////////////// /////////// - txdataF = UE->common_vars.txdataF; - - for (ap=0; ap< Nl; ap++) { - - uint8_t k_prime = 0; - uint16_t m = 0; - - - #ifdef DEBUG_PUSCH_MAPPING + for (ap=0; ap< Nl; ap++) + { + uint8_t k_prime = 0; + uint16_t m = 0; + + #ifdef DEBUG_PUSCH_MAPPING printf("NR_ULSCH_UE: Value of CELL ID %d /t, u %d \n", frame_parms->Nid_cell, u); - #endif + #endif - + // DMRS params for this ap + get_Wt(Wt, ap, dmrs_type); + get_Wf(Wf, ap, dmrs_type); + delta = get_delta(ap, dmrs_type); - // DMRS params for this ap - get_Wt(Wt, ap, dmrs_type); - get_Wf(Wf, ap, dmrs_type); - delta = get_delta(ap, dmrs_type); + for (l=start_symbol; l> l) & 0x01) + { + is_dmrs_sym = 1; + if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) + { + if (dmrs_type == pusch_dmrs_type1) + dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; + else + dmrs_idx = (pusch_pdu->bwp_start + start_rb)*4; - uint16_t k = start_sc; - uint16_t n = 0; - uint8_t is_dmrs_sym = 0; - uint8_t is_ptrs_sym = 0; - uint16_t dmrs_idx = 0, ptrs_idx = 0; + // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part + // Perform this on gold sequence, not required when SC FDMA operation is done, + LOG_D(PHY,"DMRS in symbol %d\n",l); + nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated + } + else + { + dmrs_idx = 0; + } + } + else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) + { + AssertFatal(pusch_pdu->transformPrecoder == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); - if ((ul_dmrs_symb_pos >> l) & 0x01) { - is_dmrs_sym = 1; - - // transform precoding disabled (value 1) - if (pusch_pdu->transform_precoding == 1){ - - if (dmrs_type == pusch_dmrs_type1) - dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; - else - dmrs_idx = (pusch_pdu->bwp_start + start_rb)*4; - - // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part - // Perform this on gold sequence, not required when SC FDMA operation is done, - LOG_D(PHY,"DMRS in symbol %d\n",l); - nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated - - } else { - dmrs_idx = 0; + if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) + { + is_ptrs_sym = 1; + nr_modulation(pusch_dmrs[l][0], nb_rb, DMRS_MOD_ORDER, mod_ptrs); + } } - - - } else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { - AssertFatal(pusch_pdu->transform_precoding == 1, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); + for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) + { + uint8_t is_dmrs = 0; + uint8_t is_ptrs = 0; - if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) { - is_ptrs_sym = 1; - nr_modulation(pusch_dmrs[l][0], nb_rb, DMRS_MOD_ORDER, mod_ptrs); - } - } + sample_offsetF = l*frame_parms->ofdm_symbol_size + k; - for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) { - - uint8_t is_dmrs = 0; - uint8_t is_ptrs = 0; - - sample_offsetF = l*frame_parms->ofdm_symbol_size + k; - - if (is_dmrs_sym) { - if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) - is_dmrs = 1; - } else if (is_ptrs_sym) { + if (is_dmrs_sym) + { + if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) + is_dmrs = 1; + } + else if (is_ptrs_sym) + { is_ptrs = is_ptrs_subcarrier(k, - rnti, - ap, - dmrs_type, - K_ptrs, - nb_rb, - pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset, - start_sc, - frame_parms->ofdm_symbol_size); - } + rnti, + ap, + dmrs_type, + K_ptrs, + nb_rb, + pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset, + start_sc, + frame_parms->ofdm_symbol_size); + } - if (is_dmrs == 1) { - // if transform precoding is enabled - if (pusch_pdu->transform_precoding == 0) { + if (is_dmrs == 1) + { + if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) + { + ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; + } + else + { + ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; + } + + #ifdef DEBUG_PUSCH_MAPPING + printf("DMRS: Layer: %d\t, dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", + ap, dmrs_idx, l, k, k_prime, n, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); + #endif + + dmrs_idx++; + k_prime++; + k_prime&=1; + n+=(k_prime)?0:1; + } + else if (is_ptrs == 1) + { + ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; + + ptrs_idx++; + } + else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) + { + if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) + { + ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[ap])[m<<1]; + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[ap])[(m<<1) + 1]; + } + else + { + ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; + } + + #ifdef DEBUG_PUSCH_MAPPING + printf("DATA: layer %d\t m %d\t l %d \t k %d \t txdataF: %d %d\n", + ap, m, l, k, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); + #endif + + m++; + } + else + { + ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = 0; + ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = 0; + } + + if (++k >= frame_parms->ofdm_symbol_size) + { + k -= frame_parms->ofdm_symbol_size; + } + } //for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) + }//for (l=start_symbol; lnb_antennas_tx; ap++) + { + for (int l=start_symbol; lTpmi; - ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; - ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; - - } else { - - ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; - ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; - + if (pmi == 0) {//unitary Precoding + if(ap< pusch_pdu->nrOfLayers) + memcpy((void*)&txdataF[ap][l*frame_parms->ofdm_symbol_size + k], + (void*)&tx_precoding[ap][2*(l*frame_parms->ofdm_symbol_size + k)], + NR_NB_SC_PER_RB*sizeof(int32_t)); + else + memset((void*)&txdataF[ap][l*frame_parms->ofdm_symbol_size + k], + 0, + NR_NB_SC_PER_RB*sizeof(int32_t)); + + k += NR_NB_SC_PER_RB; + if (k >= frame_parms->ofdm_symbol_size) { + k -= frame_parms->ofdm_symbol_size; + } + } + else + { + //get the precoding matrix weights: + char *W_prec; + switch (frame_parms->nb_antennas_tx) + { + case 1://1 antenna port + W_prec = nr_W_1l_2p[pmi][ap]; + break; + case 2://2 antenna ports + if (pusch_pdu->nrOfLayers == 1)//1 layer + W_prec = nr_W_1l_2p[pmi][ap]; + else//2 layers + W_prec = nr_W_2l_2p[pmi][ap]; + break; + case 4://4 antenna ports + if (pusch_pdu->nrOfLayers == 1)//1 layer + W_prec = nr_W_1l_4p[pmi][ap]; + else if (pusch_pdu->nrOfLayers == 2)//2 layers + W_prec = nr_W_2l_4p[pmi][ap]; + else if (pusch_pdu->nrOfLayers == 3)//3 layers + W_prec = nr_W_3l_4p[pmi][ap]; + else//4 layers + W_prec = nr_W_4l_4p[pmi][ap]; + break; + default: + LOG_D(PHY,"Precoding 1,2, or 4 antenna ports are currently supported\n"); + W_prec = nr_W_1l_2p[pmi][ap]; + break; } - #ifdef DEBUG_PUSCH_MAPPING - printf("dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", - dmrs_idx, l, k, k_prime, n, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], - ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1]); - #endif + for (int i=0; iofdm_symbol_size + k; + int32_t precodatatx_F = nr_layer_precoder(tx_precoding, W_prec, pusch_pdu->nrOfLayers, re_offset); + ((int16_t*)txdataF[ap])[(re_offset<<1)] = ((int16_t *) &precodatatx_F)[0]; + ((int16_t*)txdataF[ap])[(re_offset<<1) + 1] = ((int16_t *) &precodatatx_F)[1]; + + if (++k >= frame_parms->ofdm_symbol_size) + { + k -= frame_parms->ofdm_symbol_size; + } + } + } + } //RB loop + } // symbol loop + }// port loop - - dmrs_idx++; - k_prime++; - k_prime&=1; - n+=(k_prime)?0:1; - - } else if (is_ptrs == 1) { - - ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; - ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; - - ptrs_idx++; - - } else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) { - - ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; - ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; - - #ifdef DEBUG_PUSCH_MAPPING - printf("m %d\t l %d \t k %d \t txdataF: %d %d\n", - m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], - ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1]); - #endif - - m++; - - } else { - - ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = 0; - ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = 0; - - } - - if (++k >= frame_parms->ofdm_symbol_size) - k -= frame_parms->ofdm_symbol_size; - } - } - } - } + } //for (cwd_index = 0;cwd_index < num_of_codewords; cwd_index++) NR_UL_UE_HARQ_t *harq_process_ulsch=NULL; harq_process_ulsch = UE->ulsch[thread_id][gNB_id][0]->harq_processes[harq_pid]; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 11b5112a6c..600ba07cb3 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -479,7 +479,7 @@ typedef struct { /// \brief Cross-correlation of two UE signals. /// - first index: rx antenna [0..nb_antennas_rx[ /// - second index: symbol [0..] - int32_t **rho; + int32_t ***rho; /// \f$\log_2(\max|H_i|^2)\f$ int16_t log2_maxh; /// \brief Magnitude of Uplink Channel first layer (16QAM level/First 64QAM level). @@ -509,6 +509,10 @@ typedef struct { /// \brief llr values. /// - first index: ? [0..1179743] (hard coded) int16_t *llr; + /// \brief llr values per layer. + /// - first index: ? [0..3] (hard coded) + /// - first index: ? [0..1179743] (hard coded) + int16_t **llr_layers; /// DMRS symbol index, to be updated every DMRS symbol within a slot. uint8_t dmrs_symbol; // PTRS symbol index, to be updated every PTRS symbol within a slot. diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 69978f5548..02988b590a 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -229,6 +229,9 @@ typedef struct { typedef struct { /// TX buffers for multiple layers int32_t *txdataF_layers[NR_MAX_NB_LAYERS]; + + /// TX buffers for UE-spec transmission + int32_t *txdataF_precoding[NR_MAX_NB_LAYERS]; } NR_UE_PUSCH; typedef struct { diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index c385d323e3..fbe094f41c 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -93,7 +93,7 @@ #define NR_MAX_CSET_DURATION 3 #define NR_MAX_NB_RBG 18 -#define NR_MAX_NB_LAYERS 2 // 8 // SU-MIMO (3GPP TS 38.211 V15.4.0 section 7.3.1.3) +#define NR_MAX_NB_LAYERS 4 // 8 // SU-MIMO (3GPP TS 38.211 V15.4.0 section 7.3.1.3) #define NR_MAX_NB_CODEWORDS 2 #define NR_MAX_NB_HARQ_PROCESSES 16 #define NR_MAX_PDSCH_ENCODED_LENGTH (NR_MAX_NB_RB*NR_SYMBOLS_PER_SLOT*NR_NB_SC_PER_RB*8*NR_MAX_NB_LAYERS) // 8 is the maximum modulation order (it was 950984 before !!) @@ -104,11 +104,11 @@ #define MAX_NUM_NR_DLSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) #define MAX_NR_DLSCH_PAYLOAD_BYTES (MAX_NUM_NR_DLSCH_SEGMENTS*1056) -#define MAX_NUM_NR_ULSCH_SEGMENTS 34 +#define MAX_NUM_NR_ULSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) #define MAX_NR_ULSCH_PAYLOAD_BYTES (MAX_NUM_NR_ULSCH_SEGMENTS*1056) -#define MAX_NUM_NR_CHANNEL_BITS (14*273*12*8) // 14 symbols, 273 RB -#define MAX_NUM_NR_RE (14*273*12) +#define MAX_NUM_NR_CHANNEL_BITS (4*14*273*12*8) // 14 symbols, 273 RB +#define MAX_NUM_NR_RE (4*14*273*12) #define NR_RX_NB_TH 1 #define NR_NB_TH_SLOT 2 diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c index d8fa11d157..640a0740ef 100644 --- a/openair1/SCHED_NR/nr_ru_procedures.c +++ b/openair1/SCHED_NR/nr_ru_procedures.c @@ -526,7 +526,8 @@ void nr_fep0(RU_t *ru, int first_half) { end_symbol = NR_SYMBOLS_PER_SLOT; } - LOG_D(PHY,"In fep0 for slot = %d, first_half = %d, start_symbol = %d, end_symbol = %d\n", proc->tti_rx, first_half, start_symbol, end_symbol); + LOG_D(PHY,"In fep0 for slot = %d, first_half = %d, start_symbol = %d, end_symbol = %d, nb_antennas_rx = %d,N_TA_offset = %d\n", + proc->tti_rx, first_half, start_symbol, end_symbol,fp->nb_antennas_rx,ru->N_TA_offset); // printf("fep0: slot %d\n",slot); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX+proc->tti_rx, 1); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1685e206ae..63c708e565 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -253,13 +253,13 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { // if all segments are done if (rdata->nbSegments == ulsch_harq->processedSegments) { if (decodeSuccess) { - LOG_D(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", + LOG_I(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", gNB->Mod_id,ulsch_harq->frame,ulsch_harq->slot,rdata->harq_pid,pusch_pdu->pusch_data.new_data_indicator,ulsch_harq->status,ulsch_harq->round,ulsch_harq->TBS,rdata->decodeIterations); ulsch_harq->status = SCH_IDLE; ulsch_harq->round = 0; ulsch->harq_mask &= ~(1 << rdata->harq_pid); - LOG_D(PHY, "ULSCH received ok \n"); + LOG_I(PHY, "ULSCH received ok \n"); nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 0,0); //dumpsig=1; } else { @@ -281,7 +281,7 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { } ulsch_harq->handled = 1; - LOG_D(PHY, "ULSCH %d in error\n",rdata->ulsch_id); + LOG_I(PHY, "ULSCH %d in error\n",rdata->ulsch_id); nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 1,0); } /* @@ -365,6 +365,14 @@ void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs pusch_pdu->qam_mod_order, pusch_pdu->nrOfLayers); + + + nr_ulsch_layer_demapping(gNB->pusch_vars[ULSCH_id]->llr, + pusch_pdu->nrOfLayers, + pusch_pdu->qam_mod_order, + G, + gNB->pusch_vars[ULSCH_id]->llr_layers); + //---------------------------------------------------------- //------------------- ULSCH unscrambling ------------------- //---------------------------------------------------------- @@ -731,7 +739,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { (ulsch_harq->slot == slot_rx) && (ulsch_harq->handled == 0)){ - LOG_D(PHY, "PUSCH detection started in frame %d slot %d\n", + LOG_I(PHY, "PUSCH detection started in frame %d slot %d\n", frame_rx,slot_rx); int num_dmrs=0; for (int s=0;srx_pusch_stats); no_sig = nr_rx_pusch(gNB, ULSCH_id, frame_rx, slot_rx, harq_pid); if (no_sig) { - LOG_D(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); + LOG_I(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, harq_pid, 1,1); return 1; } @@ -783,7 +791,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot) + gNB->pusch_thres) { NR_gNB_SCH_STATS_t *stats=get_ulsch_stats(gNB,ulsch); - LOG_D(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, + LOG_I(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot), dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot),gNB->pusch_thres); gNB->pusch_vars[ULSCH_id]->ulsch_power_tot = gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot; diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index f6e4d90bee..8e1df435c3 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -157,7 +157,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ for (i = 0; i < ul_config->number_pdus; ++i){ AssertFatal(ul_config->ul_config_list[i].pdu_type <= FAPI_NR_UL_CONFIG_TYPES,"pdu_type %d out of bounds\n",ul_config->ul_config_list[i].pdu_type); - LOG_D(PHY, "In %s: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); + LOG_I(PHY, "In %s i %d: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, i, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); uint8_t pdu_type = ul_config->ul_config_list[i].pdu_type, current_harq_pid, gNB_id = 0; /* PRACH */ @@ -180,6 +180,8 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ if (harq_process_ul_ue){ nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &harq_process_ul_ue->pusch_pdu; + + LOG_I(PHY, "In %s i %d: copy pusch_config_pdu nrOfLayers:%d, num_dmrs_cdm_grps_no_data:%d \n", __FUNCTION__, i, pusch_config_pdu->nrOfLayers,pusch_config_pdu->num_dmrs_cdm_grps_no_data); memcpy(pusch_pdu, pusch_config_pdu, sizeof(nfapi_nr_ue_pusch_pdu_t)); diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 940931694c..0a0a9cc2f9 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -270,9 +270,12 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, AssertFatal(ue->CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX,VCD_FUNCTION_IN); - - memset(ue->common_vars.txdataF[0], 0, sizeof(int)*14*ue->frame_parms.ofdm_symbol_size); - + + for(int i=0; i< ue->frame_parms.nb_antennas_tx; ++i) + { + memset(ue->common_vars.txdataF[i], 0, sizeof(int)*14*ue->frame_parms.ofdm_symbol_size); + } + LOG_D(PHY,"****** start TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx); diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 1eb6027936..d5a1054654 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -768,7 +768,7 @@ int main(int argc, char **argv) prepare_scd(scd); - fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, 0, 0, 0); + fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, n_rx, 0, 0, 0); /* RRC parameter validation for secondaryCellGroup */ fix_scd(scd); diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 94aea36a67..fb8b4295ae 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -137,6 +137,7 @@ int main(int argc, char **argv) uint16_t nb_symb_sch = 12; uint16_t nb_rb = 50; uint8_t Imcs = 9; + uint8_t Nl = 1; double DS_TDL = .03; @@ -150,7 +151,7 @@ int main(int argc, char **argv) randominit(0); //while ((c = getopt(argc, argv, "df:hpg:i:j:n:l:m:r:s:S:y:z:M:N:F:R:P:")) != -1) { - while ((c = getopt(argc, argv, "hg:n:s:S:py:z:M:N:R:F:m:l:r:")) != -1) { + while ((c = getopt(argc, argv, "hg:n:s:S:py:z:M:N:R:F:m:l:r:W:")) != -1) { switch (c) { /*case 'f': write_output_file = 1; @@ -251,7 +252,7 @@ int main(int argc, char **argv) case 'y': n_tx = atoi(optarg); - if ((n_tx == 0) || (n_tx > 2)) { + if ((n_tx == 0) || (n_tx > 4)) { printf("Unsupported number of TX antennas %d. Exiting.\n", n_tx); exit(-1); } @@ -261,7 +262,7 @@ int main(int argc, char **argv) case 'z': n_rx = atoi(optarg); - if ((n_rx == 0) || (n_rx > 2)) { + if ((n_rx == 0) || (n_rx > 4)) { printf("Unsupported number of RX antennas %d. Exiting.\n", n_rx); exit(-1); } @@ -299,6 +300,10 @@ int main(int argc, char **argv) printf("Illegal PBCH phase (0-3) got %d\n", pbch_phase); break;*/ + case 'W': + Nl = atoi(optarg); + break; + case 'm': Imcs = atoi(optarg); #ifdef DEBUG_NR_ULSCHSIM @@ -335,13 +340,14 @@ int main(int argc, char **argv) printf("-z Number of RX antennas used in UE\n"); //printf("-i Relative strength of first intefering eNB (in dB) - cell_id mod 3 = 1\n"); //printf("-j Relative strength of second intefering eNB (in dB) - cell_id mod 3 = 2\n"); + printf("-W number of layer\n"); printf("-M Multiple SSB positions in burst\n"); printf("-N Nid_cell\n"); printf("-R N_RB_UL\n"); printf("-F Input filename (.txt format) for RX conformance testing\n"); - printf("-m\n"); - printf("-l\n"); - printf("-r\n"); + printf("-m MCS\n"); + printf("-l number of symbol\n"); + printf("-r number of RB\n"); //printf("-O oversampling factor (1,2,4,8,16)\n"); //printf("-A Interpolation_filname Run with Abstraction to generate Scatter plot using interpolation polynomial in file\n"); //printf("-C Generate Calibration information for Abstraction (effective SNR adjustment to remove Pe bias w.r.t. AWGN)\n"); @@ -359,8 +365,8 @@ int main(int argc, char **argv) snr1 = snr0 + 10; gNB2UE = new_channel_desc_scm(n_tx, - n_rx, - channel_model, + n_rx, + channel_model, 61.44e6, //N_RB2sampling_rate(N_RB_DL), 40e6, //N_RB2channel_bandwidth(N_RB_DL), DS_TDL, @@ -426,8 +432,7 @@ int main(int argc, char **argv) uint8_t length_dmrs = 1; uint8_t N_PRB_oh; uint16_t N_RE_prime,code_rate; - unsigned char mod_order; - uint8_t Nl = 1; + unsigned char mod_order; uint8_t rvidx = 0; uint8_t UE_id = 0; @@ -437,9 +442,14 @@ int main(int argc, char **argv) NR_UE_ULSCH_t *ulsch_ue = UE->ulsch[0][0][0]; + if ((Nl==4)||(Nl==3)) + { + nb_re_dmrs = nb_re_dmrs*2; + } + mod_order = nr_get_Qm_ul(Imcs, 0); code_rate = nr_get_code_rate_ul(Imcs, 0); - available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, 1); + available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, Nl); TBS = nr_compute_tbs(mod_order,code_rate, nb_rb, nb_symb_sch, nb_re_dmrs*length_dmrs, 0, 0, Nl); printf("\nAvailable bits %u TBS %u mod_order %d\n", available_bits, TBS, mod_order); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 69316d6b88..b6cfae3fe1 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -282,7 +282,7 @@ int main(int argc, char **argv) int gNB_id = 0; int ap; int tx_offset; - int32_t txlev=0; + int32_t txlev_sum = 0, atxlev[4]; int start_rb = 0; int UE_id =0; // [hna] only works for UE_id = 0 because NUMBER_OF_NR_UE_MAX is set to 1 (phy_init_nr_gNB causes segmentation fault) float target_error_rate = 0.01; @@ -307,7 +307,7 @@ int main(int argc, char **argv) uint16_t ptrsSymbPerSlot = 0; uint16_t ptrsRePerSymb = 0; - uint8_t transform_precoding = 1; // 0 - ENABLE, 1 - DISABLE + uint8_t transformPrecoder = transformPrecoder_disabled; // 0 - ENABLE, 1 - DISABLE uint8_t num_dmrs_cdm_grps_no_data = 1; uint8_t mcs_table = 0; @@ -329,7 +329,7 @@ int main(int argc, char **argv) /* initialize the sin-cos table */ InitSinLUT(); - while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:ikl:m:n:p:r:s:u:w:y:z:F:G:H:M:N:PR:S:T:U:L:Z")) != -1) { + while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:i:kl:m:n:p:r:s:u:w:y:z:F:G:H:M:N:PR:S:T:U:L:Z:W:")) != -1) { printf("handling optarg %c\n",c); switch (c) { @@ -363,8 +363,8 @@ int main(int argc, char **argv) scg_fd = fopen(optarg, "r"); if (scg_fd == NULL) { - printf("Error opening %s\n", optarg); - exit(-1); + printf("Error opening %s\n", optarg); + exit(-1); } break; @@ -437,6 +437,10 @@ int main(int argc, char **argv) case 'm': Imcs = atoi(optarg); break; + + case 'W': + precod_nbr_layers = atoi(optarg); + break; case 'n': n_trials = atoi(optarg); @@ -485,9 +489,9 @@ int main(int argc, char **argv) case 'y': n_tx = atoi(optarg); - if ((n_tx == 0) || (n_tx > 2)) { - printf("Unsupported number of tx antennas %d\n", n_tx); - exit(-1); + if ((n_tx == 0) || (n_tx > 4)) { + printf("Unsupported number of tx antennas %d\n", n_tx); + exit(-1); } break; @@ -496,8 +500,8 @@ int main(int argc, char **argv) n_rx = atoi(optarg); if ((n_rx == 0) || (n_rx > 8)) { - printf("Unsupported number of rx antennas %d\n", n_rx); - exit(-1); + printf("Unsupported number of rx antennas %d\n", n_rx); + exit(-1); } break; @@ -506,8 +510,8 @@ int main(int argc, char **argv) input_fd = fopen(optarg, "r"); if (input_fd == NULL) { - printf("Problem with filename %s\n", optarg); - exit(-1); + printf("Problem with filename %s\n", optarg); + exit(-1); } break; @@ -568,11 +572,11 @@ int main(int argc, char **argv) case 'Z': - transform_precoding = 0; // enabled + transformPrecoder = transformPrecoder_enabled; num_dmrs_cdm_grps_no_data = 2; mcs_table = 3; - printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transform_precoding); + printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transformPrecoder); break; @@ -611,6 +615,7 @@ int main(int argc, char **argv) printf("-U Change DMRS Config, arguments list DMRS TYPE{0=A,1=B} DMRS AddPos{0:3}, e.g. -U 2 0 2 \n"); printf("-Q If -F used, read parameters from file\n"); printf("-Z If -Z is used, SC-FDMA or transform precoding is enabled in Uplink \n"); + printf("-W Num of layer for PUSCH\n"); exit(-1); break; @@ -713,7 +718,7 @@ int main(int argc, char **argv) prepare_scd(scd); - fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, 0, 0, 0); + fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, n_rx, 0, 0, 0); // xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)secondaryCellGroup); @@ -724,9 +729,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, scc, &rrc.carrier.mib,0, 0, NULL); + rrc_mac_config_req_gNB(0,0, n_tx, n_rx, n_tx, scc, &rrc.carrier.mib,0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, n_tx, n_rx, n_tx, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); frame_parms->nb_antennas_tx = n_tx; frame_parms->nb_antennas_rx = n_rx; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; @@ -870,14 +875,18 @@ int main(int argc, char **argv) uint16_t number_dmrs_symbols = get_dmrs_symbols_in_slot(l_prime_mask, nb_symb_sch); printf("num dmrs sym %d\n",number_dmrs_symbols); uint8_t nb_re_dmrs = (dmrs_config_type == pusch_dmrs_type1) ? 6 : 4; - - // if transform precoding is enabled - if (transform_precoding == 0) { + + if ((frame_parms->nb_antennas_tx==4)&&(precod_nbr_layers==4)) + { + num_dmrs_cdm_grps_no_data = 2; + } + + if (transformPrecoder == transformPrecoder_enabled) { AssertFatal(enable_ptrs == 0, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); int8_t index = get_index_for_dmrs_lowpapr_seq((NR_NB_SC_PER_RB/2) * nb_rb); - AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); + AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); dmrs_config_type = pusch_dmrs_type1; @@ -886,7 +895,7 @@ int main(int argc, char **argv) nb_re_dmrs = nb_re_dmrs * num_dmrs_cdm_grps_no_data; - unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, mod_order, 1); + unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, mod_order, precod_nbr_layers); unsigned int TBS = nr_compute_tbs(mod_order, code_rate, nb_rb, nb_symb_sch, nb_re_dmrs * number_dmrs_symbols, 0, 0, precod_nbr_layers); @@ -1082,14 +1091,14 @@ int main(int argc, char **argv) pusch_pdu->mcs_table = mcs_table; pusch_pdu->target_code_rate = code_rate; pusch_pdu->qam_mod_order = mod_order; - pusch_pdu->transform_precoding = transform_precoding; + pusch_pdu->transformPrecoder = transformPrecoder; pusch_pdu->data_scrambling_id = *scc->physCellId; - pusch_pdu->nrOfLayers = 1; + pusch_pdu->nrOfLayers = precod_nbr_layers; pusch_pdu->ul_dmrs_symb_pos = l_prime_mask; pusch_pdu->dmrs_config_type = dmrs_config_type; pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId; pusch_pdu->scid = 0; - pusch_pdu->dmrs_ports = 1; + pusch_pdu->dmrs_ports = ((1<num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; pusch_pdu->resource_alloc = 1; pusch_pdu->rb_start = start_rb; @@ -1109,7 +1118,7 @@ int main(int argc, char **argv) pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; // if transform precoding is enabled - if (transform_precoding == 0) { + if (transformPrecoder == transformPrecoder_enabled) { pusch_pdu->dfts_ofdm.low_papr_group_number = *scc->physCellId % 30; // U as defined in 38.211 section 6.4.1.1.1.2 pusch_pdu->dfts_ofdm.low_papr_sequence_number = 0; // V as defined in 38.211 section 6.4.1.1.1.2 @@ -1167,10 +1176,10 @@ int main(int argc, char **argv) ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list = (nfapi_nr_ue_ptrs_ports_t *) malloc(2*sizeof(nfapi_nr_ue_ptrs_ports_t)); ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; - ul_config.ul_config_list[0].pusch_config_pdu.transform_precoding = transform_precoding; + ul_config.ul_config_list[0].pusch_config_pdu.transformPrecoder = transformPrecoder; // if transform precoding is enabled - if (transform_precoding == 0) { + if (transformPrecoder == transformPrecoder_enabled) { ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_group_number = *scc->physCellId % 30;// U as defined in 38.211 section 6.4.1.1.1.2 ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_sequence_number = 0;// V as defined in 38.211 section 6.4.1.1.1.2 @@ -1198,28 +1207,54 @@ int main(int argc, char **argv) LOG_D(PHY, "Sending Uplink data \n"); nr_ue_pusch_common_procedures(UE, slot, - &UE->frame_parms,1); - - if (n_trials==1) { - LOG_M("txsig0.m","txs0", UE->common_vars.txdata[0],frame_parms->samples_per_subframe*10,1,1); + &UE->frame_parms, + frame_parms->nb_antennas_tx); + + + if (n_trials==1) + { + LOG_M("txsig0.m","txs0", &UE->common_vars.txdata[0][slot_offset],slot_length,1,1); LOG_M("txsig0F.m","txs0F", UE->common_vars.txdataF[0],frame_parms->ofdm_symbol_size*14,1,1); + if (precod_nbr_layers > 1) + { + LOG_M("txsig1.m","txs1", &UE->common_vars.txdata[1][slot_offset],slot_length,1,1); + LOG_M("txsig1F.m","txs1F", UE->common_vars.txdataF[1],frame_parms->ofdm_symbol_size*14,1,1); + if (precod_nbr_layers==4) + { + LOG_M("txsig2.m","txs2", &UE->common_vars.txdata[2][slot_offset],slot_length,1,1); + LOG_M("txsig3.m","txs3", &UE->common_vars.txdata[3][slot_offset],slot_length,1,1); + + LOG_M("txsig2F.m","txs2F", UE->common_vars.txdataF[2],frame_parms->ofdm_symbol_size*14,1,1); + LOG_M("txsig3F.m","txs3F", UE->common_vars.txdataF[3],frame_parms->ofdm_symbol_size*14,1,1); + } + } } /////////// //////////////////////////////////////////////////// tx_offset = frame_parms->get_samples_slot_timestamp(slot,frame_parms,0); - - txlev = signal_energy(&UE->common_vars.txdata[0][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], + txlev_sum = 0; + for (int aa=0; aanb_antennas_tx; aa++) + { + atxlev[aa] = signal_energy(&UE->common_vars.txdata[aa][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples); - } - else n_trials = 1; + + txlev_sum += atxlev[aa]; + + if (n_trials==1) printf("txlev[%d] = %d (%f dB) txlev_sum %d\n",aa,atxlev[aa],10*log10((double)atxlev[aa]),txlev_sum); + } + } + else + { + n_trials = 1; + } - if (input_fd == NULL ) { - - sigma_dB = 10 * log10((double)txlev * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; + if (input_fd == NULL ) + { + sigma_dB = 10 * log10((double)txlev_sum * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; sigma = pow(10,sigma_dB/10); - if(n_trials==1) printf("sigma %f (%f dB), txlev %f (factor %f)\n",sigma,sigma_dB,10*log10((double)txlev),(double)(double) + if(n_trials==1) printf("sigma %f (%f dB), txlev_sum %f (factor %f)\n",sigma,sigma_dB,10*log10((double)txlev_sum),(double)(double) frame_parms->ofdm_symbol_size/(12*nb_rb)); for (i=0; imax_Doppler == 0) { + if (UE2gNB->max_Doppler == 0) + { multipath_channel(UE2gNB, s_re, s_im, r_re, r_im, slot_length, 0, (n_trials==1)?1:0); } else { multipath_tv_channel(UE2gNB, s_re, s_im, r_re, r_im, 2*slot_length, 0); } - for (i=0; inb_antennas_rx; ap++) { + for (i=0; inb_antennas_rx; ap++) + { + if (channel_model == AWGN) + { + double H_awgn[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 + {0.0, 1.0, 0.0, 0.0}, //rx 1 + {0.0, 0.0, 1.0, 0.0}, //rx 2 + {0.0, 0.0, 0.0, 1.0}};//rx 3 + #if 0 + double H_awgn_mimo[4][4] ={{1.0, 0.5, 0.25, 0.125},//rx 0 + {0.5, 1.0, 0.5, 0.25}, //rx 1 + {0.25, 0.5, 1.0, 0.5}, //rx 2 + {0.125, 0.25, 0.5, 1.0}};//rx 3 + #endif + // sum up signals from different Tx antennas + r_re[ap][i] = 0; + r_im[ap][i] = 0; + + for (int aa=0; aaofdm_symbol_size/(12*nb_rb))) - SNR;; + sigma = pow(10,sigma_dB/10); + ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); // convert to fixed point ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); + + #if 0 + ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i])); // convert to fixed point + ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i])); + + + ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) ] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i) ]; // convert to fixed point + ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i)+1 ]; + #endif + /* Add phase noise if enabled */ if (pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { phase_noise(ts, &((int16_t*)&gNB->common_vars.rxdata[ap][slot_offset])[(2*i)], @@ -1250,7 +1324,8 @@ int main(int argc, char **argv) } /*End input_fd */ - if(pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { + if(pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) + { set_ptrs_symb_idx(&ptrsSymPos, pusch_pdu->nr_of_symbols, pusch_pdu->start_symbol_index, @@ -1268,24 +1343,37 @@ int main(int argc, char **argv) gNB->UL_INFO.rx_ind.number_of_pdus = 0; gNB->UL_INFO.crc_ind.number_crcs = 0; - phy_procedures_gNB_common_RX(gNB, frame, slot); + phy_procedures_gNB_common_RX(gNB, frame, slot); - ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot); + ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot); - if (n_trials==1 && round==0) { + if (n_trials==1 && round==0) + { LOG_M("rxsig0.m","rx0",&gNB->common_vars.rxdata[0][slot_offset],slot_length,1,1); - LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0]+start_symbol*frame_parms->ofdm_symbol_size,nb_symb_sch*frame_parms->ofdm_symbol_size,1,1); - + LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0],14*frame_parms->ofdm_symbol_size,1,1); + if (precod_nbr_layers > 1) + { + LOG_M("rxsig1.m","rx1",&gNB->common_vars.rxdata[1][slot_offset],slot_length,1,1); + LOG_M("rxsigF1.m","rxsF1",gNB->common_vars.rxdataF[1],14*frame_parms->ofdm_symbol_size,1,1); + if (precod_nbr_layers==4) + { + LOG_M("rxsig2.m","rx2",&gNB->common_vars.rxdata[2][slot_offset],slot_length,1,1); + LOG_M("rxsig3.m","rx3",&gNB->common_vars.rxdata[3][slot_offset],slot_length,1,1); + + LOG_M("rxsigF2.m","rxsF2",gNB->common_vars.rxdataF[2],14*frame_parms->ofdm_symbol_size,1,1); + LOG_M("rxsigF3.m","rxsF3",gNB->common_vars.rxdataF[3],14*frame_parms->ofdm_symbol_size,1,1); + } + } } if (n_trials == 1 && round==0) { -#ifdef __AVX2__ + #ifdef __AVX2__ int off = ((nb_rb&1) == 1)? 4:0; -#else + #else int off = 0; -#endif + #endif LOG_M("rxsigF0_ext.m","rxsF0_ext", &gNB->pusch_vars[0]->rxdataF_ext[0][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); @@ -1298,11 +1386,77 @@ int main(int argc, char **argv) (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); LOG_M("rxsigF0_comp.m","rxsF0_comp", &gNB->pusch_vars[0]->rxdataF_comp[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_M("chmagF0.m","chmF0", - &gNB->pusch_vars[0]->ul_ch_mag[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_M("chmagbF0.m","chmbF0", - &gNB->pusch_vars[0]->ul_ch_magb[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - if (n_rx == 2) { + LOG_M("chmagF0.m","chmF0", + &gNB->pusch_vars[0]->ul_ch_mag[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("chmagbF0.m","chmbF0", + &gNB->pusch_vars[0]->ul_ch_magb[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF0_llrlayers0.m","rxsF0_llrlayers0", + &gNB->pusch_vars[0]->llr_layers[0][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + + if (precod_nbr_layers==2) + { + LOG_M("rxsigF1_ext.m","rxsF1_ext", + &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + + LOG_M("chestF3.m","chF3", + &gNB->pusch_vars[0]->ul_ch_estimates[3][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); + + LOG_M("chestF3_ext.m","chF3_ext", + &gNB->pusch_vars[0]->ul_ch_estimates_ext[3][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], + (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + + LOG_M("rxsigF2_comp.m","rxsF2_comp", + &gNB->pusch_vars[0]->rxdataF_comp[2][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + + LOG_M("rxsigF0_llrlayers1.m","rxsF0_llrlayers1", + &gNB->pusch_vars[0]->llr_layers[1][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + + } + + if (precod_nbr_layers==4) + { + LOG_M("rxsigF1_ext.m","rxsF1_ext", + &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF2_ext.m","rxsF2_ext", + &gNB->pusch_vars[0]->rxdataF_ext[2][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF3_ext.m","rxsF3_ext", + &gNB->pusch_vars[0]->rxdataF_ext[3][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + + LOG_M("chestF5.m","chF5", + &gNB->pusch_vars[0]->ul_ch_estimates[5][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); + LOG_M("chestF10.m","chF10", + &gNB->pusch_vars[0]->ul_ch_estimates[10][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); + LOG_M("chestF15.m","chF15", + &gNB->pusch_vars[0]->ul_ch_estimates[15][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); + + + LOG_M("chestF5_ext.m","chF5_ext", + &gNB->pusch_vars[0]->ul_ch_estimates_ext[5][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], + (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("chestF10_ext.m","chF10_ext", + &gNB->pusch_vars[0]->ul_ch_estimates_ext[10][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], + (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("chestF15_ext.m","chF15_ext", + &gNB->pusch_vars[0]->ul_ch_estimates_ext[15][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], + (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + + + LOG_M("rxsigF4_comp.m","rxsF4_comp", + &gNB->pusch_vars[0]->rxdataF_comp[4][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF8_comp.m","rxsF8_comp", + &gNB->pusch_vars[0]->rxdataF_comp[8][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF12_comp.m","rxsF12_comp", + &gNB->pusch_vars[0]->rxdataF_comp[12][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF0_llrlayers1.m","rxsF0_llrlayers1", + &gNB->pusch_vars[0]->llr_layers[1][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + LOG_M("rxsigF0_llrlayers2.m","rxsF0_llrlayers2", + &gNB->pusch_vars[0]->llr_layers[2][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + LOG_M("rxsigF0_llrlayers3.m","rxsF0_llrlayers3", + &gNB->pusch_vars[0]->llr_layers[3][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + } + + #if 0 + if (n_rx == 2) { LOG_MM("rxsigF0_comp.m","rxsF1_comp", &gNB->pusch_vars[0]->rxdataF_comp[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); LOG_MM("rxsigF0_ext.m","rxsF1_ext", @@ -1349,9 +1503,10 @@ int main(int argc, char **argv) LOG_MM("chmagbF0.m","chmbF3", &gNB->pusch_vars[0]->ul_ch_magb[3][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); } + #endif LOG_M("rxsigF0_llr.m","rxsF0_llr", - &gNB->pusch_vars[0]->llr[0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + &gNB->pusch_vars[0]->llr[0],precod_nbr_layers*(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); } //////////////////////////////////////////////////////////// diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 3f4be9dd58..7e5be9eeba 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2708,6 +2708,9 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, LOG_D(NR_MAC,"dci_pdu->srs_resource_indicator.nbits %d\n",dci_pdu->srs_resource_indicator.nbits); // Precoding info and number of layers long transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, ubwpd, (uint8_t*)&format, rnti_type, 0); + + pusch_antenna_ports = *pusch_Config->maxRank; + dci_pdu->precoding_information.nbits=0; if (pusch_Config && pusch_Config->txConfig != NULL){ diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index c23d4bb073..963723725f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -190,7 +190,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con else transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; } - + pusch_config_pdu->transformPrecoder = transformPrecoder; /* PRECOD_NBR_LAYERS */ if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_nonCodebook)); @@ -198,11 +198,12 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)){ - uint8_t n_antenna_port = 0; //FIXME!!! + uint8_t n_antenna_port = mac->phy_config.config_req.carrier_config.num_tx_ant; if (n_antenna_port == 1); // 1 antenna port and the higher layer parameter txConfig = codebook 0 bits - if (n_antenna_port == 4){ // 4 antenna port and the higher layer parameter txConfig = codebook + if (n_antenna_port == 4) + { // 4 antenna port and the higher layer parameter txConfig = codebook // Table 7.3.1.1.2-2: transformPrecoder=disabled and maxRank = 2 or 3 or 4 if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) @@ -212,54 +213,56 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][0]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1]; } if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent){ pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][2]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3]; } if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][4]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5]; } } // Table 7.3.1.1.2-3: transformPrecoder= enabled, or transformPrecoder=disabled and maxRank = 1 if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) - && (*pusch_Config->maxRank == 1)){ + && (*pusch_Config->maxRank == 1)) + { if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][6]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7]; } if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent){ pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][8]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9]; } if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][10]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11]; } } } - if (n_antenna_port == 4){ // 2 antenna port and the higher layer parameter txConfig = codebook + if (n_antenna_port == 2) + { // 2 antenna port and the higher layer parameter txConfig = codebook // Table 7.3.1.1.2-4: transformPrecoder=disabled and maxRank = 2 - if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)){ - + if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)) + { if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][12]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13]; } if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][14]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15]; } } @@ -267,16 +270,17 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con // Table 7.3.1.1.2-5: transformPrecoder= enabled, or transformPrecoder= disabled and maxRank = 1 if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) - && (*pusch_Config->maxRank == 1)){ + && (*pusch_Config->maxRank == 1)) + { if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][16]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17]; } if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][18]; - pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19]; + pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19]; } } @@ -285,9 +289,9 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con /*-------------------- Changed to enable Transform precoding in RF SIM------------------------------------------------*/ - /*if (pusch_config_pdu->transform_precoding == transform_precoder_enabled) { + /*if (pusch_config_pdu->transformPrecoder == transformPrecoder_enabled) { - pusch_config_dedicated->transform_precoder = transform_precoder_enabled; + pusch_config_dedicated->transform_precoder = transformPrecoder_enabled; if(pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA != NULL) { @@ -309,7 +313,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con } } else - pusch_config_dedicated->transform_precoder = transform_precoder_disabled;*/ + pusch_config_dedicated->transformPrecoder = ttransformPrecoder_disabled;*/ } // todo: this function shall be reviewed completely because of the many comments left by the author @@ -342,7 +346,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf } long *max_length = NULL; long *dmrs_type = NULL; - LOG_D(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); + LOG_I(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); if (pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA) { max_length = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup->maxLength; @@ -353,7 +357,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf dmrs_type = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->dmrs_Type; } - LOG_D(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n",pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); + LOG_I(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n",pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) && (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-6 pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC @@ -369,35 +373,32 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf } if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && - (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-8/9/10/11 + (dmrs_type == NULL) && (max_length == NULL)) + { // tables 7.3.1.1.2-8/9/10/11 + + if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)) + { + rank = pusch_config_pdu->nrOfLayers; + } if (rank == 1) { - pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC - pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val); //TBC + pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; + pusch_config_pdu->dmrs_ports =1<<((dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val)); } if (rank == 2){ - pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; //TBC - pusch_config_pdu->dmrs_ports = 0; //FIXME - //pusch_config_pdu->dmrs_ports[0] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?0:2):0; - //pusch_config_pdu->dmrs_ports[1] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?2:3):1; + pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; + pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?((dci->antenna_ports.val> 2)?0x5:0xc):0x3; } if (rank == 3){ - pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC - pusch_config_pdu->dmrs_ports = 0; //FIXME - //pusch_config_pdu->dmrs_ports[0] = 0; - //pusch_config_pdu->dmrs_ports[1] = 1; - //pusch_config_pdu->dmrs_ports[2] = 2; + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; + pusch_config_pdu->dmrs_ports = ((1<num_dmrs_cdm_grps_no_data = 2; //TBC - pusch_config_pdu->dmrs_ports = 0; //FIXME - //pusch_config_pdu->dmrs_ports[0] = 0; - //pusch_config_pdu->dmrs_ports[1] = 1; - //pusch_config_pdu->dmrs_ports[2] = 2; - //pusch_config_pdu->dmrs_ports[3] = 3; + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; + pusch_config_pdu->dmrs_ports = ((1<n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME } } - LOG_D(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); + LOG_I(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); } // Configuration of Msg3 PDU according to clauses: @@ -542,6 +543,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; pusch_config_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; pusch_config_pdu->nrOfLayers = 1; + pusch_config_pdu->Tpmi = 0; pusch_config_pdu->rnti = rnti; NR_BWP_UplinkCommon_t *initialUplinkBWP; if (mac->scc) initialUplinkBWP = mac->scc->uplinkConfigCommon->initialUplinkBWP; @@ -634,7 +636,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->scid = 0; // Transform precoding according to 6.1.3 UE procedure for applying transform precoding on PUSCH in 38.214 - pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out + pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out // Resource allocation in frequency domain according to 6.1.2.2 in TS 38.214 pusch_config_pdu->resource_alloc = (mac->cg) ? pusch_Config->resourceAllocation : 1; @@ -680,7 +682,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* Transform precoding */ if (rnti_type != NR_RNTI_CS || (rnti_type == NR_RNTI_CS && dci->ndi == 1)) { - pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); + pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); } /*DCI format-related configuration*/ @@ -716,7 +718,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* TRANSFORM PRECODING ------------------------------------------------------------------------------------------*/ - if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled) { + if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) { pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; @@ -761,7 +763,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->mcs_index = dci->mcs; /* MCS TABLE */ - if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { + if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_Table : NULL, 0, *dci_format, rnti_type, target_ss, false); } else { pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_TableTransformPrecoder : NULL, 1, *dci_format, rnti_type, target_ss, false); @@ -798,9 +800,14 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, mappingtype, add_pos, dmrslength, pusch_config_pdu->start_symbol_index, mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); - if ((mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled)) - pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; - else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled)) + if (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) + { + if (*dci_format != NR_UL_DCI_FORMAT_0_1) + { + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; + } + } + else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; // Num PRB Overhead from PUSCH-ServingCellConfig @@ -821,7 +828,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup && mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { - if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { + if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, @@ -909,6 +916,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in fill_scheduled_response(&scheduled_response, &dcireq.dl_config_req, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) + LOG_I(NR_MAC,"1# scheduled_response transmitted, %d, %d\n", rx_frame, rx_slot); mac->if_module->scheduled_response(&scheduled_response); } else { @@ -1022,6 +1030,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in fill_scheduled_response(&scheduled_response, NULL, ul_config, &tx_req, mod_id, cc_id, rx_frame, rx_slot, ul_info->thread_id); if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){ + LOG_I(NR_MAC,"3# scheduled_response transmitted,%d, %d\n", frame_tx, slot_tx); mac->if_module->scheduled_response(&scheduled_response); } pthread_mutex_lock(&ul_config->mutex_ul_config); diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 1cb3fdb708..d9c98852a3 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -388,7 +388,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts cfg->carrier_config.num_rx_ant.value = pusch_AntennaPorts; AssertFatal(pusch_AntennaPorts > 0 && pusch_AntennaPorts < 13, "pusch_AntennaPorts in 1...12\n"); cfg->carrier_config.num_rx_ant.tl.tag = NFAPI_NR_CONFIG_NUM_RX_ANT_TAG; - LOG_I(NR_MAC,"Set TX/RX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); + LOG_I(NR_MAC,"Set RX antenna number to %d, Set TX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,cfg->carrier_config.num_rx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); AssertFatal(cfg->carrier_config.num_tx_ant.value > 0,"carrier_config.num_tx_ant.value %d !\n",cfg->carrier_config.num_tx_ant.value ); cfg->num_tlv++; cfg->num_tlv++; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 6ac7f3f82e..5de36bb2db 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -977,9 +977,9 @@ void fill_msg3_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, pusch_pdu->target_code_rate = nr_get_code_rate_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); pusch_pdu->qam_mod_order = nr_get_Qm_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) - pusch_pdu->transform_precoding = 1; + pusch_pdu->transformPrecoder = 1; else - pusch_pdu->transform_precoding = 0; + pusch_pdu->transformPrecoder = 0; pusch_pdu->data_scrambling_id = *scc->physCellId; pusch_pdu->nrOfLayers = 1; pusch_pdu->ul_dmrs_symb_pos = get_l_prime(nr_of_symbols,mappingtype,pusch_dmrs_pos2,pusch_len1,start_symbol_index, scc->dmrs_TypeA_Position); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index abc007197d..9cbbe7d9a1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1139,13 +1139,14 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_payload.dmrs_sequence_initialization.val = pdsch_pdu->SCID; LOG_D(NR_MAC, "%4d.%2d DCI type 1 payload: freq_alloc %d (%d,%d,%d), " - "time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", + "nrOfLayers %d, time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", frame, slot, dci_payload.frequency_domain_assignment.val, pdsch_pdu->rbStart, pdsch_pdu->rbSize, pdsch_pdu->BWPSize, + pdsch_pdu->nrOfLayers, dci_payload.time_domain_assignment.val, dci_payload.vrb_to_prb_mapping.val, dci_payload.mcs, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index f1005cea70..8f7c856a30 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -405,6 +405,7 @@ void nr_preprocessor_phytest(module_id_t module_id, uint32_t target_ul_mcs = 9; uint32_t target_ul_bw = 50; +uint32_t target_ul_Nl = 1; uint64_t ulsch_slot_bitmap = (1 << 8); bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_t slot) { @@ -446,15 +447,21 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; - const uint8_t num_dmrs_cdm_grps_no_data = 1; + uint8_t num_dmrs_cdm_grps_no_data = 1; + if ((target_ul_Nl==4)||(target_ul_Nl==3)) + { + num_dmrs_cdm_grps_no_data = 2; + } + /* we want to avoid a lengthy deduction of DMRS and other parameters in * every TTI if we can save it, so check whether dci_format, TDA, or * num_dmrs_cdm_grps_no_data has changed and only then recompute */ NR_pusch_semi_static_t *ps = &sched_ctrl->pusch_semi_static; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format + || ps->nrOfLayers != target_ul_Nl || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) - nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, NULL,dci_format, tda, num_dmrs_cdm_grps_no_data, ps); + nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, NULL,dci_format, tda, num_dmrs_cdm_grps_no_data,target_ul_Nl,ps); uint16_t rbStart = 0; uint16_t rbSize; @@ -525,6 +532,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ sched_pusch->ul_harq_pid = sched_ctrl->retrans_ul_harq.head; /* Calculate TBS from MCS */ + ps->nrOfLayers = target_ul_Nl; sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table); sched_pusch->Qm = nr_get_Qm_ul(mcs, ps->mcs_table); if (ps->pusch_Config->tp_pi2BPSK @@ -539,7 +547,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ ps->N_PRB_DMRS * ps->num_dmrs_symb, 0, // nb_rb_oh 0, - 1 /* NrOfLayers */) + ps->nrOfLayers /* NrOfLayers */) >> 3; /* mark the corresponding RBs as used */ diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 1c0cb0353e..4583da876f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -355,6 +355,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, long dci_format, int tda, uint8_t num_dmrs_cdm_grps_no_data, + uint8_t nrOfLayers, NR_pusch_semi_static_t *ps) { ps->dci_format = dci_format; @@ -371,11 +372,11 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, ps->pusch_Config = ubwp?ubwp->bwp_Dedicated->pusch_Config->choice.setup:(ubwpd ? ubwpd->pusch_Config->choice.setup : NULL); if (ps->pusch_Config == NULL || !ps->pusch_Config->transformPrecoder) - ps->transform_precoding = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; + ps->transformPrecoder = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; else - ps->transform_precoding = *ps->pusch_Config->transformPrecoder; + ps->transformPrecoder = *ps->pusch_Config->transformPrecoder; const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; - if (ps->transform_precoding) + if (ps->transformPrecoder) ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config ? ps->pusch_Config->mcs_Table : NULL, 0, ps->dci_format, @@ -392,6 +393,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, num_dmrs_cdm_grps_no_data = 2; // in case of transform precoding - no Data sent in DMRS symbol } + ps->nrOfLayers = nrOfLayers; ps->num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; /* DMRS calculations */ @@ -689,8 +691,21 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, "Non Codebook configuration non supported\n"); dci_pdu_rel15->srs_resource_indicator.val = 0; // taking resource 0 for SRS } + AssertFatal((pusch_pdu->Tpmi==0), "unsupport Tpmi\n"); + dci_pdu_rel15->precoding_information.val= 0; + if (pusch_pdu->nrOfLayers == 2) + { + dci_pdu_rel15->precoding_information.val = 4; + } + else if (pusch_pdu->nrOfLayers == 4) + { + dci_pdu_rel15->precoding_information.val = 11; + } + + // antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4 // Antenna Ports - dci_pdu_rel15->antenna_ports.val = 0; // TODO for now it is hardcoded, it should depends on cdm group no data and rank + dci_pdu_rel15->antenna_ports.val = 0; + // DMRS sequence initialization dci_pdu_rel15->dmrs_sequence_initialization.val = pusch_pdu->scid; break; @@ -699,11 +714,14 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, } LOG_D(NR_MAC, - "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n", + "%s() ULDCI type 0 payload: dci_format %d, freq_alloc %d, time_alloc %d, freq_hop_flag %d, precoding_information.val %d antenna_ports.val %d mcs %d tpc %d ndi %d rv %d\n", __func__, + dci_format, dci_pdu_rel15->frequency_domain_assignment.val, dci_pdu_rel15->time_domain_assignment.val, dci_pdu_rel15->frequency_hopping_flag.val, + dci_pdu_rel15->precoding_information.val, + dci_pdu_rel15->antenna_ports.val, dci_pdu_rel15->mcs, dci_pdu_rel15->tpc, dci_pdu_rel15->ndi, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 9a6fa32b5f..269d6c5359 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -905,7 +905,7 @@ bool allocate_ul_retransmission(module_id_t module_id, NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters : &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; int rbStart = 0; // wrt BWP start const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); - + const uint8_t nrOfLayers = 1; const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_bwp || ubwpd) ? 1 : 2; const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; LOG_D(NR_MAC,"retInfo->time_domain_allocation = %d, tda = %d\n", retInfo->time_domain_allocation, tda); @@ -926,8 +926,9 @@ bool allocate_ul_retransmission(module_id_t module_id, if (ps->time_domain_allocation != tda || ps->dci_format != dci_format + || ps->nrOfLayers != nrOfLayers || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) - nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); + nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers,ps); LOG_D(NR_MAC, "%s(): retransmission keeping TDA %d and TBS %d\n", __func__, tda, retInfo->tb_size); } else { /* the retransmission will use a different time domain allocation, check @@ -939,7 +940,7 @@ bool allocate_ul_retransmission(module_id_t module_id, rbSize++; NR_pusch_semi_static_t temp_ps; int dci_format = get_dci_format(sched_ctrl); - nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp,ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, &temp_ps); + nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp,ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers, &temp_ps); uint32_t new_tbs; uint16_t new_rbSize; bool success = nr_find_nb_rb(retInfo->Qm, @@ -1094,7 +1095,7 @@ void pf_ul(module_id_t module_id, if (max_num_ue < 0) return; - LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d\n", min_rb, rbStart); + LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d num_dmrs_cdm_grps_no_data %d\n", min_rb, rbStart, ps->num_dmrs_cdm_grps_no_data); while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++; if (rbStart + min_rb >= bwpSize) { LOG_W(NR_MAC, "cannot allocate continuous UL data for UE %d/RNTI %04x: no resources (rbStart %d, min_rb %d, bwpSize %d\n", @@ -1106,13 +1107,15 @@ void pf_ul(module_id_t module_id, /* we want to avoid a lengthy deduction of DMRS and other parameters in * every TTI if we can save it, so check whether dci_format, TDA, or * num_dmrs_cdm_grps_no_data has changed and only then recompute */ + const uint8_t nrOfLayers = 1; const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_ubwp || ubwpd) ? 1 : 2; int dci_format = get_dci_format(sched_ctrl); const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format + || ps->nrOfLayers != nrOfLayers || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) - nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); + nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data,nrOfLayers, ps); NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; sched_pusch->mcs = 9; update_ul_ue_R_Qm(sched_pusch, ps); @@ -1125,7 +1128,7 @@ void pf_ul(module_id_t module_id, ps->N_PRB_DMRS * ps->num_dmrs_symb, 0, // nb_rb_oh 0, - 1 /* NrOfLayers */) + ps->nrOfLayers) >> 3; /* Mark the corresponding RBs as used */ @@ -1205,13 +1208,15 @@ void pf_ul(module_id_t module_id, /* we want to avoid a lengthy deduction of DMRS and other parameters in * every TTI if we can save it, so check whether dci_format, TDA, or * num_dmrs_cdm_grps_no_data has changed and only then recompute */ + const uint8_t nrOfLayers = 1; const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_ubwp || ubwpd) ? 1 : 2; int dci_format = get_dci_format(sched_ctrl); const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format + || ps->nrOfLayers != nrOfLayers || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) - nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); + nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers,ps); update_ul_ue_R_Qm(sched_pusch, ps); /* Calculate the current scheduling bytes and the necessary RBs */ @@ -1473,7 +1478,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) sched_ctrl->last_ul_slot = sched_pusch->slot; LOG_D(NR_MAC, - "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d startSymbol %2d nb_symbol %2d dmrs_pos %x MCS %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched %6d est BSR %6d TPC %d\n", + "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d startSymbol %2d nb_symbol %2d dmrs_pos %x MCS %2d nrOfLayers %2d num_dmrs_cdm_grps_no_data %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched %6d est BSR %6d TPC %d\n", frame, slot, rnti, @@ -1486,6 +1491,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) ps->nrOfSymbols, ps->ul_dmrs_symb_pos, sched_pusch->mcs, + ps->nrOfLayers, + ps->num_dmrs_cdm_grps_no_data, sched_pusch->tb_size, harq_id, cur_harq->round, @@ -1532,17 +1539,18 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pusch_pdu->qam_mod_order = sched_pusch->Qm; pusch_pdu->mcs_index = sched_pusch->mcs; pusch_pdu->mcs_table = ps->mcs_table; - pusch_pdu->transform_precoding = ps->transform_precoding; + pusch_pdu->transformPrecoder = ps->transformPrecoder; if (ps->pusch_Config && ps->pusch_Config->dataScramblingIdentityPUSCH) pusch_pdu->data_scrambling_id = *ps->pusch_Config->dataScramblingIdentityPUSCH; else pusch_pdu->data_scrambling_id = *scc->physCellId; - pusch_pdu->nrOfLayers = 1; + pusch_pdu->nrOfLayers = ps->nrOfLayers; + pusch_pdu->num_dmrs_cdm_grps_no_data = ps->num_dmrs_cdm_grps_no_data; /* FAPI: DMRS */ pusch_pdu->ul_dmrs_symb_pos = ps->ul_dmrs_symb_pos; pusch_pdu->dmrs_config_type = ps->dmrs_config_type; - if (pusch_pdu->transform_precoding) { // transform precoding disabled + if (pusch_pdu->transformPrecoder) { // transform precoding disabled long *scramblingid=NULL; if (ps->NR_DMRS_UplinkConfig && pusch_pdu->scid == 0) scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0; @@ -1562,7 +1570,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) } pusch_pdu->scid = 0; // DMRS sequence initialization [TS38.211, sec 6.4.1.1.1] pusch_pdu->num_dmrs_cdm_grps_no_data = ps->num_dmrs_cdm_grps_no_data; - pusch_pdu->dmrs_ports = 1; + pusch_pdu->dmrs_ports = ((1<nrOfLayers) - 1); /* FAPI: Pusch Allocation in frequency domain */ pusch_pdu->resource_alloc = 1; //type 1 @@ -1588,7 +1596,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) LOG_D(NR_MAC,"PUSCH PDU : data_scrambling_identity %x, dmrs_scrambling_id %x\n",pusch_pdu->data_scrambling_id,pusch_pdu->ul_dmrs_scrambling_id); /* TRANSFORM PRECODING --------------------------------------------------------*/ - if (pusch_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled){ + if (pusch_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled){ // U as specified in section 6.4.1.1.1.2 in 38.211, if sequence hopping and group hopping are disabled pusch_pdu->dfts_ofdm.low_papr_group_number = pusch_pdu->pusch_identity % 30; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 91221276b0..8a560580c9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -298,6 +298,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, long dci_format, int tda, uint8_t num_dmrs_cdm_grps_no_data, + uint8_t nrOfLayers, NR_pusch_semi_static_t *ps); uint8_t nr_get_tpc(int target, uint8_t cqi, int incr); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 27f42f4b1d..66b10dad42 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -318,13 +318,14 @@ typedef struct NR_sched_pucch { typedef struct NR_pusch_semi_static_t { int dci_format; int time_domain_allocation; + uint8_t nrOfLayers; uint8_t num_dmrs_cdm_grps_no_data; int startSymbolIndex; int nrOfSymbols; NR_PUSCH_Config_t *pusch_Config; - uint8_t transform_precoding; + uint8_t transformPrecoder; uint8_t mcs_table; long mapping_type; diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index ff6923429e..52d03b5e56 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1867,6 +1867,7 @@ int16_t do_RRCReconfiguration( // 1, // 1, // carrier->pdsch_AntennaPorts, + // carrier->pusch_AntennaPorts, // carrier->initial_csi_index[ue_context_p->local_uid + 1], // ue_context_pP->local_uid); diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 9465b9b4b0..6a94450450 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -79,6 +79,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco int scg_id, int servCellIndex, int dl_antenna_ports, + int ul_antenna_ports, int do_csirs, int initial_csi_index, int uid); @@ -90,6 +91,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon NR_RRCReconfiguration_IEs_t *reconfig, NR_CellGroupConfig_t *secondaryCellGroup, int dl_antenna_ports, + int ul_antenna_ports, int do_csirs, int initial_csi_index, int uid); diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index f9fce48f41..22e43cbb6a 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -241,6 +241,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ reconfig_ies, ue_context_p->ue_context.secondaryCellGroup, carrier->pdsch_AntennaPorts, + carrier->pusch_AntennaPorts, carrier->do_CSIRS, carrier->initial_csi_index[ue_context_p->local_uid + 1], ue_context_p->local_uid); @@ -250,6 +251,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ reconfig_ies, ue_context_p->ue_context.secondaryCellGroup, carrier->pdsch_AntennaPorts, + carrier->pusch_AntennaPorts, carrier->do_CSIRS, carrier->initial_csi_index[ue_context_p->local_uid + 1], ue_context_p->local_uid); @@ -348,7 +350,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ rrc_mac_config_req_gNB(rrc->module_id, rrc->carrier.ssb_SubcarrierOffset, rrc->carrier.pdsch_AntennaPorts, - rrc->carrier.pusch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, rrc->carrier.sib1_tda, rrc->carrier.servingcellconfigcommon, &rrc->carrier.mib, diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 980bd39c37..d03f361502 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -55,6 +55,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco int scg_id, int servCellIndex, int dl_antenna_ports, + int ul_antenna_ports, int do_csirs, int initial_csi_index, int uid) { @@ -745,14 +746,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pusch_Config->codebookSubset=calloc(1,sizeof(*pusch_Config->codebookSubset)); *pusch_Config->codebookSubset = NR_PUSCH_Config__codebookSubset_nonCoherent; pusch_Config->maxRank=calloc(1,sizeof(*pusch_Config->maxRank)); - *pusch_Config->maxRank= 1; + *pusch_Config->maxRank= ul_antenna_ports; pusch_Config->rbg_Size=NULL; pusch_Config->uci_OnPUSCH=NULL; pusch_Config->tp_pi2BPSK=NULL; /*------------------------------TRANSFORM PRECODING- -----------------------------------------------------------------------*/ - uint8_t transform_precoding = NR_PUSCH_Config__transformPrecoder_disabled; + uint8_t transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; // TBD: configure this from .conf file, Dedicated params cannot yet be configured in .conf file. // Enable this to test transform precoding enabled from dedicated config. @@ -764,13 +765,13 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (pusch_Config->transformPrecoder == NULL) { if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder != NULL) - transform_precoding = NR_PUSCH_Config__transformPrecoder_enabled; + transformPrecoder = NR_PUSCH_Config__transformPrecoder_enabled; } else - transform_precoding = *pusch_Config->transformPrecoder; + transformPrecoder = *pusch_Config->transformPrecoder; - if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled ) { + if (transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled ) { /* Enable DMRS uplink config for transform precoding enabled */ NR_DMRS_UplinkConfig->transformPrecodingEnabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingEnabled)); NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity = NULL; @@ -1348,6 +1349,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon NR_RRCReconfiguration_IEs_t *reconfig, NR_CellGroupConfig_t *secondaryCellGroup, int dl_antenna_ports, + int ul_antenna_ports, int do_csirs, int initial_csi_index, int uid) { @@ -1363,6 +1365,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon 1, 1, dl_antenna_ports, + ul_antenna_ports, do_csirs, initial_csi_index, uid); diff --git a/targets/ARCH/rfsimulator/simulator.c b/targets/ARCH/rfsimulator/simulator.c index 13945998d7..20faa86591 100644 --- a/targets/ARCH/rfsimulator/simulator.c +++ b/targets/ARCH/rfsimulator/simulator.c @@ -429,7 +429,7 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp if (!alreadyLocked) pthread_mutex_lock(&Sockmutex); - LOG_D(HW,"sending %d samples at time: %ld\n", nsamps, timestamp); + LOG_D(HW,"sending %d samples at time: %ld, nbAnt %d\n", nsamps, timestamp, nbAnt); for (int i=0; ibuf[i]; @@ -623,7 +623,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest } rfsimulator_state_t *t = device->priv; - LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld\n", nsamps, t->nextTimestamp+nsamps); + LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld, nbAnt %d\n", nsamps, t->nextTimestamp+nsamps, nbAnt); // deliver data from received data // check if a UE is connected int first_sock; @@ -728,6 +728,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest for (int a=0; achannel_model != NULL ) // apply a channel model + { rxAddInput( ptr->circularBuf, (struct complex16 *) samplesVoid[a], a, ptr->channel_model, @@ -735,11 +736,21 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest t->nextTimestamp, CirSize ); - else { // no channel modeling + } + else + { // no channel modeling + + #if 0 double H_awgn_mimo[4][4] ={{1.0, 0.5, 0.25, 0.125},//rx 0 {0.5, 1.0, 0.5, 0.25}, //rx 1 {0.25, 0.5, 1.0, 0.5}, //rx 2 {0.125, 0.25, 0.5, 1.0}};//rx 3 + #else + double H_awgn_mimo[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 + {0.0, 1.0, 0.0, 0.0},//rx 1 + {0.0, 0.0, 1.0, 0.0},//rx 2 + {0.0, 0.0, 0.0, 1.0}};//rx 3 + #endif sample_t *out=(sample_t *)samplesVoid[a]; int nbAnt_tx = ptr->th.nbAnt;//number of Tx antennas diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf new file mode 100644 index 0000000000..74f2f9c11c --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf @@ -0,0 +1,320 @@ +Active_gNBs = ( "gNB-Eurecom-5GNRBox"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-Eurecom-5GNRBox"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); + + }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 4; + pusch_AntennaPorts = 4; + sib1_tda = 0; + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 0; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641280; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640008; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + #pdsch-ConfigCommon + #pdschTimeDomainAllocationList (up to 16 entries) + initialDLBWPk0_0 = 0; #for DL slot + initialDLBWPmappingType_0 = 0; #0=typeA,1=typeB + initialDLBWPstartSymbolAndLength_0 = 40; #this is SS=1,L=13 + + initialDLBWPk0_1 = 0; #for mixed slot + initialDLBWPmappingType_1 = 0; + initialDLBWPstartSymbolAndLength_1 = 57; #this is SS=1,L=5 + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + # pusch-ConfigCommon (up to 16 elements) + initialULBWPk2_0 = 6; # used for UL slot + initialULBWPmappingType_0 = 1 + initialULBWPstartSymbolAndLength_0 = 41; # this is SS=0 L=13 + + initialULBWPk2_1 = 6; # used for mixed slot + initialULBWPmappingType_1 = 1; + initialULBWPstartSymbolAndLength_1 = 52; # this is SS=10 L=4 + + initialULBWPk2_2 = 7; # used for Msg.3 during RA + initialULBWPmappingType_2 = 1; + initialULBWPstartSymbolAndLength_2 = 52; # this is SS=10 L=4 + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// MME parameters: + amf_ip_address = ( { ipv4 = "192.168.70.132"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 150; + pucch_TargetSNRx10 = 200; + } +); + +L1s = ( + { + num_cc = 1; + tr_n_preference = "local_mac"; + pusch_proc_threads = 8; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 150; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 + } +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 4 + nb_rx = 4 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + #beamforming 1x4 matrix: + bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff]; + clock_src = "internal"; + } +); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + +rfsimulator : +{ + serveraddr = "server"; + serverport = "4043"; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + + log_config : + { + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + ngap_log_level ="info"; + ngap_log_verbosity ="medium"; + }; + From 2aa8a438f2af6765d042c3024e2208085b806c5c Mon Sep 17 00:00:00 2001 From: Fang Date: Tue, 7 Dec 2021 13:47:15 +0800 Subject: [PATCH 047/233] add mimo doc --- doc/UL_MIMO.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 doc/UL_MIMO.txt diff --git a/doc/UL_MIMO.txt b/doc/UL_MIMO.txt new file mode 100644 index 0000000000..c06bc78c4c --- /dev/null +++ b/doc/UL_MIMO.txt @@ -0,0 +1,31 @@ +# how to build phy sim +cd cmake_targets/ +sudo ./build_oai --phy_simulators -c + +cd phy_simulators/build/ +# ULSCH sim +# bit level validation +# PUSCH encoding and decoding for 4 layers 2 CMD without data +sudo ./nr_ulschsim -R 106 -m9 -s13 -n100 -y4 -z4 -W4 + +# UL sim +# Uplink chain validation +# PUSCH on UE side and gNB side,whole chain for PUSCH +# No channel model on data domain signal +# No cross-path connection +# PMI = 0 is only unitary prcoding matrix +sudo ./nr_ulsim -n100 -m9 -r106 -s13 -W4 -y4 -z4 + +# How to build rfsim +sudo ./build_oai -c --gNB --nrUE -w SIMU + +# How to run with rfsim +# 4x4 RANK 4 +sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf --rfsim --phy-test --l 2 --L 4 +sudo ./nr-uesoftmodem --rfsim --phy-test --ue-nb-ant-rx 4 --ue-nb-ant-tx 4 +# 4x4 RANK 2 +sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf --rfsim --phy-test --l 2 --L 2 +sudo ./nr-uesoftmodem --rfsim --phy-test --ue-nb-ant-rx 4 --ue-nb-ant-tx 4 + + + From 79002c119057c7a37fda5d17e9359bae71eadc4c Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 9 Dec 2021 17:37:16 +0100 Subject: [PATCH 048/233] phy-sim test cases for UL-MIMO --- cmake_targets/autotests/test_case_list.xml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml index 3472b14efc..00fcfc4140 100755 --- a/cmake_targets/autotests/test_case_list.xml +++ b/cmake_targets/autotests/test_case_list.xml @@ -1182,9 +1182,10 @@ execution - nr_ulschsim Test cases. (Test1: 106 PRB), - (Test2: 217 PRB), - (Test3: 273 PRB) + nr_ulschsim Test cases. (Test1: 106 PRBs), + (Test2: 217 PRBs), + (Test3: 273 PRBs) + (Test4: 106 PRBs 4-layer MIMO) $OPENAIR_DIR/cmake_targets/build_oai --phy_simulators -c @@ -1193,8 +1194,9 @@ $OPENAIR_DIR/targets/bin/nr_ulschsim.Rel15 -R 106 -m9 -s13 -n100 -R 217 -m15 -s15 -n100 - -R 273 -m19 -s20 -n100 - nr_ulschsim.test1 nr_ulschsim.test2 nr_ulschsim.test3 + -R 273 -m19 -s20 -n100 + -R 106 -m9 -s13 -n100 -y4 -z4 -W4 + nr_ulschsim.test1 nr_ulschsim.test2 nr_ulschsim.test3 nr_ulschsim.test4 PUSCH test OK segmentation fault|assertion|exiting|fatal 3 @@ -1302,7 +1304,9 @@ (Test12: SC-FDMA, 216 PRBs), (Test13: SC-FDMA, 273 PRBs), (Test14: SC-FDMA, 3 DMRS), - (Test15: MCS 16 50 PRBs 2 RX_Antenna) + (Test15: MCS 16 50 PRBs 2 RX_Antenna) + (Test16: MCS 9 106 PRBs MIMO 2 layers) + (Test17: MCS 9 106 PRBs MIMO 4 layers) $OPENAIR_DIR/cmake_targets/build_oai --phy_simulators -c @@ -1323,9 +1327,11 @@ -n50 -s5 -Z -r216 -R217 -n50 -s5 -Z -r270 -R273 -n100 -s5 -Z -U 2 0 2 - -n100 -m16 -s10 -z2 + -n100 -m16 -s10 -z2 + -n100 -m9 -r106 -s10 -W2 -y2 -z2 + -n100 -m9 -r106 -s13 -W4 -y4 -z4 - nr_ulsim.test1 nr_ulsim.test2 nr_ulsim.test3 nr_ulsim.test4 nr_ulsim.test5 nr_ulsim.test6 nr_ulsim.test7 nr_ulsim.test8 nr_ulsim.test9 nr_ulsim.test10 nr_ulsim.test11 nr_ulsim.test12 nr_ulsim.test13 nr_ulsim.test14 nr_ulsim.test15 + nr_ulsim.test1 nr_ulsim.test2 nr_ulsim.test3 nr_ulsim.test4 nr_ulsim.test5 nr_ulsim.test6 nr_ulsim.test7 nr_ulsim.test8 nr_ulsim.test9 nr_ulsim.test10 nr_ulsim.test11 nr_ulsim.test12 nr_ulsim.test13 nr_ulsim.test14 nr_ulsim.test15 nr_ulsim.test16 nr_ulsim.test16 PUSCH test OK segmentation fault|assertion|exiting|fatal 3 From dbdf38133e3f99320f01ac54a3e398802981f35e Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 9 Dec 2021 17:37:55 +0100 Subject: [PATCH 049/233] reducing log levels --- diff_1206_2.txt | 4257 ----------------- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 6 +- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 4 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 14 +- openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 4 +- 5 files changed, 14 insertions(+), 4271 deletions(-) delete mode 100644 diff_1206_2.txt diff --git a/diff_1206_2.txt b/diff_1206_2.txt deleted file mode 100644 index ff352f4157..0000000000 --- a/diff_1206_2.txt +++ /dev/null @@ -1,4257 +0,0 @@ -diff --git a/executables/nr-softmodem-common.h b/executables/nr-softmodem-common.h -index c02155326b..1238b43c27 100644 ---- a/executables/nr-softmodem-common.h -+++ b/executables/nr-softmodem-common.h -@@ -67,6 +67,7 @@ - #define CONFIG_HLP_ITTIL "Generate ITTI analyzser logs (similar to wireshark logs but with more details)\n" - #define CONFIG_HLP_DLMCS_PHYTEST "Set the downlink MCS for PHYTEST mode\n" - #define CONFIG_HLP_DLNL_PHYTEST "Set the downlink nrOfLayers for PHYTEST mode\n" -+#define CONFIG_HLP_ULNL_PHYTEST "Set the uplink nrOfLayers for PHYTEST mode\n" - #define CONFIG_HLP_STMON "Enable processing timing measurement of lte softmodem on per subframe basis \n" - #define CONFIG_HLP_MSLOTS "Skip the missed slots/subframes \n" - #define CONFIG_HLP_ULMCS_PHYTEST "Set the uplink MCS for PHYTEST mode\n" -diff --git a/executables/nr-softmodem.h b/executables/nr-softmodem.h -index 896c363cbf..a11572c0e1 100644 ---- a/executables/nr-softmodem.h -+++ b/executables/nr-softmodem.h -@@ -23,6 +23,7 @@ - {"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, i8ptr:&threequarter_fs, defintval:0, TYPE_INT8, 0}, \ - {"m" , CONFIG_HLP_DLMCS_PHYTEST,0, uptr:&target_dl_mcs, defintval:0, TYPE_UINT, 0}, \ - {"l" , CONFIG_HLP_DLNL_PHYTEST,0, uptr:&target_dl_Nl, defintval:0, TYPE_UINT, 0}, \ -+ {"L" , CONFIG_HLP_ULNL_PHYTEST,0, uptr:&target_ul_Nl, defintval:0, TYPE_UINT, 0}, \ - {"t" , CONFIG_HLP_ULMCS_PHYTEST,0, uptr:&target_ul_mcs, defintval:0, TYPE_UINT, 0}, \ - {"M" , CONFIG_HLP_DLBW_PHYTEST,0, uptr:&target_dl_bw, defintval:0, TYPE_UINT, 0}, \ - {"T" , CONFIG_HLP_ULBW_PHYTEST,0, uptr:&target_ul_bw, defintval:0, TYPE_UINT, 0}, \ -@@ -36,6 +37,7 @@ - extern threads_t threads; - extern uint32_t target_dl_mcs; - extern uint32_t target_dl_Nl; -+extern uint32_t target_ul_Nl; - extern uint32_t target_ul_mcs; - extern uint32_t target_dl_bw; - extern uint32_t target_ul_bw; -diff --git a/executables/nr-ue.c b/executables/nr-ue.c -index 60ff40abeb..a23f91b1dd 100644 ---- a/executables/nr-ue.c -+++ b/executables/nr-ue.c -@@ -278,7 +278,7 @@ void processSlotTX(void *arg) { - int tx_slot_type = nr_ue_slot_select(cfg, proc->frame_tx, proc->nr_slot_tx); - uint8_t gNB_id = 0; - -- LOG_D(PHY,"%d.%d => slot type %d\n",proc->frame_tx,proc->nr_slot_tx,tx_slot_type); -+ LOG_D(PHY,"processSlotTX %d.%d => slot type %d\n",proc->frame_tx,proc->nr_slot_tx,tx_slot_type); - if (tx_slot_type == NR_UPLINK_SLOT || tx_slot_type == NR_MIXED_SLOT){ - - // trigger L2 to run ue_scheduler thru IF module -@@ -372,7 +372,8 @@ void processSlotRX(void *arg) { - LOG_D(PHY, "Sending Uplink data \n"); - nr_ue_pusch_common_procedures(UE, - proc->nr_slot_tx, -- &UE->frame_parms,1); -+ &UE->frame_parms, -+ UE->frame_parms.nb_antennas_tx); - } - - if (UE->UE_mode[gNB_id] > NOT_SYNCHED && UE->UE_mode[gNB_id] < PUSCH) { -diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c -index ad5ea982c9..57fd284601 100644 ---- a/executables/nr-uesoftmodem.c -+++ b/executables/nr-uesoftmodem.c -@@ -465,8 +465,11 @@ int main( int argc, char **argv ) { - memset(UE[CC_id],0,sizeof(PHY_VARS_NR_UE)); - - set_options(CC_id, UE[CC_id]); -- NR_UE_MAC_INST_t *mac = get_mac_inst(0); -- -+ NR_DL_FRAME_PARMS *fp = &UE[CC_id]->frame_parms; -+ NR_UE_MAC_INST_t *mac = get_mac_inst(0); -+ -+ mac->phy_config.config_req.carrier_config.num_tx_ant = fp->nb_antennas_tx; -+ - if (get_softmodem_params()->sa) { - uint16_t nr_band = get_band(downlink_frequency[CC_id][0],uplink_frequency_offset[CC_id][0]); - mac->nr_band = nr_band; -diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h -index 0e923931ad..8b93417cb0 100644 ---- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h -+++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h -@@ -298,9 +298,10 @@ typedef struct - uint8_t qam_mod_order; - uint8_t mcs_index; - uint8_t mcs_table; -- uint8_t transform_precoding; -+ uint8_t transformPrecoder; - uint16_t data_scrambling_id; - uint8_t nrOfLayers; -+ uint8_t Tpmi; - //DMRS - uint16_t ul_dmrs_symb_pos; - uint8_t dmrs_config_type; -diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h -index 9d245446c8..58ff9037ce 100644 ---- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h -+++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h -@@ -1208,9 +1208,10 @@ typedef struct - uint8_t qam_mod_order; - uint8_t mcs_index; - uint8_t mcs_table; -- uint8_t transform_precoding; -+ uint8_t transformPrecoder; - uint16_t data_scrambling_id; - uint8_t nrOfLayers; -+ uint8_t Tpmi; - //DMRS - uint16_t ul_dmrs_symb_pos; - uint8_t dmrs_config_type; -diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c -index 7cb74f8562..473b0900bc 100644 ---- a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c -+++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c -@@ -941,9 +941,10 @@ static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, ui - push8(pusch_pdu->qam_mod_order, ppWritePackedMsg, end) && - push8(pusch_pdu->mcs_index, ppWritePackedMsg, end) && - push8(pusch_pdu->mcs_table, ppWritePackedMsg, end) && -- push8(pusch_pdu->transform_precoding, ppWritePackedMsg, end) && -+ push8(pusch_pdu->transformPrecoder, ppWritePackedMsg, end) && - push16(pusch_pdu->data_scrambling_id, ppWritePackedMsg, end) && - push8(pusch_pdu->nrOfLayers, ppWritePackedMsg, end) && -+ push8(pusch_pdu->Tpmi, ppWritePackedMsg, end) && - push16(pusch_pdu->ul_dmrs_symb_pos, ppWritePackedMsg, end) && - push8(pusch_pdu->dmrs_config_type, ppWritePackedMsg, end) && - push16(pusch_pdu->ul_dmrs_scrambling_id, ppWritePackedMsg, end) && -@@ -4445,9 +4446,10 @@ static uint8_t unpack_ul_tti_request_pusch_pdu(void *tlv, uint8_t **ppReadPacked - pull8(ppReadPackedMsg, &pusch_pdu->qam_mod_order, end) && - pull8(ppReadPackedMsg, &pusch_pdu->mcs_index, end) && - pull8(ppReadPackedMsg, &pusch_pdu->mcs_table, end) && -- pull8(ppReadPackedMsg, &pusch_pdu->transform_precoding, end) && -+ pull8(ppReadPackedMsg, &pusch_pdu->transformPrecoder, end) && - pull16(ppReadPackedMsg, &pusch_pdu->data_scrambling_id, end) && - pull8(ppReadPackedMsg, &pusch_pdu->nrOfLayers, end) && -+ pull8(ppReadPackedMsg, &pusch_pdu->Tpmi, end) && - pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_symb_pos, end) && - pull8(ppReadPackedMsg, &pusch_pdu->dmrs_config_type, end) && - pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_scrambling_id, end) && -diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c -index cd4848ea8c..2ac3230215 100644 ---- a/openair1/PHY/INIT/nr_init.c -+++ b/openair1/PHY/INIT/nr_init.c -@@ -276,11 +276,21 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, - pusch_vars[ULSCH_id]->ul_ch_magb0 = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); - pusch_vars[ULSCH_id]->ul_ch_mag = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); - pusch_vars[ULSCH_id]->ul_ch_magb = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); -- pusch_vars[ULSCH_id]->rho = (int32_t **)malloc16_clear(n_buf*sizeof(int32_t*) ); -+ pusch_vars[ULSCH_id]->rho = (int32_t ***)malloc16_clear(Prx*sizeof(int32_t**) ); -+ pusch_vars[ULSCH_id]->llr_layers = (int16_t **)malloc16(max_ul_mimo_layers*sizeof(int32_t *) ); - - for (i=0; irxdataF_ext[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); - pusch_vars[ULSCH_id]->rxdataF_ext2[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); -+ pusch_vars[ULSCH_id]->rho[i] = (int32_t **)malloc16_clear( NR_MAX_NB_LAYERS*NR_MAX_NB_LAYERS*sizeof(int32_t*)); -+ -+ for (int j=0; j< max_ul_mimo_layers; j++) -+ { -+ for (int k=0; krho[i][j*max_ul_mimo_layers+k]=(int32_t *)malloc16_clear( sizeof(int32_t) * fp->N_RB_UL*12*7*2 ); -+ } -+ } - } - for (i=0; iul_ch_estimates[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*fp->ofdm_symbol_size*2*fp->symbols_per_slot ); -@@ -293,8 +303,12 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, - pusch_vars[ULSCH_id]->ul_ch_mag0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); - pusch_vars[ULSCH_id]->ul_ch_magb0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); - pusch_vars[ULSCH_id]->ul_ch_mag[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); -- pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); -- pusch_vars[ULSCH_id]->rho[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*(fp->N_RB_UL*12*7*2) ); -+ pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); -+ } -+ -+ for (i=0; i< max_ul_mimo_layers; i++) -+ { -+ pusch_vars[ULSCH_id]->llr_layers[i] = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear - } - pusch_vars[ULSCH_id]->llr = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear - pusch_vars[ULSCH_id]->ul_valid_re_per_slot = (int16_t *)malloc16_clear( sizeof(int16_t)*fp->symbols_per_slot); -diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c -index 4024ed1617..169db48e69 100644 ---- a/openair1/PHY/INIT/nr_init_ru.c -+++ b/openair1/PHY/INIT/nr_init_ru.c -@@ -41,7 +41,7 @@ int nr_phy_init_RU(RU_t *ru) { - int p; - int re; - -- LOG_I(PHY,"Initializing RU signal buffers (if_south %s) nb_tx %d\n",ru_if_types[ru->if_south],ru->nb_tx); -+ LOG_I(PHY,"Initializing RU signal buffers (if_south %s) nb_tx %d, nb_rx %d\n",ru_if_types[ru->if_south],ru->nb_tx, ru->nb_rx); - - nfapi_nr_config_request_scf_t *cfg; - ru->nb_log_antennas=0; -diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c -index b60e77e814..11fb6e7d61 100644 ---- a/openair1/PHY/INIT/nr_init_ue.c -+++ b/openair1/PHY/INIT/nr_init_ue.c -@@ -124,7 +124,8 @@ void phy_init_nr_ue_PUSCH(NR_UE_PUSCH *const pusch, - AssertFatal( pusch, "pusch==0" ); - - for (int i=0; itxdataF_layers[i] = (int32_t *)malloc16_clear(NR_MAX_PUSCH_ENCODED_LENGTH*sizeof(int32_t)); -+ pusch->txdataF_layers[i] = (int32_t *)malloc16_clear((NR_MAX_PUSCH_ENCODED_LENGTH)*sizeof(int32_t *)); -+ pusch->txdataF_precoding[i] = (int32_t *)malloc16_clear((NR_MAX_PUSCH_ENCODED_LENGTH)*sizeof(int32_t *)); - } - } - -@@ -143,7 +144,7 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, - uint16_t N_n_scid[2] = {0,1}; // [HOTFIX] This is a temporary implementation of scramblingID0 and scramblingID1 which are given by DMRS-UplinkConfig - int n_scid; - abstraction_flag = 0; -- LOG_I(PHY, "Initializing UE vars (abstraction %u) for gNB TXant %u, UE RXant %u\n", abstraction_flag, fp->nb_antennas_tx, fp->nb_antennas_rx); -+ LOG_I(PHY, "Initializing UE vars (abstraction %u) for UE TXant %u, UE RXant %u\n", abstraction_flag, fp->nb_antennas_tx, fp->nb_antennas_rx); - //LOG_D(PHY,"[MSC_NEW][FRAME 00000][PHY_UE][MOD %02u][]\n", ue->Mod_id+NB_gNB_INST); - phy_init_nr_top(ue); - // many memory allocation sizes are hard coded -diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c -index 1f164748bf..9a432e8615 100644 ---- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c -+++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c -@@ -110,7 +110,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, - fr = filt8_r1; - fmm = filt8_mm1; - fml = filt8_ml1; -- fmr = filt8_m1; -+ fmr = filt8_mm1; - fdcl = filt8_dcl1; - fdcr = filt8_dcr1; - fdclh = filt8_dcl1_h; -@@ -129,9 +129,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, - - //------------------generate DMRS------------------// - -- // transform precoding = 1 means disabled -- if (pusch_pdu->transform_precoding == 1) { -- nr_pusch_dmrs_rx(gNB, Ns, gNB->nr_gold_pusch_dmrs[pusch_pdu->scid][Ns][symbol], &pilot[0], 1000, 0, nb_rb_pusch, -+ if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { -+ nr_pusch_dmrs_rx(gNB, Ns, gNB->nr_gold_pusch_dmrs[pusch_pdu->scid][Ns][symbol], &pilot[0], (1000+p), 0, nb_rb_pusch, - (pusch_pdu->bwp_start + pusch_pdu->rb_start)*NR_NB_SC_PER_RB, pusch_pdu->dmrs_config_type); - } - else { // if transform precoding or SC-FDMA is enabled in Uplink -@@ -291,7 +290,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, - printf("pilot %u : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); - printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); - #endif -- multadd_real_vector_complex_scalar(fml, -+ multadd_real_vector_complex_scalar(fm, - ch, - ul_ch, - 8); -diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h -index 4fed3a468f..87e4d57362 100644 ---- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h -+++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h -@@ -137,21 +137,22 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - @param nb_rb_pusch The number of RBs allocated (used for Resource Allocation Type 1 in NR) - @param frame_parms, Pointer to frame descriptor structure - */ --void nr_ulsch_extract_rbs_single(int32_t **rxdataF, -- NR_gNB_PUSCH *pusch_vars, -- int slot, -- unsigned char symbol, -- uint8_t is_dmrs_symbol, -- nfapi_nr_pusch_pdu_t *pusch_pdu, -- NR_DL_FRAME_PARMS *frame_parms); -+void nr_ulsch_extract_rbs(int32_t **rxdataF, -+ NR_gNB_PUSCH *pusch_vars, -+ int slot, -+ unsigned char symbol, -+ uint8_t is_dmrs_symbol, -+ nfapi_nr_pusch_pdu_t *pusch_pdu, -+ NR_DL_FRAME_PARMS *frame_parms); - - void nr_ulsch_scale_channel(int32_t **ul_ch_estimates_ext, - NR_DL_FRAME_PARMS *frame_parms, - NR_gNB_ULSCH_t **ulsch_gNB, -- uint8_t symbol, -- uint8_t start_symbol, -- uint16_t nb_rb, -- pusch_dmrs_type_t pusch_dmrs_type); -+ uint8_t symbol, -+ uint8_t is_dmrs_symbol, -+ uint32_t len, -+ uint8_t nrOfLayers, -+ uint16_t nb_rb); - - /** \brief This function computes the average channel level over all allocated RBs and antennas (TX/RX) in order to compute output shift for compensated signal - @param ul_ch_estimates_ext Channel estimates in allocated RBs -@@ -185,9 +186,10 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, - int **ul_ch_mag, - int **ul_ch_magb, - int **rxdataF_comp, -- int **rho, -+ int ***rho, - NR_DL_FRAME_PARMS *frame_parms, - unsigned char symbol, -+ int length, - uint8_t is_dmrs_symbol, - unsigned char mod_order, - uint8_t nrOfLayers, -@@ -255,8 +257,8 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, - @param mod_order modulation order - */ - void nr_ulsch_compute_llr(int32_t *rxdataF_comp, -- int32_t **ul_ch_mag, -- int32_t **ul_ch_magb, -+ int32_t *ul_ch_mag, -+ int32_t *ul_ch_magb, - int16_t *ulsch_llr, - uint32_t nb_rb, - uint32_t nb_re, -diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c -index 4278338dbc..3ea19a9970 100644 ---- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c -+++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c -@@ -137,6 +137,38 @@ void nr_ulsch_unscrambling_optim(int16_t* llr, - #endif - } - -+void nr_ulsch_layer_demapping(int16_t *llr_cw, -+ uint8_t Nl, -+ uint8_t mod_order, -+ uint32_t length, -+ int16_t **llr_layers) -+{ -+ -+ switch (Nl) { -+ -+ case 1: -+ memcpy((void*)llr_cw, (void*)llr_layers[0], (length)*sizeof(int16_t)); -+ break; -+ -+ case 2: -+ case 3: -+ case 4: -+ for (int i=0; i<(length/Nl/mod_order); i++) -+ { -+ for (int l=0; lnumber_of_nr_ulsch_max;i++) { -diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h -index 80c10d74b1..0e660351ae 100644 ---- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h -+++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h -@@ -82,6 +82,12 @@ void nr_ulsch_unscrambling_optim(int16_t* llr, - uint32_t Nid, - uint32_t n_RNTI); - -+void nr_ulsch_layer_demapping(int16_t *llr_cw, -+ uint8_t Nl, -+ uint8_t mod_order, -+ uint32_t length, -+ int16_t **llr_layers); -+ - void nr_ulsch_procedures(PHY_VARS_gNB *gNB, - int frame_rx, - int slot_rx, -diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c -index 28dd5ab219..8314c83ad9 100644 ---- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c -+++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c -@@ -302,26 +302,27 @@ void nr_idft(int32_t *z, uint32_t Msc_PUSCH) - } - - --void nr_ulsch_extract_rbs_single(int32_t **rxdataF, -- NR_gNB_PUSCH *pusch_vars, -- int slot, -- unsigned char symbol, -- uint8_t is_dmrs_symbol, -- nfapi_nr_pusch_pdu_t *pusch_pdu, -- NR_DL_FRAME_PARMS *frame_parms) -+void nr_ulsch_extract_rbs(int32_t **rxdataF, -+ NR_gNB_PUSCH *pusch_vars, -+ int slot, -+ unsigned char symbol, -+ uint8_t is_dmrs_symbol, -+ nfapi_nr_pusch_pdu_t *pusch_pdu, -+ NR_DL_FRAME_PARMS *frame_parms) - { - - unsigned short start_re, re, nb_re_pusch; -- unsigned char aarx; -+ unsigned char aarx, aatx; - uint32_t rxF_ext_index = 0; - uint32_t ul_ch0_ext_index = 0; - uint32_t ul_ch0_index = 0; -- uint8_t k_prime; -- uint16_t n; -+ //uint8_t k_prime; -+ //uint16_t n; - int16_t *rxF,*rxF_ext; - int *ul_ch0,*ul_ch0_ext; -- uint8_t delta = 0; -+ //uint8_t delta = 0; - int soffset = (slot&3)*frame_parms->symbols_per_slot*frame_parms->ofdm_symbol_size; -+ - #ifdef DEBUG_RB_EXT - - printf("--------------------symbol = %d-----------------------\n", symbol); -@@ -329,7 +330,7 @@ void nr_ulsch_extract_rbs_single(int32_t **rxdataF, - - #endif - -- uint8_t is_dmrs_re; -+ uint8_t is_data_re; - start_re = (frame_parms->first_carrier_offset + (pusch_pdu->rb_start + pusch_pdu->bwp_start) * NR_NB_SC_PER_RB)%frame_parms->ofdm_symbol_size; - nb_re_pusch = NR_NB_SC_PER_RB * pusch_pdu->rb_size; - #ifdef __AVX2__ -@@ -338,64 +339,95 @@ void nr_ulsch_extract_rbs_single(int32_t **rxdataF, - int nb_re_pusch2 = nb_re_pusch; - #endif - -- for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { -+ for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) -+ { - - rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol * frame_parms->ofdm_symbol_size)]; - rxF_ext = (int16_t *)&pusch_vars->rxdataF_ext[aarx][symbol * nb_re_pusch2]; // [hna] rxdataF_ext isn't contiguous in order to solve an alignment problem ib llr computation in case of mod_order = 4, 6 -- -- ul_ch0 = &pusch_vars->ul_ch_estimates[aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available -- -- ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aarx][symbol*nb_re_pusch2]; -- -- n = 0; -- k_prime = 0; -- rxF_ext_index = 0; -- ul_ch0_ext_index = 0; -- ul_ch0_index = 0; -- -- if (is_dmrs_symbol == 0) { -+ -+ if (is_dmrs_symbol == 0) -+ { - // - //rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); -- if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) { -+ if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) -+ { - memcpy1((void*)rxF_ext, - (void*)&rxF[start_re*2], - nb_re_pusch*sizeof(int32_t)); -- } else { -- int neg_length = frame_parms->ofdm_symbol_size-start_re; -- int pos_length = nb_re_pusch-neg_length; -+ } -+ else -+ { -+ int neg_length = frame_parms->ofdm_symbol_size-start_re; -+ int pos_length = nb_re_pusch-neg_length; -+ memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); -+ memcpy1((void*)&rxF_ext[2*neg_length],(void*)rxF,pos_length*sizeof(int32_t)); -+ } - -- memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); -- memcpy1((void*)&rxF_ext[2*neg_length],(void*)rxF,pos_length*sizeof(int32_t)); -+ #ifdef SUPPORT_PMI_MATRIC -+ for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) -+ #else -+ aatx = aarx; -+ #endif -+ { -+ ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available -+ ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; -+ -+ memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); - } -- memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); - } -- else { -- for (re = 0; re < nb_re_pusch; re++) { -- -- is_dmrs_re = (re == get_dmrs_freq_idx_ul(n, k_prime, delta, pusch_pdu->dmrs_config_type)); -- --#ifdef DEBUG_RB_EXT -- printf("re = %d, kprime %d, n %d, is_dmrs_symbol = %d, symbol = %d\n", re, k_prime, n, is_dmrs_symbol, symbol); --#endif -- -- /* save only data and respective channel estimates */ -- if (is_dmrs_re == 0) { -- rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); -- rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); -- ul_ch0_ext[ul_ch0_ext_index] = ul_ch0[ul_ch0_index]; -+ else -+ { -+ #ifdef SUPPORT_PMI_MATRIC -+ for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) -+ #else -+ aatx = aarx; -+ #endif -+ { -+ ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available -+ ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; -+ -+ //n = 0; -+ //k_prime = 0; -+ rxF_ext_index = 0; -+ ul_ch0_ext_index = 0; -+ ul_ch0_index = 0; -+ -+ for (re = 0; re < nb_re_pusch; re++) -+ { -+ uint16_t k= start_re+re; -+ -+ is_data_re = allowed_xlsch_re_in_dmrs_symbol(k, start_re, frame_parms->ofdm_symbol_size, pusch_pdu->num_dmrs_cdm_grps_no_data, pusch_pdu->dmrs_config_type); -+ -+ if (++k >= frame_parms->ofdm_symbol_size) -+ { -+ k -= frame_parms->ofdm_symbol_size; -+ } - --#ifdef DEBUG_RB_EXT -- printf("dmrs symb %d: rxF_ext[%d] = (%d,%d), ul_ch0_ext[%d] = (%d,%d)\n", -+ #ifdef DEBUG_RB_EXT -+ printf("re = %d, kprime %d, n %d, is_dmrs_symbol = %d, symbol = %d\n", re, k_prime, n, is_dmrs_symbol, symbol); -+ #endif -+ -+ /* save only data and respective channel estimates */ -+ if (is_data_re == 1) -+ { -+ if (aatx == aarx) -+ { -+ rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); -+ rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); -+ rxF_ext_index +=2; -+ } -+ -+ ul_ch0_ext[ul_ch0_ext_index] = ul_ch0[ul_ch0_index]; -+ ul_ch0_ext_index++; -+ -+ #ifdef DEBUG_RB_EXT -+ printf("dmrs symb %d: rxF_ext[%d] = (%d,%d), ul_ch0_ext[%d] = (%d,%d)\n", - is_dmrs_symbol,rxF_ext_index>>1, rxF_ext[rxF_ext_index],rxF_ext[rxF_ext_index+1], - ul_ch0_ext_index, ((int16_t*)&ul_ch0_ext[ul_ch0_ext_index])[0], ((int16_t*)&ul_ch0_ext[ul_ch0_ext_index])[1]); --#endif -- ul_ch0_ext_index++; -- rxF_ext_index +=2; -- } else { -- n += k_prime; -- k_prime ^= 1; -+ #endif -+ } -+ ul_ch0_index++; - } -- ul_ch0_index++; - } - } - } -@@ -406,16 +438,19 @@ void nr_ulsch_scale_channel(int **ul_ch_estimates_ext, - NR_gNB_ULSCH_t **ulsch_gNB, - uint8_t symbol, - uint8_t is_dmrs_symbol, -- unsigned short nb_rb, -- pusch_dmrs_type_t pusch_dmrs_type) -+ uint32_t len, -+ uint8_t nrOfLayers, -+ unsigned short nb_rb) - { - - #if defined(__x86_64__)||defined(__i386__) - - short rb, ch_amp; -- unsigned char aarx; -+ unsigned char aarx,aatx; - __m128i *ul_ch128, ch_amp128; - -+ uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); -+ - // Determine scaling amplitude based the symbol - - ch_amp = 1024*8; //((pilots) ? (ulsch_gNB[0]->sqrt_rho_b) : (ulsch_gNB[0]->sqrt_rho_a)); -@@ -431,35 +466,26 @@ void nr_ulsch_scale_channel(int **ul_ch_estimates_ext, - int off = 0; - #endif - -- for (aarx=0; aarx < frame_parms->nb_antennas_rx; aarx++) { -- -- ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aarx][symbol*(off+(nb_rb*NR_NB_SC_PER_RB))]; -- -- if (is_dmrs_symbol==1){ -- if (pusch_dmrs_type == pusch_dmrs_type1) -- nb_rb = nb_rb>>1; -- else -- nb_rb = (2*nb_rb)/3; -- } -- -- for (rb=0;rbnb_antennas_rx; aarx++) -+ { -+ ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*NR_NB_SC_PER_RB))]; - -+ for (rb=0;rb < nb_rb_0;rb++) -+ { - ul_ch128[0] = _mm_mulhi_epi16(ul_ch128[0], ch_amp128); - ul_ch128[0] = _mm_slli_epi16(ul_ch128[0], 3); - - ul_ch128[1] = _mm_mulhi_epi16(ul_ch128[1], ch_amp128); - ul_ch128[1] = _mm_slli_epi16(ul_ch128[1], 3); - -- if (is_dmrs_symbol) { -- ul_ch128+=2; -- } else { -- ul_ch128[2] = _mm_mulhi_epi16(ul_ch128[2], ch_amp128); -- ul_ch128[2] = _mm_slli_epi16(ul_ch128[2], 3); -- ul_ch128+=3; -- -- } -+ ul_ch128[2] = _mm_mulhi_epi16(ul_ch128[2], ch_amp128); -+ ul_ch128[2] = _mm_slli_epi16(ul_ch128[2], 3); -+ ul_ch128+=3; - } -- } -+ } -+} - #endif - } - -@@ -481,6 +507,8 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, - - int16_t x = factor2(len); - int16_t y = (len)>>x; -+ -+ uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); - - #ifdef __AVX2__ - int off = ((nb_rb&1) == 1)? 4:0; -@@ -489,13 +517,16 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, - #endif - - for (aatx = 0; aatx < nrOfLayers; aatx++) -- for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { -+ { -+ for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) -+ { - //clear average level - avg128U = _mm_setzero_si128(); - - ul_ch128=(__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - -- for (rb = 0; rb < len/12; rb++) { -+ for (rb = 0; rb < nb_rb_0; rb++) -+ { - avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[0], ul_ch128[0]), x)); - avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[1], ul_ch128[1]), x)); - avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[2], ul_ch128[2]), x)); -@@ -508,7 +539,8 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, - ((int32_t*)&avg128U)[3]) / y; - - } -- -+ } -+ - _mm_empty(); - _m_empty(); - -@@ -520,7 +552,7 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, - int16x4_t *ul_ch128; - - symbol_mod = (symbol>=(7-frame_parms->Ncp)) ? symbol-(7-frame_parms->Ncp) : symbol; -- -+ uint32_t nb_rb_0 = len/12 + ((len%12)?1:0); - for (aatx=0; aatxnb_antennas_rx; aarx++) { - //clear average level -@@ -529,7 +561,7 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, - - ul_ch128 = (int16x4_t *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*frame_parms->N_RB_UL*12]; - -- for (rb = 0; rb < nb_rb; rb++) { -+ for (rb = 0; rb < nb_rb_0; rb++) { - // printf("rb %d : ",rb); - // print_shorts("ch",&ul_ch128[0]); - avg128U = vqaddq_s32(avg128U, vmull_s16(ul_ch128[0], ul_ch128[0])); -@@ -568,14 +600,20 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, - #endif - } - -+ -+ -+//============================================================================================== -+// Pre-processing for LLR computation -+//============================================================================================== - void nr_ulsch_channel_compensation(int **rxdataF_ext, - int **ul_ch_estimates_ext, - int **ul_ch_mag, - int **ul_ch_magb, - int **rxdataF_comp, -- int **rho, -+ int ***rho, - NR_DL_FRAME_PARMS *frame_parms, - unsigned char symbol, -+ int length, - uint8_t is_dmrs_symbol, - unsigned char mod_order, - uint8_t nrOfLayers, -@@ -637,20 +675,24 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, - __m128i mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3,QAM_amp128={0},QAM_amp128b={0}; - QAM_amp128b = _mm_setzero_si128(); - -- for (aatx=0; aatxnb_antennas_rx; aarx++) { -- -+ for (aarx=0; aarxnb_antennas_rx; aarx++) -+ { - ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - ul_ch_mag128 = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - ul_ch_mag128b = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; -@@ -658,8 +700,10 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, - rxdataF_comp128 = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - - -- for (rb=0; rb2) { -+ for (rb=0; rb2) -+ { - // get channel amplitude if not QPSK - - //print_shorts("ch:",(int16_t*)&ul_ch128[0]); -@@ -679,6 +723,8 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, - ul_ch_mag128[0] = _mm_mulhi_epi16(ul_ch_mag128[0],QAM_amp128); - ul_ch_mag128[0] = _mm_slli_epi16(ul_ch_mag128[0],1); - -+ ul_ch_mag128b[0] = _mm_mulhi_epi16(ul_ch_mag128b[0],QAM_amp128b); -+ ul_ch_mag128b[0] = _mm_slli_epi16(ul_ch_mag128b[0],1); - // print_ints("ch: = ",(int32_t*)&mmtmpD0); - // print_shorts("QAM_amp:",(int16_t*)&QAM_amp128); - // print_shorts("mag:",(int16_t*)&ul_ch_mag128[0]); -@@ -687,30 +733,24 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, - ul_ch_mag128b[1] = ul_ch_mag128[1]; - ul_ch_mag128[1] = _mm_mulhi_epi16(ul_ch_mag128[1],QAM_amp128); - ul_ch_mag128[1] = _mm_slli_epi16(ul_ch_mag128[1],1); -+ -+ ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128); -+ ul_ch_mag128b[1] = _mm_slli_epi16(ul_ch_mag128b[1],1); - -- if (is_dmrs_symbol==0) { -- mmtmpD0 = _mm_madd_epi16(ul_ch128[2],ul_ch128[2]); -- mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); -- mmtmpD1 = _mm_packs_epi32(mmtmpD0,mmtmpD0); -- -- ul_ch_mag128[2] = _mm_unpacklo_epi16(mmtmpD1,mmtmpD1); -- ul_ch_mag128b[2] = ul_ch_mag128[2]; -+ mmtmpD0 = _mm_madd_epi16(ul_ch128[2],ul_ch128[2]); -+ mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); -+ mmtmpD1 = _mm_packs_epi32(mmtmpD0,mmtmpD0); - -- ul_ch_mag128[2] = _mm_mulhi_epi16(ul_ch_mag128[2],QAM_amp128); -- ul_ch_mag128[2] = _mm_slli_epi16(ul_ch_mag128[2],1); -- } -+ ul_ch_mag128[2] = _mm_unpacklo_epi16(mmtmpD1,mmtmpD1); -+ ul_ch_mag128b[2] = ul_ch_mag128[2]; - -- ul_ch_mag128b[0] = _mm_mulhi_epi16(ul_ch_mag128b[0],QAM_amp128b); -- ul_ch_mag128b[0] = _mm_slli_epi16(ul_ch_mag128b[0],1); -+ ul_ch_mag128[2] = _mm_mulhi_epi16(ul_ch_mag128[2],QAM_amp128); -+ ul_ch_mag128[2] = _mm_slli_epi16(ul_ch_mag128[2],1); - - -- ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128b); -- ul_ch_mag128b[1] = _mm_slli_epi16(ul_ch_mag128b[1],1); -+ ul_ch_mag128b[2] = _mm_mulhi_epi16(ul_ch_mag128b[2],QAM_amp128b); -+ ul_ch_mag128b[2] = _mm_slli_epi16(ul_ch_mag128b[2],1); - -- if (is_dmrs_symbol==0) { -- ul_ch_mag128b[2] = _mm_mulhi_epi16(ul_ch_mag128b[2],QAM_amp128b); -- ul_ch_mag128b[2] = _mm_slli_epi16(ul_ch_mag128b[2],1); -- } - } - - // multiply by conjugated channel -@@ -755,117 +795,157 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, - // print_shorts("ch:",ul_ch128+1); - // print_shorts("pack:",rxdataF_comp128+1); - -- if (is_dmrs_symbol==0) { -- // multiply by conjugated channel -- mmtmpD0 = _mm_madd_epi16(ul_ch128[2],rxdataF128[2]); -- // mmtmpD0 contains real part of 4 consecutive outputs (32-bit) -- mmtmpD1 = _mm_shufflelo_epi16(ul_ch128[2],_MM_SHUFFLE(2,3,0,1)); -- mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); -- mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)conjugate); -- mmtmpD1 = _mm_madd_epi16(mmtmpD1,rxdataF128[2]); -- // mmtmpD1 contains imag part of 4 consecutive outputs (32-bit) -- mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift); -- mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift); -- mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); -- mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); -- -- rxdataF_comp128[2] = _mm_packs_epi32(mmtmpD2,mmtmpD3); -- // print_shorts("rx:",rxdataF128+2); -- // print_shorts("ch:",ul_ch128+2); -- // print_shorts("pack:",rxdataF_comp128+2); -- -- ul_ch128+=3; -- ul_ch_mag128+=3; -- ul_ch_mag128b+=3; -- rxdataF128+=3; -- rxdataF_comp128+=3; -- } else { // we have a smaller PUSCH in symbols with pilots so skip last group of 4 REs and increment less -- ul_ch128+=2; -- ul_ch_mag128+=2; -- ul_ch_mag128b+=2; -- rxdataF128+=2; -- rxdataF_comp128+=2; -- } -- -- } -- } -- } -- -- if (rho) { -- -- for (aarx=0; aarxnb_antennas_rx; aarx++) { -- rho128 = (__m128i *)&rho[aarx][symbol*frame_parms->N_RB_UL*12]; -- ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aarx][symbol*frame_parms->N_RB_UL*12]; -- ul_ch128_2 = (__m128i *)&ul_ch_estimates_ext[2+aarx][symbol*frame_parms->N_RB_UL*12]; -- -- for (rb=0; rbnb_antennas_rx][nrOfLayers*nrOfLayers]; -+ int avg_rho_im[frame_parms->nb_antennas_rx][nrOfLayers*nrOfLayers]; -+ -+ for (aarx=0; aarxnb_antennas_rx; aarx++) -+ { -+ for (aatx=0; aatx < nrOfLayers; aatx++) -+ { -+ ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; -+ -+ for (int atx=0; atx< nrOfLayers; atx++) -+ { -+ avg_rho_re[aarx][aatx*nrOfLayers+atx] = 0; -+ avg_rho_im[aarx][aatx*nrOfLayers+atx] = 0; -+ rho128 = (__m128i *)&rho[aarx][aatx*nrOfLayers+atx][symbol*(off+(nb_rb*12))]; -+ ul_ch128_2 = (__m128i *)&ul_ch_estimates_ext[atx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; -+ -+ for (rb=0; rbrx_correlation[0][0][aarx] = signal_energy(&rho[aarx][aatx*nb_aatx+atx][symbol*nb_rb*12],rb*12); -+ avg_rho_re[aarx][aatx*nrOfLayers+atx] = 16*avg_rho_re[aarx][aatx*nrOfLayers+atx]/(nb_rb*12); -+ avg_rho_im[aarx][aatx*nrOfLayers+atx] = 16*avg_rho_im[aarx][aatx*nrOfLayers+atx]/(nb_rb*12); -+ //printf("rho[rx]%d tx%d tx%d = Re: %d Im: %d\n",aarx, aatx,atx, avg_rho_re[aarx][aatx*nb_aatx+atx], avg_rho_im[aarx][aatx*nb_aatx+atx]); -+ } -+ } -+ } - } - } - -@@ -1105,19 +1185,23 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, - } - - void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, -- int32_t **rxdataF_comp, -- int32_t **ul_ch_mag, -- int32_t **ul_ch_magb, -- uint8_t symbol, -- uint16_t nb_rb) { -+ int32_t **rxdataF_comp, -+ int32_t **ul_ch_mag, -+ int32_t **ul_ch_magb, -+ int32_t ***rho, -+ uint8_t nrOfLayers, -+ uint8_t symbol, -+ uint16_t nb_rb, -+ int length) { - int n_rx = frame_parms->nb_antennas_rx; - #if defined(__x86_64__) || defined(__i386__) -- __m128i *rxdataF_comp128[1+n_rx],*ul_ch_mag128[1+n_rx],*ul_ch_mag128b[1+n_rx]; -+ __m128i *rxdataF_comp128[2],*ul_ch_mag128[2],*ul_ch_mag128b[2]; - #elif defined(__arm__) - int16x8_t *rxdataF_comp128_0,*ul_ch_mag128_0,*ul_ch_mag128_0b; - int16x8_t *rxdataF_comp128_1,*ul_ch_mag128_1,*ul_ch_mag128_1b; - #endif - int32_t i; -+ uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); - - #ifdef __AVX2__ - int off = ((nb_rb&1) == 1)? 4:0; -@@ -1125,23 +1209,34 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, - int off = 0; - #endif - -- if (frame_parms->nb_antennas_rx>1) { --#if defined(__x86_64__) || defined(__i386__) -- int nb_re = nb_rb*12; -- for (int aa=0;aanb_antennas_rx;aa++) { -- rxdataF_comp128[aa] = (__m128i *)&rxdataF_comp[aa][(symbol*(nb_re + off))]; -- ul_ch_mag128[aa] = (__m128i *)&ul_ch_mag[aa][(symbol*(nb_re + off))]; -- ul_ch_mag128b[aa] = (__m128i *)&ul_ch_magb[aa][(symbol*(nb_re + off))]; -- } -- for (int aa=1;aanb_antennas_rx;aa++) { -- // MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM llr computation) -- for (i=0; i 1) -+ { -+ #if defined(__x86_64__) || defined(__i386__) -+ for (int aatx=0; aatxnb_antennas_rx][(symbol*(nb_re + off))]; -+ ul_ch_mag128[0] = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx][(symbol*(nb_re + off))]; -+ ul_ch_mag128b[0] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx][(symbol*(nb_re + off))]; -+ -+ for (int aa=1;aa < n_rx;aa++) -+ { -+ rxdataF_comp128[1] = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; -+ ul_ch_mag128[1] = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; -+ ul_ch_mag128b[1] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; -+ -+ // MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM llr computation) -+ for (i=0; iN_RB_DL*12]; - rxdataF_comp128_1 = (int16x8_t *)&rxdataF_comp[1][symbol*frame_parms->N_RB_DL*12]; - ul_ch_mag128_0 = (int16x8_t *)&ul_ch_mag[0][symbol*frame_parms->N_RB_DL*12]; -@@ -1156,7 +1251,7 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, - ul_ch_mag128_0b[i] = vhaddq_s16(ul_ch_mag128_0b[i],ul_ch_mag128_1b[i]); - rxdataF_comp128_0[i] = vqaddq_s16(rxdataF_comp128_0[i],(*(int16x8_t *)&jitterc[0])); - } --#endif -+ #endif - } - - #if defined(__x86_64__) || defined(__i386__) -@@ -1165,6 +1260,696 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, - #endif - } - -+/* Zero Forcing Rx function: nr_det_HhH() -+ * -+ * -+ * */ -+void nr_ulsch_det_HhH(int32_t *after_mf_00,//a -+ int32_t *after_mf_01,//b -+ int32_t *after_mf_10,//c -+ int32_t *after_mf_11,//d -+ int32_t *det_fin,//1/ad-bc -+ unsigned short nb_rb, -+ unsigned char symbol, -+ int32_t shift) -+{ -+ int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; -+ unsigned short rb; -+ __m128i *after_mf_00_128,*after_mf_01_128, *after_mf_10_128, *after_mf_11_128, ad_re_128, bc_re_128; //ad_im_128, bc_im_128; -+ __m128i *det_fin_128, det_re_128; //det_im_128, tmp_det0, tmp_det1; -+ -+ after_mf_00_128 = (__m128i *)after_mf_00; -+ after_mf_01_128 = (__m128i *)after_mf_01; -+ after_mf_10_128 = (__m128i *)after_mf_10; -+ after_mf_11_128 = (__m128i *)after_mf_11; -+ -+ det_fin_128 = (__m128i *)det_fin; -+ -+ for (rb=0; rb<3*nb_rb; rb++) { -+ -+ //complex multiplication (I_a+jQ_a)(I_d+jQ_d) = (I_aI_d - Q_aQ_d) + j(Q_aI_d + I_aQ_d) -+ //The imag part is often zero, we compute only the real part -+ ad_re_128 = _mm_sign_epi16(after_mf_00_128[0],*(__m128i*)&nr_conjug2[0]); -+ ad_re_128 = _mm_madd_epi16(ad_re_128,after_mf_11_128[0]); //Re: I_a0*I_d0 - Q_a1*Q_d1 -+ //ad_im_128 = _mm_shufflelo_epi16(after_mf_00_128[0],_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ //ad_im_128 = _mm_shufflehi_epi16(ad_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ //ad_im_128 = _mm_madd_epi16(ad_im_128,after_mf_11_128[0]);//Im: (Q_aI_d + I_aQ_d) -+ -+ //complex multiplication (I_b+jQ_b)(I_c+jQ_c) = (I_bI_c - Q_bQ_c) + j(Q_bI_c + I_bQ_c) -+ //The imag part is often zero, we compute only the real part -+ bc_re_128 = _mm_sign_epi16(after_mf_01_128[0],*(__m128i*)&nr_conjug2[0]); -+ bc_re_128 = _mm_madd_epi16(bc_re_128,after_mf_10_128[0]); //Re: I_b0*I_c0 - Q_b1*Q_c1 -+ //bc_im_128 = _mm_shufflelo_epi16(after_mf_01_128[0],_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_b0 Q_b1 I_b2 Q_b3]_64bits to [Q_b1 I_b0 Q_b3 I_b2]_64bits -+ //bc_im_128 = _mm_shufflehi_epi16(bc_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_b0 Q_b1 I_b2 Q_b3]_64bits to [Q_b1 I_b0 Q_b3 I_b2]_64bits -+ //bc_im_128 = _mm_madd_epi16(bc_im_128,after_mf_10_128[0]);//Im: (Q_bI_c + I_bQ_c) -+ -+ det_re_128 = _mm_sub_epi32(ad_re_128, bc_re_128); -+ //det_im_128 = _mm_sub_epi32(ad_im_128, bc_im_128); -+ -+ //det in Q30 format -+ det_fin_128[0] = _mm_abs_epi32(det_re_128); -+ -+ -+#ifdef DEBUG_DLSCH_DEMOD -+ printf("\n Computing det_HhH_inv \n"); -+ //print_ints("det_re_128:",(int32_t*)&det_re_128); -+ //print_ints("det_im_128:",(int32_t*)&det_im_128); -+ print_ints("det_fin_128:",(int32_t*)&det_fin_128[0]); -+#endif -+ det_fin_128+=1; -+ after_mf_00_128+=1; -+ after_mf_01_128+=1; -+ after_mf_10_128+=1; -+ after_mf_11_128+=1; -+ } -+ _mm_empty(); -+ _m_empty(); -+} -+ -+/* Zero Forcing Rx function: nr_inv_comp_muli -+ * Complex number multi: z = x*y -+ * = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) -+ * */ -+__m128i nr_ulsch_inv_comp_muli(__m128i input_x, -+ __m128i input_y) -+{ -+ int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; -+ -+ __m128i xy_re_128, xy_im_128; -+ __m128i output_z, tmp_z0, tmp_z1; -+ -+ // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) -+ -+ // the real part -+ xy_re_128 = _mm_sign_epi16(input_x,*(__m128i*)&nr_conjug2[0]); -+ xy_re_128 = _mm_madd_epi16(xy_re_128,input_y); //Re: (x_re*y_re - x_im*y_im) -+ -+ // the imag part -+ xy_im_128 = _mm_shufflelo_epi16(input_x,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ xy_im_128 = _mm_shufflehi_epi16(xy_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ xy_im_128 = _mm_madd_epi16(xy_im_128,input_y);//Im: (x_im*y_re + x_re*y_im) -+ -+ //convert back to Q15 before packing -+ xy_re_128 = _mm_srai_epi32(xy_re_128,4);//(2^15/64*2*16) -+ xy_im_128 = _mm_srai_epi32(xy_im_128,4); -+ -+ tmp_z0 = _mm_unpacklo_epi32(xy_re_128,xy_im_128); -+ //print_ints("unpack lo:",&tmp_z0[0]); -+ tmp_z1 = _mm_unpackhi_epi32(xy_re_128,xy_im_128); -+ //print_ints("unpack hi:",&tmp_z1[0]); -+ output_z = _mm_packs_epi32(tmp_z0,tmp_z1); -+ -+ _mm_empty(); -+ _m_empty(); -+ return(output_z); -+} -+ -+/* Zero Forcing Rx function: nr_conjch0_mult_ch1() -+ * -+ * -+ * */ -+void nr_ulsch_conjch0_mult_ch1(int *ch0, -+ int *ch1, -+ int32_t *ch0conj_ch1, -+ unsigned short nb_rb, -+ unsigned char output_shift0) -+{ -+ //This function is used to compute multiplications in H_hermitian * H matrix -+ short nr_conjugate[8]__attribute__((aligned(16))) = {-1,1,-1,1,-1,1,-1,1}; -+ unsigned short rb; -+ __m128i *dl_ch0_128,*dl_ch1_128, *ch0conj_ch1_128, mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3; -+ -+ dl_ch0_128 = (__m128i *)ch0; -+ dl_ch1_128 = (__m128i *)ch1; -+ -+ ch0conj_ch1_128 = (__m128i *)ch0conj_ch1; -+ -+ for (rb=0; rb<3*nb_rb; rb++) { -+ -+ mmtmpD0 = _mm_madd_epi16(dl_ch0_128[0],dl_ch1_128[0]); -+ mmtmpD1 = _mm_shufflelo_epi16(dl_ch0_128[0],_MM_SHUFFLE(2,3,0,1)); -+ mmtmpD1 = _mm_shufflehi_epi16(mmtmpD1,_MM_SHUFFLE(2,3,0,1)); -+ mmtmpD1 = _mm_sign_epi16(mmtmpD1,*(__m128i*)&nr_conjugate[0]); -+ mmtmpD1 = _mm_madd_epi16(mmtmpD1,dl_ch1_128[0]); -+ mmtmpD0 = _mm_srai_epi32(mmtmpD0,output_shift0); -+ mmtmpD1 = _mm_srai_epi32(mmtmpD1,output_shift0); -+ mmtmpD2 = _mm_unpacklo_epi32(mmtmpD0,mmtmpD1); -+ mmtmpD3 = _mm_unpackhi_epi32(mmtmpD0,mmtmpD1); -+ -+ ch0conj_ch1_128[0] = _mm_packs_epi32(mmtmpD2,mmtmpD3); -+ -+ /*printf("\n Computing conjugates \n"); -+ print_shorts("ch0:",(int16_t*)&dl_ch0_128[0]); -+ print_shorts("ch1:",(int16_t*)&dl_ch1_128[0]); -+ print_shorts("pack:",(int16_t*)&ch0conj_ch1_128[0]);*/ -+ -+ dl_ch0_128+=1; -+ dl_ch1_128+=1; -+ ch0conj_ch1_128+=1; -+ } -+ _mm_empty(); -+ _m_empty(); -+} -+__m128i nr_ulsch_comp_muli_sum(__m128i input_x, -+ __m128i input_y, -+ __m128i input_w, -+ __m128i input_z, -+ __m128i det) -+{ -+ int16_t nr_conjug2[8]__attribute__((aligned(16))) = {1,-1,1,-1,1,-1,1,-1} ; -+ -+ __m128i xy_re_128, xy_im_128, wz_re_128, wz_im_128; -+ __m128i output, tmp_z0, tmp_z1; -+ -+ // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) -+ // the real part -+ xy_re_128 = _mm_sign_epi16(input_x,*(__m128i*)&nr_conjug2[0]); -+ xy_re_128 = _mm_madd_epi16(xy_re_128,input_y); //Re: (x_re*y_re - x_im*y_im) -+ -+ // the imag part -+ xy_im_128 = _mm_shufflelo_epi16(input_x,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ xy_im_128 = _mm_shufflehi_epi16(xy_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ xy_im_128 = _mm_madd_epi16(xy_im_128,input_y);//Im: (x_im*y_re + x_re*y_im) -+ -+ // complex multiplication (w_re + jw_im)*(z_re + jz_im) = (w_re*z_re - w_im*z_im) + j(w_im*z_re + w_re*z_im) -+ // the real part -+ wz_re_128 = _mm_sign_epi16(input_w,*(__m128i*)&nr_conjug2[0]); -+ wz_re_128 = _mm_madd_epi16(wz_re_128,input_z); //Re: (w_re*z_re - w_im*z_im) -+ -+ // the imag part -+ wz_im_128 = _mm_shufflelo_epi16(input_w,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the low 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ wz_im_128 = _mm_shufflehi_epi16(wz_im_128,_MM_SHUFFLE(2,3,0,1));//permutes IQs for the high 64 bits as [I_a0 Q_a1 I_a2 Q_a3]_64bits to [Q_a1 I_a0 Q_a3 I_a2]_64bits -+ wz_im_128 = _mm_madd_epi16(wz_im_128,input_z);//Im: (w_im*z_re + w_re*z_im) -+ -+ -+ xy_re_128 = _mm_sub_epi32(xy_re_128, wz_re_128); -+ xy_im_128 = _mm_sub_epi32(xy_im_128, wz_im_128); -+ //print_ints("rx_re:",(int32_t*)&xy_re_128[0]); -+ //print_ints("rx_Img:",(int32_t*)&xy_im_128[0]); -+ //divide by matrix det and convert back to Q15 before packing -+ int sum_det =0; -+ for (int k=0; k<4;k++) { -+ sum_det += ((((int *)&det[0])[k])>>2); -+ //printf("det_%d = %d log2 =%d \n",k,(((int *)&det[0])[k]),log2_approx(((int *)&det[0])[k])); -+ } -+ -+ xy_re_128 = _mm_slli_epi32(xy_re_128,5); -+ xy_re_128 = _mm_srai_epi32(xy_re_128,log2_approx(sum_det)); -+ xy_re_128 = _mm_slli_epi32(xy_re_128,5); -+ -+ xy_im_128 = _mm_slli_epi32(xy_im_128,5); -+ xy_im_128 = _mm_srai_epi32(xy_im_128,log2_approx(sum_det)); -+ xy_im_128 = _mm_slli_epi32(xy_im_128,5); -+ -+ tmp_z0 = _mm_unpacklo_epi32(xy_re_128,xy_im_128); -+ //print_ints("unpack lo:",&tmp_z0[0]); -+ tmp_z1 = _mm_unpackhi_epi32(xy_re_128,xy_im_128); -+ //print_ints("unpack hi:",&tmp_z1[0]); -+ output = _mm_packs_epi32(tmp_z0,tmp_z1); -+ -+ _mm_empty(); -+ _m_empty(); -+ return(output); -+} -+/* Zero Forcing Rx function: nr_construct_HhH_elements() -+ * -+ * -+ * */ -+void nr_ulsch_construct_HhH_elements(int *conjch00_ch00, -+ int *conjch01_ch01, -+ int *conjch11_ch11, -+ int *conjch10_ch10,// -+ int *conjch20_ch20, -+ int *conjch21_ch21, -+ int *conjch30_ch30, -+ int *conjch31_ch31, -+ int *conjch00_ch01,//00_01 -+ int *conjch01_ch00,//01_00 -+ int *conjch10_ch11,//10_11 -+ int *conjch11_ch10,//11_10 -+ int *conjch20_ch21, -+ int *conjch21_ch20, -+ int *conjch30_ch31, -+ int *conjch31_ch30, -+ int32_t *after_mf_00, -+ int32_t *after_mf_01, -+ int32_t *after_mf_10, -+ int32_t *after_mf_11, -+ unsigned short nb_rb, -+ unsigned char symbol) -+{ -+ //This function is used to construct the (H_hermitian * H matrix) matrix elements -+ unsigned short rb; -+ __m128i *conjch00_ch00_128, *conjch01_ch01_128, *conjch11_ch11_128, *conjch10_ch10_128; -+ __m128i *conjch20_ch20_128, *conjch21_ch21_128, *conjch30_ch30_128, *conjch31_ch31_128; -+ __m128i *conjch00_ch01_128, *conjch01_ch00_128, *conjch10_ch11_128, *conjch11_ch10_128; -+ __m128i *conjch20_ch21_128, *conjch21_ch20_128, *conjch30_ch31_128, *conjch31_ch30_128; -+ __m128i *after_mf_00_128, *after_mf_01_128, *after_mf_10_128, *after_mf_11_128; -+ -+ conjch00_ch00_128 = (__m128i *)conjch00_ch00; -+ conjch01_ch01_128 = (__m128i *)conjch01_ch01; -+ conjch11_ch11_128 = (__m128i *)conjch11_ch11; -+ conjch10_ch10_128 = (__m128i *)conjch10_ch10; -+ -+ conjch20_ch20_128 = (__m128i *)conjch20_ch20; -+ conjch21_ch21_128 = (__m128i *)conjch21_ch21; -+ conjch30_ch30_128 = (__m128i *)conjch30_ch30; -+ conjch31_ch31_128 = (__m128i *)conjch31_ch31; -+ -+ conjch00_ch01_128 = (__m128i *)conjch00_ch01; -+ conjch01_ch00_128 = (__m128i *)conjch01_ch00; -+ conjch10_ch11_128 = (__m128i *)conjch10_ch11; -+ conjch11_ch10_128 = (__m128i *)conjch11_ch10; -+ -+ conjch20_ch21_128 = (__m128i *)conjch20_ch21; -+ conjch21_ch20_128 = (__m128i *)conjch21_ch20; -+ conjch30_ch31_128 = (__m128i *)conjch30_ch31; -+ conjch31_ch30_128 = (__m128i *)conjch31_ch30; -+ -+ after_mf_00_128 = (__m128i *)after_mf_00; -+ after_mf_01_128 = (__m128i *)after_mf_01; -+ after_mf_10_128 = (__m128i *)after_mf_10; -+ after_mf_11_128 = (__m128i *)after_mf_11; -+ -+ for (rb=0; rb<3*nb_rb; rb++) { -+ -+ after_mf_00_128[0] =_mm_adds_epi16(conjch00_ch00_128[0],conjch10_ch10_128[0]);//00_00 + 10_10 -+ if (conjch20_ch20 != NULL) after_mf_00_128[0] =_mm_adds_epi16(after_mf_00_128[0],conjch20_ch20_128[0]); -+ if (conjch30_ch30 != NULL) after_mf_00_128[0] =_mm_adds_epi16(after_mf_00_128[0],conjch30_ch30_128[0]); -+ -+ after_mf_11_128[0] =_mm_adds_epi16(conjch01_ch01_128[0], conjch11_ch11_128[0]); //01_01 + 11_11 -+ if (conjch21_ch21 != NULL) after_mf_11_128[0] =_mm_adds_epi16(after_mf_11_128[0],conjch21_ch21_128[0]); -+ if (conjch31_ch31 != NULL) after_mf_11_128[0] =_mm_adds_epi16(after_mf_11_128[0],conjch31_ch31_128[0]); -+ -+ after_mf_01_128[0] =_mm_adds_epi16(conjch00_ch01_128[0], conjch10_ch11_128[0]);//00_01 + 10_11 -+ if (conjch20_ch21 != NULL) after_mf_01_128[0] =_mm_adds_epi16(after_mf_01_128[0],conjch20_ch21_128[0]); -+ if (conjch30_ch31 != NULL) after_mf_01_128[0] =_mm_adds_epi16(after_mf_01_128[0],conjch30_ch31_128[0]); -+ -+ after_mf_10_128[0] =_mm_adds_epi16(conjch01_ch00_128[0], conjch11_ch10_128[0]);//01_00 + 11_10 -+ if (conjch21_ch20 != NULL) after_mf_10_128[0] =_mm_adds_epi16(after_mf_10_128[0],conjch21_ch20_128[0]); -+ if (conjch31_ch30 != NULL) after_mf_10_128[0] =_mm_adds_epi16(after_mf_10_128[0],conjch31_ch30_128[0]); -+ -+#ifdef DEBUG_DLSCH_DEMOD -+ if ((rb<=30)) -+ { -+ printf(" \n construct_HhH_elements \n"); -+ print_shorts("after_mf_00_128:",(int16_t*)&after_mf_00_128[0]); -+ print_shorts("after_mf_01_128:",(int16_t*)&after_mf_01_128[0]); -+ print_shorts("after_mf_10_128:",(int16_t*)&after_mf_10_128[0]); -+ print_shorts("after_mf_11_128:",(int16_t*)&after_mf_11_128[0]); -+ } -+#endif -+ conjch00_ch00_128+=1; -+ conjch10_ch10_128+=1; -+ conjch01_ch01_128+=1; -+ conjch11_ch11_128+=1; -+ -+ if (conjch20_ch20 != NULL) conjch20_ch20_128+=1; -+ if (conjch21_ch21 != NULL) conjch21_ch21_128+=1; -+ if (conjch30_ch30 != NULL) conjch30_ch30_128+=1; -+ if (conjch31_ch31 != NULL) conjch31_ch31_128+=1; -+ -+ conjch00_ch01_128+=1; -+ conjch01_ch00_128+=1; -+ conjch10_ch11_128+=1; -+ conjch11_ch10_128+=1; -+ -+ if (conjch20_ch21 != NULL) conjch20_ch21_128+=1; -+ if (conjch21_ch20 != NULL) conjch21_ch20_128+=1; -+ if (conjch30_ch31 != NULL) conjch30_ch31_128+=1; -+ if (conjch31_ch30 != NULL) conjch31_ch30_128+=1; -+ -+ after_mf_00_128 += 1; -+ after_mf_01_128 += 1; -+ after_mf_10_128 += 1; -+ after_mf_11_128 += 1; -+ } -+ _mm_empty(); -+ _m_empty(); -+} -+ -+/* Zero Forcing Rx function: nr_ulsch_zero_forcing_rx_2layers() -+ * -+ * -+ * */ -+uint8_t nr_ulsch_zero_forcing_rx_2layers(int **rxdataF_comp, -+ int **ul_ch_mag, -+ int **ul_ch_magb, -+ int **ul_ch_estimates_ext, -+ unsigned short nb_rb, -+ unsigned char n_rx, -+ unsigned char mod_order, -+ int shift, -+ unsigned char symbol, -+ int length) -+{ -+ int *ch00, *ch01, *ch10, *ch11; -+ int *ch20, *ch30, *ch21, *ch31; -+ uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); -+ -+ #ifdef __AVX2__ -+ int off = ((nb_rb&1) == 1)? 4:0; -+ #else -+ int off = 0; -+ #endif -+ -+ /* we need at least alignment to 16 bytes, let's put 32 to be sure -+ * (maybe not necessary but doesn't hurt) -+ */ -+ int32_t conjch00_ch01[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch01_ch00[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch10_ch11[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch11_ch10[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch00_ch00[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch01_ch01[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch10_ch10[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch11_ch11[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch20_ch20[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch21_ch21[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch30_ch30[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch31_ch31[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch20_ch21[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch30_ch31[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch21_ch20[12*nb_rb] __attribute__((aligned(32))); -+ int32_t conjch31_ch30[12*nb_rb] __attribute__((aligned(32))); -+ -+ int32_t af_mf_00[12*nb_rb] __attribute__((aligned(32))); -+ int32_t af_mf_01[12*nb_rb] __attribute__((aligned(32))); -+ int32_t af_mf_10[12*nb_rb] __attribute__((aligned(32))); -+ int32_t af_mf_11[12*nb_rb] __attribute__((aligned(32))); -+ int32_t determ_fin[12*nb_rb] __attribute__((aligned(32))); -+ -+ switch (n_rx) { -+ case 2:// -+ ch00 = (int *)&ul_ch_estimates_ext[0][symbol*(off+nb_rb*12)]; -+ ch01 = (int *)&ul_ch_estimates_ext[2][symbol*(off+nb_rb*12)]; -+ ch10 = (int *)&ul_ch_estimates_ext[1][symbol*(off+nb_rb*12)]; -+ ch11 = (int *)&ul_ch_estimates_ext[3][symbol*(off+nb_rb*12)]; -+ ch20 = NULL; -+ ch21 = NULL; -+ ch30 = NULL; -+ ch31 = NULL; -+ break; -+ -+ case 4:// -+ ch00 = (int *)&ul_ch_estimates_ext[0][symbol*(off+nb_rb*12)]; -+ ch01 = (int *)&ul_ch_estimates_ext[4][symbol*(off+nb_rb*12)]; -+ ch10 = (int *)&ul_ch_estimates_ext[1][symbol*(off+nb_rb*12)]; -+ ch11 = (int *)&ul_ch_estimates_ext[5][symbol*(off+nb_rb*12)]; -+ ch20 = (int *)&ul_ch_estimates_ext[2][symbol*(off+nb_rb*12)]; -+ ch21 = (int *)&ul_ch_estimates_ext[6][symbol*(off+nb_rb*12)]; -+ ch30 = (int *)&ul_ch_estimates_ext[3][symbol*(off+nb_rb*12)]; -+ ch31 = (int *)&ul_ch_estimates_ext[7][symbol*(off+nb_rb*12)]; -+ break; -+ -+ default: -+ return -1; -+ break; -+ } -+ -+ /* 1- Compute the rx channel matrix after compensation: (1/2^log2_max)x(H_herm x H) -+ * for n_rx = 2 -+ * |conj_H_00 conj_H_10| | H_00 H_01| |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| -+ * | | x | | = | | -+ * |conj_H_01 conj_H_11| | H_10 H_11| |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| -+ * -+ */ -+ -+ if (n_rx>=2){ -+ // (1/2^log2_maxh)*conj_H_00xH_00: (1/(64*2))conjH_00*H_00*2^15 -+ nr_ulsch_conjch0_mult_ch1(ch00, -+ ch00, -+ conjch00_ch00, -+ nb_rb_0, -+ shift); -+ // (1/2^log2_maxh)*conj_H_10xH_10: (1/(64*2))conjH_10*H_10*2^15 -+ nr_ulsch_conjch0_mult_ch1(ch10, -+ ch10, -+ conjch10_ch10, -+ nb_rb_0, -+ shift); -+ // conj_H_00xH_01 -+ nr_ulsch_conjch0_mult_ch1(ch00, -+ ch01, -+ conjch00_ch01, -+ nb_rb_0, -+ shift); // this shift is equal to the channel level log2_maxh -+ // conj_H_10xH_11 -+ nr_ulsch_conjch0_mult_ch1(ch10, -+ ch11, -+ conjch10_ch11, -+ nb_rb_0, -+ shift); -+ // conj_H_01xH_01 -+ nr_ulsch_conjch0_mult_ch1(ch01, -+ ch01, -+ conjch01_ch01, -+ nb_rb_0, -+ shift); -+ // conj_H_11xH_11 -+ nr_ulsch_conjch0_mult_ch1(ch11, -+ ch11, -+ conjch11_ch11, -+ nb_rb_0, -+ shift); -+ // conj_H_01xH_00 -+ nr_ulsch_conjch0_mult_ch1(ch01, -+ ch00, -+ conjch01_ch00, -+ nb_rb_0, -+ shift); -+ // conj_H_11xH_10 -+ nr_ulsch_conjch0_mult_ch1(ch11, -+ ch10, -+ conjch11_ch10, -+ nb_rb_0, -+ shift); -+ } -+ if (n_rx==4){ -+ // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2*16))conjH_20*H_20*2^15 -+ nr_ulsch_conjch0_mult_ch1(ch20, -+ ch20, -+ conjch20_ch20, -+ nb_rb_0, -+ shift); -+ -+ // (1/2^log2_maxh)*conj_H_30xH_30: (1/(64*2*4))conjH_30*H_30*2^15 -+ nr_ulsch_conjch0_mult_ch1(ch30, -+ ch30, -+ conjch30_ch30, -+ nb_rb_0, -+ shift); -+ -+ // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 -+ nr_ulsch_conjch0_mult_ch1(ch20, -+ ch21, -+ conjch20_ch21, -+ nb_rb_0, -+ shift); -+ -+ nr_ulsch_conjch0_mult_ch1(ch30, -+ ch31, -+ conjch30_ch31, -+ nb_rb_0, -+ shift); -+ -+ nr_ulsch_conjch0_mult_ch1(ch21, -+ ch21, -+ conjch21_ch21, -+ nb_rb_0, -+ shift); -+ -+ nr_ulsch_conjch0_mult_ch1(ch31, -+ ch31, -+ conjch31_ch31, -+ nb_rb_0, -+ shift); -+ -+ // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 -+ nr_ulsch_conjch0_mult_ch1(ch21, -+ ch20, -+ conjch21_ch20, -+ nb_rb_0, -+ shift); -+ -+ nr_ulsch_conjch0_mult_ch1(ch31, -+ ch30, -+ conjch31_ch30, -+ nb_rb_0, -+ shift); -+ -+ nr_ulsch_construct_HhH_elements(conjch00_ch00, -+ conjch01_ch01, -+ conjch11_ch11, -+ conjch10_ch10,// -+ conjch20_ch20, -+ conjch21_ch21, -+ conjch30_ch30, -+ conjch31_ch31, -+ conjch00_ch01, -+ conjch01_ch00, -+ conjch10_ch11, -+ conjch11_ch10,// -+ conjch20_ch21, -+ conjch21_ch20, -+ conjch30_ch31, -+ conjch31_ch30, -+ af_mf_00, -+ af_mf_01, -+ af_mf_10, -+ af_mf_11, -+ nb_rb_0, -+ symbol); -+ } -+ if (n_rx==2){ -+ nr_ulsch_construct_HhH_elements(conjch00_ch00, -+ conjch01_ch01, -+ conjch11_ch11, -+ conjch10_ch10,// -+ NULL, -+ NULL, -+ NULL, -+ NULL, -+ conjch00_ch01, -+ conjch01_ch00, -+ conjch10_ch11, -+ conjch11_ch10,// -+ NULL, -+ NULL, -+ NULL, -+ NULL, -+ af_mf_00, -+ af_mf_01, -+ af_mf_10, -+ af_mf_11, -+ nb_rb_0, -+ symbol); -+ } -+ //det_HhH = ad -bc -+ nr_ulsch_det_HhH(af_mf_00,//a -+ af_mf_01,//b -+ af_mf_10,//c -+ af_mf_11,//d -+ determ_fin, -+ nb_rb_0, -+ symbol, -+ shift); -+ /* 2- Compute the channel matrix inversion ********************************** -+ * -+ * |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| -+ * A= | | -+ * |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| -+ * -+ * -+ * -+ *inv(A) =(1/det)*[d -b -+ * -c a] -+ * -+ * -+ **************************************************************************/ -+ __m128i *rxdataF_comp128_0,*rxdataF_comp128_1,*ul_ch_mag128_0=NULL,*ul_ch_mag128b_0=NULL,*determ_fin_128;//*dl_ch_mag128_1,*dl_ch_mag128b_1,*dl_ch_mag128r_1 -+ __m128i mmtmpD0,mmtmpD1,mmtmpD2,mmtmpD3; -+ __m128i *after_mf_a_128,*after_mf_b_128, *after_mf_c_128, *after_mf_d_128; -+ __m128i QAM_amp128={0},QAM_amp128b={0}; -+ -+ determ_fin_128 = (__m128i *)&determ_fin[0]; -+ -+ rxdataF_comp128_0 = (__m128i *)&rxdataF_comp[0][symbol*(off+nb_rb*12)];//aatx=0 @ aarx =0 -+ rxdataF_comp128_1 = (__m128i *)&rxdataF_comp[n_rx][symbol*(off+nb_rb*12)];//aatx=1 @ aarx =0 -+ -+ after_mf_a_128 = (__m128i *)af_mf_00; -+ after_mf_b_128 = (__m128i *)af_mf_01; -+ after_mf_c_128 = (__m128i *)af_mf_10; -+ after_mf_d_128 = (__m128i *)af_mf_11; -+ -+ if (mod_order>2) { -+ if (mod_order == 4) { -+ QAM_amp128 = _mm_set1_epi16(QAM16_n1); //2/sqrt(10) -+ QAM_amp128b = _mm_setzero_si128(); -+ } else if (mod_order == 6) { -+ QAM_amp128 = _mm_set1_epi16(QAM64_n1); //4/sqrt{42} -+ QAM_amp128b = _mm_set1_epi16(QAM64_n2); //2/sqrt{42} -+ } -+ ul_ch_mag128_0 = (__m128i *)&ul_ch_mag[0][symbol*(off+nb_rb*12)]; -+ ul_ch_mag128b_0 = (__m128i *)&ul_ch_magb[0][symbol*(off+nb_rb*12)]; -+ } -+ -+ for (int rb=0; rb<3*nb_rb_0; rb++) { -+ if (mod_order>2) { -+ int sum_det =0; -+ for (int k=0; k<4;k++) { -+ sum_det += ((((int *)&determ_fin_128[0])[k])>>2); -+ //printf("det_%d = %d\n",k,sum_det); -+ } -+ -+ mmtmpD2 = _mm_slli_epi32(determ_fin_128[0],5); -+ mmtmpD2 = _mm_srai_epi32(mmtmpD2,log2_approx(sum_det)); -+ mmtmpD2 = _mm_slli_epi32(mmtmpD2,5); -+ -+ mmtmpD3 = _mm_unpacklo_epi32(mmtmpD2,mmtmpD2); -+ -+ mmtmpD2 = _mm_unpackhi_epi32(mmtmpD2,mmtmpD2); -+ -+ mmtmpD2 = _mm_packs_epi32(mmtmpD3,mmtmpD2); -+ -+ ul_ch_mag128_0[0] = mmtmpD2; -+ ul_ch_mag128b_0[0] = mmtmpD2; -+ -+ ul_ch_mag128_0[0] = _mm_mulhi_epi16(ul_ch_mag128_0[0],QAM_amp128); -+ ul_ch_mag128_0[0] = _mm_slli_epi16(ul_ch_mag128_0[0],1); -+ -+ ul_ch_mag128b_0[0] = _mm_mulhi_epi16(ul_ch_mag128b_0[0],QAM_amp128b); -+ ul_ch_mag128b_0[0] = _mm_slli_epi16(ul_ch_mag128b_0[0],1); -+ -+ //print_shorts("mag layer 1:",(int16_t*)&dl_ch_mag128_0[0]); -+ //print_shorts("mag layer 2:",(int16_t*)&dl_ch_mag128_1[0]); -+ //print_shorts("magb layer 1:",(int16_t*)&dl_ch_mag128b_0[0]); -+ //print_shorts("magb layer 2:",(int16_t*)&dl_ch_mag128b_1[0]); -+ //print_shorts("magr layer 1:",(int16_t*)&dl_ch_mag128r_0[0]); -+ //print_shorts("magr layer 2:",(int16_t*)&dl_ch_mag128r_1[0]); -+ } -+ // multiply by channel Inv -+ //rxdataF_zf128_0 = rxdataF_comp128_0*d - b*rxdataF_comp128_1 -+ //rxdataF_zf128_1 = rxdataF_comp128_1*a - c*rxdataF_comp128_0 -+ //printf("layer_1 \n"); -+ mmtmpD0 = nr_ulsch_comp_muli_sum(rxdataF_comp128_0[0], -+ after_mf_d_128[0], -+ rxdataF_comp128_1[0], -+ after_mf_b_128[0], -+ determ_fin_128[0]); -+ -+ //printf("layer_2 \n"); -+ mmtmpD1 = nr_ulsch_comp_muli_sum(rxdataF_comp128_1[0], -+ after_mf_a_128[0], -+ rxdataF_comp128_0[0], -+ after_mf_c_128[0], -+ determ_fin_128[0]); -+ -+ rxdataF_comp128_0[0] = mmtmpD0; -+ rxdataF_comp128_1[0] = mmtmpD1; -+#ifdef DEBUG_DLSCH_DEMOD -+ printf("\n Rx signal after ZF l%d rb%d\n",symbol,rb); -+ print_shorts(" Rx layer 1:",(int16_t*)&rxdataF_comp128_0[0]); -+ print_shorts(" Rx layer 2:",(int16_t*)&rxdataF_comp128_1[0]); -+#endif -+ determ_fin_128 += 1; -+ ul_ch_mag128_0 += 1; -+ ul_ch_mag128b_0 += 1; -+ rxdataF_comp128_0 += 1; -+ rxdataF_comp128_1 += 1; -+ after_mf_a_128 += 1; -+ after_mf_b_128 += 1; -+ after_mf_c_128 += 1; -+ after_mf_d_128 += 1; -+ } -+ _mm_empty(); -+ _m_empty(); -+ return(0); -+} -+ -+//============================================================================================== -+ -+/* Main Function */ - int nr_rx_pusch(PHY_VARS_gNB *gNB, - uint8_t ulsch_id, - uint32_t frame, -@@ -1184,21 +1969,29 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - gNB->pusch_vars[ulsch_id]->cl_done = 0; - - bwp_start_subcarrier = ((rel15_ul->rb_start + rel15_ul->bwp_start)*NR_NB_SC_PER_RB + frame_parms->first_carrier_offset) % frame_parms->ofdm_symbol_size; -- LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); -- LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); -- -+ LOG_I(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); -+ LOG_I(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); -+ LOG_I(PHY,"ulsch RX %x : start_rb %d nb_rb %d mcs %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_ports %d\n", -+ rel15_ul->rnti,rel15_ul->rb_start,rel15_ul->rb_size,rel15_ul->mcs_index, -+ rel15_ul->nrOfLayers,0,rel15_ul->bwp_start,0,rel15_ul->start_symbol_index,rel15_ul->nr_of_symbols, -+ rel15_ul->num_dmrs_cdm_grps_no_data,rel15_ul->ul_dmrs_symb_pos,rel15_ul->dmrs_ports); - //---------------------------------------------------------- - //--------------------- Channel estimation --------------------- - //---------------------------------------------------------- - start_meas(&gNB->ulsch_channel_estimation_stats); -- for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { -+ for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) -+ { - uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; - LOG_D(PHY, "symbol %d, dmrs_symbol_flag :%d\n", symbol, dmrs_symbol_flag); -- if (dmrs_symbol_flag == 1) { -+ -+ if (dmrs_symbol_flag == 1) -+ { - if (gNB->pusch_vars[ulsch_id]->dmrs_symbol == INVALID_VALUE) - gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; - - for (int nl=0; nlnrOfLayers; nl++) -+ { -+ - nr_pusch_channel_estimation(gNB, - slot, - get_dmrs_port(nl,rel15_ul->dmrs_ports), -@@ -1206,6 +1999,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - ulsch_id, - bwp_start_subcarrier, - rel15_ul); -+ } - - nr_gnb_measurements(gNB, ulsch_id, harq_pid, symbol,rel15_ul->nrOfLayers); - -@@ -1214,9 +2008,12 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] = 0; - gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] = 0; - } -- gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( -- &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aarx][symbol * frame_parms->ofdm_symbol_size], -+ for (aatx = 0; aatx < rel15_ul->nrOfLayers; aatx++) -+ { -+ gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( -+ &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aatx*gNB->frame_parms.nb_antennas_rx+aarx][symbol * frame_parms->ofdm_symbol_size], - rel15_ul->rb_size * 12); -+ } - for (int rb = 0; rb < rel15_ul->rb_size; rb++) { - gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] += - gNB->measurements.n0_subband_power[aarx][rel15_ul->bwp_start + rel15_ul->rb_start + rb] / -@@ -1234,23 +2031,29 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - #endif - uint32_t rxdataF_ext_offset = 0; - -- for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { -+ for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) -+ { - uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; -- if (dmrs_symbol_flag == 1) { -+ if (dmrs_symbol_flag == 1) -+ { - if ((rel15_ul->ul_dmrs_symb_pos >> ((symbol + 1) % frame_parms->symbols_per_slot)) & 0x01) - AssertFatal(1==0,"Double DMRS configuration is not yet supported\n"); - - gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; - -- if (rel15_ul->dmrs_config_type == 0) { -+ if (rel15_ul->dmrs_config_type == 0) -+ { - // if no data in dmrs cdm group is 1 only even REs have no data - // if no data in dmrs cdm group is 2 both odd and even REs have no data - nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*6)); - } -- else { -+ else -+ { - nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*4)); - } -- } else { -+ } -+ else -+ { - nb_re_pusch = rel15_ul->rb_size * NR_NB_SC_PER_RB; - } - -@@ -1260,16 +2063,16 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - //---------------------------------------------------------- - //--------------------- RBs extraction --------------------- - //---------------------------------------------------------- -- if (nb_re_pusch > 0) { -- -+ if (nb_re_pusch > 0) -+ { - start_meas(&gNB->ulsch_rbs_extraction_stats); -- nr_ulsch_extract_rbs_single(gNB->common_vars.rxdataF, -- gNB->pusch_vars[ulsch_id], -- slot, -- symbol, -- dmrs_symbol_flag, -- rel15_ul, -- frame_parms); -+ nr_ulsch_extract_rbs(gNB->common_vars.rxdataF, -+ gNB->pusch_vars[ulsch_id], -+ slot, -+ symbol, -+ dmrs_symbol_flag, -+ rel15_ul, -+ frame_parms); - stop_meas(&gNB->ulsch_rbs_extraction_stats); - - //---------------------------------------------------------- -@@ -1280,8 +2083,9 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - gNB->ulsch[ulsch_id], - symbol, - dmrs_symbol_flag, -- rel15_ul->rb_size, -- rel15_ul->dmrs_config_type); -+ nb_re_pusch, -+ rel15_ul->nrOfLayers, -+ rel15_ul->rb_size); - - if (gNB->pusch_vars[ulsch_id]->cl_done==0) { - nr_ulsch_channel_level(gNB->pusch_vars[ulsch_id]->ul_ch_estimates_ext, -@@ -1312,9 +2116,14 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - gNB->pusch_vars[ulsch_id]->ul_ch_mag0, - gNB->pusch_vars[ulsch_id]->ul_ch_magb0, - gNB->pusch_vars[ulsch_id]->rxdataF_comp, -+ #ifdef SUPPORT_PMI_MATRIC - (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, -+ #else -+ NULL, -+ #endif - frame_parms, - symbol, -+ nb_re_pusch, - dmrs_symbol_flag, - rel15_ul->qam_mod_order, - rel15_ul->nrOfLayers, -@@ -1327,19 +2136,37 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - gNB->pusch_vars[ulsch_id]->rxdataF_comp, - gNB->pusch_vars[ulsch_id]->ul_ch_mag0, - gNB->pusch_vars[ulsch_id]->ul_ch_magb0, -+ #ifdef SUPPORT_PMI_MATRIC -+ (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, -+ #else -+ NULL, -+ #endif -+ rel15_ul->nrOfLayers, - symbol, -- rel15_ul->rb_size); -+ rel15_ul->rb_size, -+ nb_re_pusch); -+ -+ if (rel15_ul->nrOfLayers == 2)//Apply zero forcing for 2 Tx layers -+ nr_ulsch_zero_forcing_rx_2layers(gNB->pusch_vars[ulsch_id]->rxdataF_comp, -+ gNB->pusch_vars[ulsch_id]->ul_ch_mag0, -+ gNB->pusch_vars[ulsch_id]->ul_ch_magb0, -+ gNB->pusch_vars[ulsch_id]->ul_ch_estimates_ext, -+ rel15_ul->rb_size, -+ frame_parms->nb_antennas_rx, -+ rel15_ul->qam_mod_order, -+ gNB->pusch_vars[ulsch_id]->log2_maxh, -+ symbol, -+ nb_re_pusch); - stop_meas(&gNB->ulsch_mrc_stats); - -- // transform precoding = 0 means enabled -- if (rel15_ul->transform_precoding == 0) { -- -- #ifdef __AVX2__ -+ if (rel15_ul->transformPrecoder == transformPrecoder_enabled) -+ { -+ #ifdef __AVX2__ - // For odd number of resource blocks need byte alignment to multiple of 8 - int nb_re_pusch2 = nb_re_pusch + (nb_re_pusch&7); -- #else -+ #else - int nb_re_pusch2 = nb_re_pusch; -- #endif -+ #endif - - // perform IDFT operation on the compensated rxdata if transform precoding is enabled - nr_idft(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[0][symbol * nb_re_pusch2], nb_re_pusch); -@@ -1351,7 +2178,8 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - //---------------------------------------------------------- - /* In case PTRS is enabled then LLR will be calculated after PTRS symbols are processed * - * otherwise LLR are calculated for each symbol based upon DMRS channel estimates only. */ -- if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { -+ if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) -+ { - start_meas(&gNB->ulsch_ptrs_processing_stats); - nr_pusch_ptrs_processing(gNB, - frame_parms, -@@ -1370,14 +2198,17 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, - /*-------------------- LLRs computation -------------------------------------------------------------*/ - /*-----------------------------------------------------------------------------------------------------*/ - start_meas(&gNB->ulsch_llr_stats); -- nr_ulsch_compute_llr(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[0][symbol * (off + rel15_ul->rb_size * NR_NB_SC_PER_RB)], -- gNB->pusch_vars[ulsch_id]->ul_ch_mag0, -- gNB->pusch_vars[ulsch_id]->ul_ch_magb0, -- &gNB->pusch_vars[ulsch_id]->llr[rxdataF_ext_offset * rel15_ul->qam_mod_order], -- rel15_ul->rb_size, -- gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol], -- symbol, -- rel15_ul->qam_mod_order); -+ for (aatx=0; aatx < rel15_ul->nrOfLayers; aatx++) -+ { -+ nr_ulsch_compute_llr(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[aatx*frame_parms->nb_antennas_rx][symbol * (off + rel15_ul->rb_size * NR_NB_SC_PER_RB)], -+ gNB->pusch_vars[ulsch_id]->ul_ch_mag0[aatx*frame_parms->nb_antennas_rx], -+ gNB->pusch_vars[ulsch_id]->ul_ch_magb0[aatx*frame_parms->nb_antennas_rx], -+ &gNB->pusch_vars[ulsch_id]->llr_layers[aatx][rxdataF_ext_offset * rel15_ul->qam_mod_order], -+ rel15_ul->rb_size, -+ gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol], -+ symbol, -+ rel15_ul->qam_mod_order); -+ } - stop_meas(&gNB->ulsch_llr_stats); - rxdataF_ext_offset += gNB->pusch_vars[ulsch_id]->ul_valid_re_per_slot[symbol]; - } -diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c -index bf96a76106..ad41ebf3d6 100644 ---- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c -+++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c -@@ -64,7 +64,7 @@ void nr_ulsch_qpsk_llr(int32_t *rxdataF_comp, - //---------------------------------------------------------------------------------------------- - - void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, -- int32_t **ul_ch_mag, -+ int32_t *ul_ch_mag, - int16_t *ulsch_llr, - uint32_t nb_rb, - uint32_t nb_re, -@@ -110,12 +110,12 @@ void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, - - #if defined(__x86_64__) || defined(__i386__) - #ifdef __AVX2__ -- ch_mag = (__m256i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; -+ ch_mag = (__m256i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; - #else -- ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; -+ ch_mag = (__m128i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; - #endif - #elif defined(__arm__) -- ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*nb_rb*12)]; -+ ch_mag = (int16x8_t*)&ul_ch_mag[(symbol*nb_rb*12)]; - #endif - - #ifdef __AVX2__ -@@ -231,8 +231,8 @@ void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, - //---------------------------------------------------------------------------------------------- - - void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, -- int32_t **ul_ch_mag, -- int32_t **ul_ch_magb, -+ int32_t *ul_ch_mag, -+ int32_t *ul_ch_magb, - int16_t *ulsch_llr, - uint32_t nb_rb, - uint32_t nb_re, -@@ -265,15 +265,15 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, - - #if defined(__x86_64__) || defined(__i386__) - #ifdef __AVX2__ -- ch_mag = (__m256i*)&ul_ch_mag[0][(symbol*(off+(nb_rb*12)))]; -- ch_magb = (__m256i*)&ul_ch_magb[0][(symbol*(off+(nb_rb*12)))]; -+ ch_mag = (__m256i*)&ul_ch_mag[(symbol*(off+(nb_rb*12)))]; -+ ch_magb = (__m256i*)&ul_ch_magb[(symbol*(off+(nb_rb*12)))]; - #else -- ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*nb_rb*12)]; -- ch_magb = (__m128i*)&ul_ch_magb[0][(symbol*nb_rb*12)]; -+ ch_mag = (__m128i*)&ul_ch_mag[(symbol*nb_rb*12)]; -+ ch_magb = (__m128i*)&ul_ch_magb[(symbol*nb_rb*12)]; - #endif - #elif defined(__arm__) -- ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*nb_rb*12)]; -- ch_magb = (int16x8_t*)&ul_ch_magb[0][(symbol*nb_rb*12)]; -+ ch_mag = (int16x8_t*)&ul_ch_mag[(symbol*nb_rb*12)]; -+ ch_magb = (int16x8_t*)&ul_ch_magb[(symbol*nb_rb*12)]; - #endif - - #ifdef __AVX2__ -@@ -471,8 +471,8 @@ void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, - - - void nr_ulsch_compute_llr(int32_t *rxdataF_comp, -- int32_t **ul_ch_mag, -- int32_t **ul_ch_magb, -+ int32_t *ul_ch_mag, -+ int32_t *ul_ch_magb, - int16_t *ulsch_llr, - uint32_t nb_rb, - uint32_t nb_re, -diff --git a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c -index 65ae288252..2cb7609d62 100644 ---- a/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c -+++ b/openair1/PHY/NR_UE_ESTIMATION/filt16a_32.c -@@ -148,7 +148,7 @@ short filt8_dcr0_h[8]= { - 0,4096,8192,12288,16384,0,0,0}; - - short filt8_l1[8] = { --24576,16384,0,0,0,0,0,0}; -+24576,16384,8192,0,0,0,0,0}; - - short filt8_ml1[8] = { - -8192,0,8192,16384,8192,0,0,0}; -@@ -163,10 +163,10 @@ short filt8_mm1[8]= { - 0,0,0,0,8192,16384,8192,0}; - - short filt8_dcl1[8]= { --0,0,0,16384,12288,8192,4096,0}; -+0,0,16384,12288,8192,4096,0,0}; - - short filt8_dcr1[8]= { --0,0,0,0,4096,8192,12288,16384}; -+0,0,0,4096,8192,12288,16384,0}; - - short filt8_dcl1_h[8]= { - 0,16384,12288,8192,4096,0,0,0}; -diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c -index 945dc51673..a293eec6d8 100644 ---- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c -+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c -@@ -258,6 +258,15 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, - Ilbrm = 0; - Tbslbrm = 950984; //max tbs - Coderate = 0.0; -+ -+ #if 0 -+ harq_process->a[0] = 0x31; -+ for (int i = 1; i < harq_process->pusch_pdu.pusch_data.tb_size; i++) -+ { -+ harq_process->a[i] = (i&0xff); -+ } -+ #endif -+ - trace_NRpdu(DIRECTION_UPLINK, harq_process->a, harq_process->pusch_pdu.pusch_data.tb_size, 0, WS_C_RNTI, 0, 0, 0,0, 0); - /////////// - ///////////////////////////////////////////////////////////////////////////////////////// -diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c -index 99dc2b658e..5d76e654ba 100644 ---- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c -+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c -@@ -98,13 +98,12 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, - uint8_t thread_id, - int gNB_id) { - -- LOG_D(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); -+ LOG_I(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); - - uint32_t available_bits; - uint8_t cwd_index, l; - uint32_t scrambled_output[NR_MAX_NB_CODEWORDS][NR_MAX_PDSCH_ENCODED_LENGTH>>5]; -- int16_t **tx_layers; -- int32_t **txdataF; -+ - int8_t Wf[2], Wt[2], l_prime[2], delta; - uint8_t nb_dmrs_re_per_rb; - int ap, i; -@@ -112,6 +111,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, - - NR_DL_FRAME_PARMS *frame_parms = &UE->frame_parms; - NR_UE_PUSCH *pusch_ue = UE->pusch_vars[thread_id][gNB_id]; -+ int16_t **tx_layers = (int16_t **)pusch_ue->txdataF_layers; -+ int16_t **tx_precoding = (int16_t **)pusch_ue->txdataF_precoding; -+ int32_t **txdataF = UE->common_vars.txdataF; - - uint8_t num_of_codewords = 1; // tmp assumption - int Nid_cell = 0; -@@ -148,9 +150,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, - - nb_dmrs_re_per_rb = ((dmrs_type == pusch_dmrs_type1) ? 6:4)*cdm_grps_no_data; - -- LOG_D(PHY,"ulsch %x : start_rb %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", -- rnti,start_rb,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); -- -+ LOG_I(PHY,"ulsch TX %x : start_rb %d nb_rb %d mod_order %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", -+ rnti,start_rb,nb_rb,mod_order,Nl,pusch_pdu->Tpmi,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); -+ - // TbD num_of_mod_symbols is set but never used - N_RE_prime = NR_NB_SC_PER_RB*number_of_symbols - nb_dmrs_re_per_rb*number_dmrs_symbols - N_PRB_oh; - harq_process_ul_ue->num_of_mod_symbols = N_RE_prime*nb_rb*num_of_codewords; -@@ -236,8 +238,6 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, - /////////////////////////ULSCH layer mapping///////////////////////// - /////////// - -- tx_layers = (int16_t **)pusch_ue->txdataF_layers; -- - nr_ue_layer_mapping(UE->ulsch[thread_id][gNB_id], - Nl, - available_bits/mod_order, -@@ -257,7 +257,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, - int16_t *dmrs_seq = NULL; - - // if transform precoding is enbaled (value 0) -- if (pusch_pdu->transform_precoding == 0) { -+ if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { - - uint32_t nb_re_pusch=nb_rb * NR_NB_SC_PER_RB; - uint32_t y_offset = 0; -@@ -315,10 +315,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, - LOG_M("UE_DMRS_SEQ.m","UE_DMRS_SEQ", dmrs_seq,nb_re_pusch,1,1); - #endif - -- } -- else -- memcpy(ulsch_ue->y, tx_layers[0], (available_bits/mod_order)*sizeof(int32_t)); -- -+ } - - /////////// - //////////////////////////////////////////////////////////////////////// -@@ -328,145 +325,233 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, - /////////////////////////ULSCH RE mapping///////////////////////// - /////////// - -- txdataF = UE->common_vars.txdataF; -- -- for (ap=0; ap< Nl; ap++) { -- -- uint8_t k_prime = 0; -- uint16_t m = 0; -- -- -- #ifdef DEBUG_PUSCH_MAPPING -+ for (ap=0; ap< Nl; ap++) -+ { -+ uint8_t k_prime = 0; -+ uint16_t m = 0; -+ -+ #ifdef DEBUG_PUSCH_MAPPING - printf("NR_ULSCH_UE: Value of CELL ID %d /t, u %d \n", frame_parms->Nid_cell, u); -- #endif -- -- -- -- // DMRS params for this ap -- get_Wt(Wt, ap, dmrs_type); -- get_Wf(Wf, ap, dmrs_type); -- delta = get_delta(ap, dmrs_type); -- -- for (l=start_symbol; l> l) & 0x01) { -- is_dmrs_sym = 1; -- -- // transform precoding disabled (value 1) -- if (pusch_pdu->transform_precoding == 1){ -- -- if (dmrs_type == pusch_dmrs_type1) -- dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; -- else -- dmrs_idx = (pusch_pdu->bwp_start + start_rb)*4; -- -- // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part -- // Perform this on gold sequence, not required when SC FDMA operation is done, -- LOG_D(PHY,"DMRS in symbol %d\n",l); -- nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated -- -- } else { -- dmrs_idx = 0; -- } -- -- -- } else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { -- -- AssertFatal(pusch_pdu->transform_precoding == 1, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); -+ #endif - -- if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) { -- is_ptrs_sym = 1; -- nr_modulation(pusch_dmrs[l][0], nb_rb, DMRS_MOD_ORDER, mod_ptrs); -+ // DMRS params for this ap -+ get_Wt(Wt, ap, dmrs_type); -+ get_Wf(Wf, ap, dmrs_type); -+ delta = get_delta(ap, dmrs_type); -+ -+ for (l=start_symbol; l> l) & 0x01) -+ { -+ is_dmrs_sym = 1; -+ if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) -+ { -+ if (dmrs_type == pusch_dmrs_type1) -+ dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; -+ else -+ dmrs_idx = (pusch_pdu->bwp_start + start_rb)*4; -+ -+ // TODO: performance improvement, we can skip the modulation of DMRS symbols outside the bandwidth part -+ // Perform this on gold sequence, not required when SC FDMA operation is done, -+ LOG_D(PHY,"DMRS in symbol %d\n",l); -+ nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated -+ } -+ else -+ { -+ dmrs_idx = 0; -+ } -+ } -+ else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) -+ { -+ AssertFatal(pusch_pdu->transformPrecoder == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); -+ -+ if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) -+ { -+ is_ptrs_sym = 1; -+ nr_modulation(pusch_dmrs[l][0], nb_rb, DMRS_MOD_ORDER, mod_ptrs); -+ } - } -- } - -- for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) { -+ for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) -+ { -+ uint8_t is_dmrs = 0; -+ uint8_t is_ptrs = 0; - -- uint8_t is_dmrs = 0; -- uint8_t is_ptrs = 0; -+ sample_offsetF = l*frame_parms->ofdm_symbol_size + k; - -- sample_offsetF = l*frame_parms->ofdm_symbol_size + k; -- -- if (is_dmrs_sym) { -- if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) -- is_dmrs = 1; -- } else if (is_ptrs_sym) { -+ if (is_dmrs_sym) -+ { -+ if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) -+ is_dmrs = 1; -+ } -+ else if (is_ptrs_sym) -+ { - is_ptrs = is_ptrs_subcarrier(k, -- rnti, -- ap, -- dmrs_type, -- K_ptrs, -- nb_rb, -- pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset, -- start_sc, -- frame_parms->ofdm_symbol_size); -- } -- -- if (is_dmrs == 1) { -- // if transform precoding is enabled -- if (pusch_pdu->transform_precoding == 0) { -- -- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; -- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; -+ rnti, -+ ap, -+ dmrs_type, -+ K_ptrs, -+ nb_rb, -+ pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset, -+ start_sc, -+ frame_parms->ofdm_symbol_size); -+ } -+ -+ if (is_dmrs == 1) -+ { -+ if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) -+ { -+ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; -+ } -+ else -+ { -+ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; -+ } -+ -+ #ifdef DEBUG_PUSCH_MAPPING -+ printf("DMRS: Layer: %d\t, dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", -+ ap, dmrs_idx, l, k, k_prime, n, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); -+ #endif -+ -+ dmrs_idx++; -+ k_prime++; -+ k_prime&=1; -+ n+=(k_prime)?0:1; -+ } -+ else if (is_ptrs == 1) -+ { -+ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; -+ -+ ptrs_idx++; -+ } -+ else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) -+ { -+ if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) -+ { -+ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[ap])[m<<1]; -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[ap])[(m<<1) + 1]; -+ } -+ else -+ { -+ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; -+ } -+ -+ #ifdef DEBUG_PUSCH_MAPPING -+ printf("DATA: layer %d\t m %d\t l %d \t k %d \t txdataF: %d %d\n", -+ ap, m, l, k, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); -+ #endif -+ -+ m++; -+ } -+ else -+ { -+ ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = 0; -+ ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = 0; -+ } -+ -+ if (++k >= frame_parms->ofdm_symbol_size) -+ { -+ k -= frame_parms->ofdm_symbol_size; -+ } -+ } //for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) -+ }//for (l=start_symbol; lnb_antennas_tx; ap++) -+ { -+ for (int l=start_symbol; lTpmi; - -- } else { -- -- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; -- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; -- -+ if (pmi == 0) {//unitary Precoding -+ if(ap< pusch_pdu->nrOfLayers) -+ memcpy((void*)&txdataF[ap][l*frame_parms->ofdm_symbol_size + k], -+ (void*)&tx_precoding[ap][2*(l*frame_parms->ofdm_symbol_size + k)], -+ NR_NB_SC_PER_RB*sizeof(int32_t)); -+ else -+ memset((void*)&txdataF[ap][l*frame_parms->ofdm_symbol_size + k], -+ 0, -+ NR_NB_SC_PER_RB*sizeof(int32_t)); -+ -+ k += NR_NB_SC_PER_RB; -+ if (k >= frame_parms->ofdm_symbol_size) { -+ k -= frame_parms->ofdm_symbol_size; -+ } -+ } -+ else -+ { -+ //get the precoding matrix weights: -+ char *W_prec; -+ switch (frame_parms->nb_antennas_tx) -+ { -+ case 1://1 antenna port -+ W_prec = nr_W_1l_2p[pmi][ap]; -+ break; -+ case 2://2 antenna ports -+ if (pusch_pdu->nrOfLayers == 1)//1 layer -+ W_prec = nr_W_1l_2p[pmi][ap]; -+ else//2 layers -+ W_prec = nr_W_2l_2p[pmi][ap]; -+ break; -+ case 4://4 antenna ports -+ if (pusch_pdu->nrOfLayers == 1)//1 layer -+ W_prec = nr_W_1l_4p[pmi][ap]; -+ else if (pusch_pdu->nrOfLayers == 2)//2 layers -+ W_prec = nr_W_2l_4p[pmi][ap]; -+ else if (pusch_pdu->nrOfLayers == 3)//3 layers -+ W_prec = nr_W_3l_4p[pmi][ap]; -+ else//4 layers -+ W_prec = nr_W_4l_4p[pmi][ap]; -+ break; -+ default: -+ LOG_D(PHY,"Precoding 1,2, or 4 antenna ports are currently supported\n"); -+ W_prec = nr_W_1l_2p[pmi][ap]; -+ break; - } - -- #ifdef DEBUG_PUSCH_MAPPING -- printf("dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", -- dmrs_idx, l, k, k_prime, n, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], -- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1]); -- #endif -- -- -- dmrs_idx++; -- k_prime++; -- k_prime&=1; -- n+=(k_prime)?0:1; -- -- } else if (is_ptrs == 1) { -- -- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; -- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; -- -- ptrs_idx++; -- -- } else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) { -- -- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; -- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; -- -- #ifdef DEBUG_PUSCH_MAPPING -- printf("m %d\t l %d \t k %d \t txdataF: %d %d\n", -- m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], -- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1]); -- #endif -- -- m++; -- -- } else { -- -- ((int16_t*)txdataF[ap])[(sample_offsetF)<<1] = 0; -- ((int16_t*)txdataF[ap])[((sample_offsetF)<<1) + 1] = 0; -- -- } -+ for (int i=0; iofdm_symbol_size + k; -+ int32_t precodatatx_F = nr_layer_precoder(tx_precoding, W_prec, pusch_pdu->nrOfLayers, re_offset); -+ ((int16_t*)txdataF[ap])[(re_offset<<1)] = ((int16_t *) &precodatatx_F)[0]; -+ ((int16_t*)txdataF[ap])[(re_offset<<1) + 1] = ((int16_t *) &precodatatx_F)[1]; -+ -+ if (++k >= frame_parms->ofdm_symbol_size) -+ { -+ k -= frame_parms->ofdm_symbol_size; -+ } -+ } -+ } -+ } //RB loop -+ } // symbol loop -+ }// port loop - -- if (++k >= frame_parms->ofdm_symbol_size) -- k -= frame_parms->ofdm_symbol_size; -- } -- } -- } -- } -+ } //for (cwd_index = 0;cwd_index < num_of_codewords; cwd_index++) - - NR_UL_UE_HARQ_t *harq_process_ulsch=NULL; - harq_process_ulsch = UE->ulsch[thread_id][gNB_id][0]->harq_processes[harq_pid]; -diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h -index 11b5112a6c..600ba07cb3 100644 ---- a/openair1/PHY/defs_gNB.h -+++ b/openair1/PHY/defs_gNB.h -@@ -479,7 +479,7 @@ typedef struct { - /// \brief Cross-correlation of two UE signals. - /// - first index: rx antenna [0..nb_antennas_rx[ - /// - second index: symbol [0..] -- int32_t **rho; -+ int32_t ***rho; - /// \f$\log_2(\max|H_i|^2)\f$ - int16_t log2_maxh; - /// \brief Magnitude of Uplink Channel first layer (16QAM level/First 64QAM level). -@@ -509,6 +509,10 @@ typedef struct { - /// \brief llr values. - /// - first index: ? [0..1179743] (hard coded) - int16_t *llr; -+ /// \brief llr values per layer. -+ /// - first index: ? [0..3] (hard coded) -+ /// - first index: ? [0..1179743] (hard coded) -+ int16_t **llr_layers; - /// DMRS symbol index, to be updated every DMRS symbol within a slot. - uint8_t dmrs_symbol; - // PTRS symbol index, to be updated every PTRS symbol within a slot. -diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h -index 69978f5548..02988b590a 100644 ---- a/openair1/PHY/defs_nr_UE.h -+++ b/openair1/PHY/defs_nr_UE.h -@@ -229,6 +229,9 @@ typedef struct { - typedef struct { - /// TX buffers for multiple layers - int32_t *txdataF_layers[NR_MAX_NB_LAYERS]; -+ -+ /// TX buffers for UE-spec transmission -+ int32_t *txdataF_precoding[NR_MAX_NB_LAYERS]; - } NR_UE_PUSCH; - - typedef struct { -diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h -index c385d323e3..fbe094f41c 100644 ---- a/openair1/PHY/defs_nr_common.h -+++ b/openair1/PHY/defs_nr_common.h -@@ -93,7 +93,7 @@ - #define NR_MAX_CSET_DURATION 3 - - #define NR_MAX_NB_RBG 18 --#define NR_MAX_NB_LAYERS 2 // 8 // SU-MIMO (3GPP TS 38.211 V15.4.0 section 7.3.1.3) -+#define NR_MAX_NB_LAYERS 4 // 8 // SU-MIMO (3GPP TS 38.211 V15.4.0 section 7.3.1.3) - #define NR_MAX_NB_CODEWORDS 2 - #define NR_MAX_NB_HARQ_PROCESSES 16 - #define NR_MAX_PDSCH_ENCODED_LENGTH (NR_MAX_NB_RB*NR_SYMBOLS_PER_SLOT*NR_NB_SC_PER_RB*8*NR_MAX_NB_LAYERS) // 8 is the maximum modulation order (it was 950984 before !!) -@@ -104,11 +104,11 @@ - #define MAX_NUM_NR_DLSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) - #define MAX_NR_DLSCH_PAYLOAD_BYTES (MAX_NUM_NR_DLSCH_SEGMENTS*1056) - --#define MAX_NUM_NR_ULSCH_SEGMENTS 34 -+#define MAX_NUM_NR_ULSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) - #define MAX_NR_ULSCH_PAYLOAD_BYTES (MAX_NUM_NR_ULSCH_SEGMENTS*1056) - --#define MAX_NUM_NR_CHANNEL_BITS (14*273*12*8) // 14 symbols, 273 RB --#define MAX_NUM_NR_RE (14*273*12) -+#define MAX_NUM_NR_CHANNEL_BITS (4*14*273*12*8) // 14 symbols, 273 RB -+#define MAX_NUM_NR_RE (4*14*273*12) - #define NR_RX_NB_TH 1 - #define NR_NB_TH_SLOT 2 - -diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c -index d8fa11d157..640a0740ef 100644 ---- a/openair1/SCHED_NR/nr_ru_procedures.c -+++ b/openair1/SCHED_NR/nr_ru_procedures.c -@@ -526,7 +526,8 @@ void nr_fep0(RU_t *ru, int first_half) { - end_symbol = NR_SYMBOLS_PER_SLOT; - } - -- LOG_D(PHY,"In fep0 for slot = %d, first_half = %d, start_symbol = %d, end_symbol = %d\n", proc->tti_rx, first_half, start_symbol, end_symbol); -+ LOG_D(PHY,"In fep0 for slot = %d, first_half = %d, start_symbol = %d, end_symbol = %d, nb_antennas_rx = %d,N_TA_offset = %d\n", -+ proc->tti_rx, first_half, start_symbol, end_symbol,fp->nb_antennas_rx,ru->N_TA_offset); - // printf("fep0: slot %d\n",slot); - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX+proc->tti_rx, 1); -diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c -index 1685e206ae..63c708e565 100644 ---- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c -+++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c -@@ -253,13 +253,13 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { - // if all segments are done - if (rdata->nbSegments == ulsch_harq->processedSegments) { - if (decodeSuccess) { -- LOG_D(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", -+ LOG_I(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", - gNB->Mod_id,ulsch_harq->frame,ulsch_harq->slot,rdata->harq_pid,pusch_pdu->pusch_data.new_data_indicator,ulsch_harq->status,ulsch_harq->round,ulsch_harq->TBS,rdata->decodeIterations); - ulsch_harq->status = SCH_IDLE; - ulsch_harq->round = 0; - ulsch->harq_mask &= ~(1 << rdata->harq_pid); - -- LOG_D(PHY, "ULSCH received ok \n"); -+ LOG_I(PHY, "ULSCH received ok \n"); - nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 0,0); - //dumpsig=1; - } else { -@@ -281,7 +281,7 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { - } - ulsch_harq->handled = 1; - -- LOG_D(PHY, "ULSCH %d in error\n",rdata->ulsch_id); -+ LOG_I(PHY, "ULSCH %d in error\n",rdata->ulsch_id); - nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 1,0); - } - /* -@@ -365,6 +365,14 @@ void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH - number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs - pusch_pdu->qam_mod_order, - pusch_pdu->nrOfLayers); -+ -+ -+ nr_ulsch_layer_demapping(gNB->pusch_vars[ULSCH_id]->llr, -+ pusch_pdu->nrOfLayers, -+ pusch_pdu->qam_mod_order, -+ G, -+ gNB->pusch_vars[ULSCH_id]->llr_layers); -+ - //---------------------------------------------------------- - //------------------- ULSCH unscrambling ------------------- - //---------------------------------------------------------- -@@ -731,7 +739,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { - (ulsch_harq->slot == slot_rx) && - (ulsch_harq->handled == 0)){ - -- LOG_D(PHY, "PUSCH detection started in frame %d slot %d\n", -+ LOG_I(PHY, "PUSCH detection started in frame %d slot %d\n", - frame_rx,slot_rx); - int num_dmrs=0; - for (int s=0;srx_pusch_stats); - no_sig = nr_rx_pusch(gNB, ULSCH_id, frame_rx, slot_rx, harq_pid); - if (no_sig) { -- LOG_D(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); -+ LOG_I(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); - nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, harq_pid, 1,1); - return 1; - } -@@ -783,7 +791,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { - dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot) + gNB->pusch_thres) { - NR_gNB_SCH_STATS_t *stats=get_ulsch_stats(gNB,ulsch); - -- LOG_D(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, -+ LOG_I(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, - dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot), - dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot),gNB->pusch_thres); - gNB->pusch_vars[ULSCH_id]->ulsch_power_tot = gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot; -diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c -index f6e4d90bee..8e1df435c3 100644 ---- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c -+++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c -@@ -157,7 +157,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ - for (i = 0; i < ul_config->number_pdus; ++i){ - - AssertFatal(ul_config->ul_config_list[i].pdu_type <= FAPI_NR_UL_CONFIG_TYPES,"pdu_type %d out of bounds\n",ul_config->ul_config_list[i].pdu_type); -- LOG_D(PHY, "In %s: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); -+ LOG_I(PHY, "In %s i %d: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, i, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); - - uint8_t pdu_type = ul_config->ul_config_list[i].pdu_type, current_harq_pid, gNB_id = 0; - /* PRACH */ -@@ -180,6 +180,8 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ - if (harq_process_ul_ue){ - - nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &harq_process_ul_ue->pusch_pdu; -+ -+ LOG_I(PHY, "In %s i %d: copy pusch_config_pdu nrOfLayers:%d, num_dmrs_cdm_grps_no_data:%d \n", __FUNCTION__, i, pusch_config_pdu->nrOfLayers,pusch_config_pdu->num_dmrs_cdm_grps_no_data); - - memcpy(pusch_pdu, pusch_config_pdu, sizeof(nfapi_nr_ue_pusch_pdu_t)); - -diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c -index 940931694c..0a0a9cc2f9 100644 ---- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c -+++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c -@@ -270,9 +270,12 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, - AssertFatal(ue->CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX,VCD_FUNCTION_IN); -- -- memset(ue->common_vars.txdataF[0], 0, sizeof(int)*14*ue->frame_parms.ofdm_symbol_size); -- -+ -+ for(int i=0; i< ue->frame_parms.nb_antennas_tx; ++i) -+ { -+ memset(ue->common_vars.txdataF[i], 0, sizeof(int)*14*ue->frame_parms.ofdm_symbol_size); -+ } -+ - LOG_D(PHY,"****** start TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx); - - -diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c -index 1eb6027936..d5a1054654 100644 ---- a/openair1/SIMULATION/NR_PHY/dlsim.c -+++ b/openair1/SIMULATION/NR_PHY/dlsim.c -@@ -768,7 +768,7 @@ int main(int argc, char **argv) - - prepare_scd(scd); - -- fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, 0, 0, 0); -+ fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, n_rx, 0, 0, 0); - - /* RRC parameter validation for secondaryCellGroup */ - fix_scd(scd); -diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c -index 94aea36a67..fb8b4295ae 100644 ---- a/openair1/SIMULATION/NR_PHY/ulschsim.c -+++ b/openair1/SIMULATION/NR_PHY/ulschsim.c -@@ -137,6 +137,7 @@ int main(int argc, char **argv) - uint16_t nb_symb_sch = 12; - uint16_t nb_rb = 50; - uint8_t Imcs = 9; -+ uint8_t Nl = 1; - - double DS_TDL = .03; - -@@ -150,7 +151,7 @@ int main(int argc, char **argv) - randominit(0); - - //while ((c = getopt(argc, argv, "df:hpg:i:j:n:l:m:r:s:S:y:z:M:N:F:R:P:")) != -1) { -- while ((c = getopt(argc, argv, "hg:n:s:S:py:z:M:N:R:F:m:l:r:")) != -1) { -+ while ((c = getopt(argc, argv, "hg:n:s:S:py:z:M:N:R:F:m:l:r:W:")) != -1) { - switch (c) { - /*case 'f': - write_output_file = 1; -@@ -251,7 +252,7 @@ int main(int argc, char **argv) - case 'y': - n_tx = atoi(optarg); - -- if ((n_tx == 0) || (n_tx > 2)) { -+ if ((n_tx == 0) || (n_tx > 4)) { - printf("Unsupported number of TX antennas %d. Exiting.\n", n_tx); - exit(-1); - } -@@ -261,7 +262,7 @@ int main(int argc, char **argv) - case 'z': - n_rx = atoi(optarg); - -- if ((n_rx == 0) || (n_rx > 2)) { -+ if ((n_rx == 0) || (n_rx > 4)) { - printf("Unsupported number of RX antennas %d. Exiting.\n", n_rx); - exit(-1); - } -@@ -299,6 +300,10 @@ int main(int argc, char **argv) - printf("Illegal PBCH phase (0-3) got %d\n", pbch_phase); - break;*/ - -+ case 'W': -+ Nl = atoi(optarg); -+ break; -+ - case 'm': - Imcs = atoi(optarg); - #ifdef DEBUG_NR_ULSCHSIM -@@ -335,13 +340,14 @@ int main(int argc, char **argv) - printf("-z Number of RX antennas used in UE\n"); - //printf("-i Relative strength of first intefering eNB (in dB) - cell_id mod 3 = 1\n"); - //printf("-j Relative strength of second intefering eNB (in dB) - cell_id mod 3 = 2\n"); -+ printf("-W number of layer\n"); - printf("-M Multiple SSB positions in burst\n"); - printf("-N Nid_cell\n"); - printf("-R N_RB_UL\n"); - printf("-F Input filename (.txt format) for RX conformance testing\n"); -- printf("-m\n"); -- printf("-l\n"); -- printf("-r\n"); -+ printf("-m MCS\n"); -+ printf("-l number of symbol\n"); -+ printf("-r number of RB\n"); - //printf("-O oversampling factor (1,2,4,8,16)\n"); - //printf("-A Interpolation_filname Run with Abstraction to generate Scatter plot using interpolation polynomial in file\n"); - //printf("-C Generate Calibration information for Abstraction (effective SNR adjustment to remove Pe bias w.r.t. AWGN)\n"); -@@ -359,8 +365,8 @@ int main(int argc, char **argv) - snr1 = snr0 + 10; - - gNB2UE = new_channel_desc_scm(n_tx, -- n_rx, -- channel_model, -+ n_rx, -+ channel_model, - 61.44e6, //N_RB2sampling_rate(N_RB_DL), - 40e6, //N_RB2channel_bandwidth(N_RB_DL), - DS_TDL, -@@ -426,8 +432,7 @@ int main(int argc, char **argv) - uint8_t length_dmrs = 1; - uint8_t N_PRB_oh; - uint16_t N_RE_prime,code_rate; -- unsigned char mod_order; -- uint8_t Nl = 1; -+ unsigned char mod_order; - uint8_t rvidx = 0; - uint8_t UE_id = 0; - -@@ -437,9 +442,14 @@ int main(int argc, char **argv) - - NR_UE_ULSCH_t *ulsch_ue = UE->ulsch[0][0][0]; - -+ if ((Nl==4)||(Nl==3)) -+ { -+ nb_re_dmrs = nb_re_dmrs*2; -+ } -+ - mod_order = nr_get_Qm_ul(Imcs, 0); - code_rate = nr_get_code_rate_ul(Imcs, 0); -- available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, 1); -+ available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, Nl); - TBS = nr_compute_tbs(mod_order,code_rate, nb_rb, nb_symb_sch, nb_re_dmrs*length_dmrs, 0, 0, Nl); - - printf("\nAvailable bits %u TBS %u mod_order %d\n", available_bits, TBS, mod_order); -diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c -index 69316d6b88..b6cfae3fe1 100644 ---- a/openair1/SIMULATION/NR_PHY/ulsim.c -+++ b/openair1/SIMULATION/NR_PHY/ulsim.c -@@ -282,7 +282,7 @@ int main(int argc, char **argv) - int gNB_id = 0; - int ap; - int tx_offset; -- int32_t txlev=0; -+ int32_t txlev_sum = 0, atxlev[4]; - int start_rb = 0; - int UE_id =0; // [hna] only works for UE_id = 0 because NUMBER_OF_NR_UE_MAX is set to 1 (phy_init_nr_gNB causes segmentation fault) - float target_error_rate = 0.01; -@@ -307,7 +307,7 @@ int main(int argc, char **argv) - uint16_t ptrsSymbPerSlot = 0; - uint16_t ptrsRePerSymb = 0; - -- uint8_t transform_precoding = 1; // 0 - ENABLE, 1 - DISABLE -+ uint8_t transformPrecoder = transformPrecoder_disabled; // 0 - ENABLE, 1 - DISABLE - uint8_t num_dmrs_cdm_grps_no_data = 1; - uint8_t mcs_table = 0; - -@@ -329,7 +329,7 @@ int main(int argc, char **argv) - /* initialize the sin-cos table */ - InitSinLUT(); - -- while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:ikl:m:n:p:r:s:u:w:y:z:F:G:H:M:N:PR:S:T:U:L:Z")) != -1) { -+ while ((c = getopt(argc, argv, "a:b:c:d:ef:g:h:i:kl:m:n:p:r:s:u:w:y:z:F:G:H:M:N:PR:S:T:U:L:Z:W:")) != -1) { - printf("handling optarg %c\n",c); - switch (c) { - -@@ -363,8 +363,8 @@ int main(int argc, char **argv) - scg_fd = fopen(optarg, "r"); - - if (scg_fd == NULL) { -- printf("Error opening %s\n", optarg); -- exit(-1); -+ printf("Error opening %s\n", optarg); -+ exit(-1); - } - - break; -@@ -437,6 +437,10 @@ int main(int argc, char **argv) - case 'm': - Imcs = atoi(optarg); - break; -+ -+ case 'W': -+ precod_nbr_layers = atoi(optarg); -+ break; - - case 'n': - n_trials = atoi(optarg); -@@ -485,9 +489,9 @@ int main(int argc, char **argv) - case 'y': - n_tx = atoi(optarg); - -- if ((n_tx == 0) || (n_tx > 2)) { -- printf("Unsupported number of tx antennas %d\n", n_tx); -- exit(-1); -+ if ((n_tx == 0) || (n_tx > 4)) { -+ printf("Unsupported number of tx antennas %d\n", n_tx); -+ exit(-1); - } - - break; -@@ -496,8 +500,8 @@ int main(int argc, char **argv) - n_rx = atoi(optarg); - - if ((n_rx == 0) || (n_rx > 8)) { -- printf("Unsupported number of rx antennas %d\n", n_rx); -- exit(-1); -+ printf("Unsupported number of rx antennas %d\n", n_rx); -+ exit(-1); - } - - break; -@@ -506,8 +510,8 @@ int main(int argc, char **argv) - input_fd = fopen(optarg, "r"); - - if (input_fd == NULL) { -- printf("Problem with filename %s\n", optarg); -- exit(-1); -+ printf("Problem with filename %s\n", optarg); -+ exit(-1); - } - - break; -@@ -568,11 +572,11 @@ int main(int argc, char **argv) - - case 'Z': - -- transform_precoding = 0; // enabled -+ transformPrecoder = transformPrecoder_enabled; - num_dmrs_cdm_grps_no_data = 2; - mcs_table = 3; - -- printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transform_precoding); -+ printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transformPrecoder); - - break; - -@@ -611,6 +615,7 @@ int main(int argc, char **argv) - printf("-U Change DMRS Config, arguments list DMRS TYPE{0=A,1=B} DMRS AddPos{0:3}, e.g. -U 2 0 2 \n"); - printf("-Q If -F used, read parameters from file\n"); - printf("-Z If -Z is used, SC-FDMA or transform precoding is enabled in Uplink \n"); -+ printf("-W Num of layer for PUSCH\n"); - exit(-1); - break; - -@@ -713,7 +718,7 @@ int main(int argc, char **argv) - - prepare_scd(scd); - -- fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, 0, 0, 0); -+ fill_default_secondaryCellGroup(scc, scd, secondaryCellGroup, 0, 1, n_tx, n_rx, 0, 0, 0); - - // xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)secondaryCellGroup); - -@@ -724,9 +729,9 @@ int main(int argc, char **argv) - - gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; - // common configuration -- rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, scc, &rrc.carrier.mib,0, 0, NULL); -+ rrc_mac_config_req_gNB(0,0, n_tx, n_rx, n_tx, scc, &rrc.carrier.mib,0, 0, NULL); - // UE dedicated configuration -- rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); -+ rrc_mac_config_req_gNB(0,0, n_tx, n_rx, n_tx, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); - frame_parms->nb_antennas_tx = n_tx; - frame_parms->nb_antennas_rx = n_rx; - nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; -@@ -870,14 +875,18 @@ int main(int argc, char **argv) - uint16_t number_dmrs_symbols = get_dmrs_symbols_in_slot(l_prime_mask, nb_symb_sch); - printf("num dmrs sym %d\n",number_dmrs_symbols); - uint8_t nb_re_dmrs = (dmrs_config_type == pusch_dmrs_type1) ? 6 : 4; -- -- // if transform precoding is enabled -- if (transform_precoding == 0) { -+ -+ if ((frame_parms->nb_antennas_tx==4)&&(precod_nbr_layers==4)) -+ { -+ num_dmrs_cdm_grps_no_data = 2; -+ } -+ -+ if (transformPrecoder == transformPrecoder_enabled) { - - AssertFatal(enable_ptrs == 0, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); - - int8_t index = get_index_for_dmrs_lowpapr_seq((NR_NB_SC_PER_RB/2) * nb_rb); -- AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); -+ AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); - - dmrs_config_type = pusch_dmrs_type1; - -@@ -886,7 +895,7 @@ int main(int argc, char **argv) - - nb_re_dmrs = nb_re_dmrs * num_dmrs_cdm_grps_no_data; - -- unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, mod_order, 1); -+ unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, mod_order, precod_nbr_layers); - unsigned int TBS = nr_compute_tbs(mod_order, code_rate, nb_rb, nb_symb_sch, nb_re_dmrs * number_dmrs_symbols, 0, 0, precod_nbr_layers); - - -@@ -1082,14 +1091,14 @@ int main(int argc, char **argv) - pusch_pdu->mcs_table = mcs_table; - pusch_pdu->target_code_rate = code_rate; - pusch_pdu->qam_mod_order = mod_order; -- pusch_pdu->transform_precoding = transform_precoding; -+ pusch_pdu->transformPrecoder = transformPrecoder; - pusch_pdu->data_scrambling_id = *scc->physCellId; -- pusch_pdu->nrOfLayers = 1; -+ pusch_pdu->nrOfLayers = precod_nbr_layers; - pusch_pdu->ul_dmrs_symb_pos = l_prime_mask; - pusch_pdu->dmrs_config_type = dmrs_config_type; - pusch_pdu->ul_dmrs_scrambling_id = *scc->physCellId; - pusch_pdu->scid = 0; -- pusch_pdu->dmrs_ports = 1; -+ pusch_pdu->dmrs_ports = ((1<num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; - pusch_pdu->resource_alloc = 1; - pusch_pdu->rb_start = start_rb; -@@ -1109,7 +1118,7 @@ int main(int argc, char **argv) - pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; - - // if transform precoding is enabled -- if (transform_precoding == 0) { -+ if (transformPrecoder == transformPrecoder_enabled) { - - pusch_pdu->dfts_ofdm.low_papr_group_number = *scc->physCellId % 30; // U as defined in 38.211 section 6.4.1.1.1.2 - pusch_pdu->dfts_ofdm.low_papr_sequence_number = 0; // V as defined in 38.211 section 6.4.1.1.1.2 -@@ -1167,10 +1176,10 @@ int main(int argc, char **argv) - ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list = (nfapi_nr_ue_ptrs_ports_t *) malloc(2*sizeof(nfapi_nr_ue_ptrs_ports_t)); - ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; - -- ul_config.ul_config_list[0].pusch_config_pdu.transform_precoding = transform_precoding; -+ ul_config.ul_config_list[0].pusch_config_pdu.transformPrecoder = transformPrecoder; - - // if transform precoding is enabled -- if (transform_precoding == 0) { -+ if (transformPrecoder == transformPrecoder_enabled) { - - ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_group_number = *scc->physCellId % 30;// U as defined in 38.211 section 6.4.1.1.1.2 - ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_sequence_number = 0;// V as defined in 38.211 section 6.4.1.1.1.2 -@@ -1198,28 +1207,54 @@ int main(int argc, char **argv) - LOG_D(PHY, "Sending Uplink data \n"); - nr_ue_pusch_common_procedures(UE, - slot, -- &UE->frame_parms,1); -- -- if (n_trials==1) { -- LOG_M("txsig0.m","txs0", UE->common_vars.txdata[0],frame_parms->samples_per_subframe*10,1,1); -+ &UE->frame_parms, -+ frame_parms->nb_antennas_tx); -+ -+ -+ if (n_trials==1) -+ { -+ LOG_M("txsig0.m","txs0", &UE->common_vars.txdata[0][slot_offset],slot_length,1,1); - LOG_M("txsig0F.m","txs0F", UE->common_vars.txdataF[0],frame_parms->ofdm_symbol_size*14,1,1); -+ if (precod_nbr_layers > 1) -+ { -+ LOG_M("txsig1.m","txs1", &UE->common_vars.txdata[1][slot_offset],slot_length,1,1); -+ LOG_M("txsig1F.m","txs1F", UE->common_vars.txdataF[1],frame_parms->ofdm_symbol_size*14,1,1); -+ if (precod_nbr_layers==4) -+ { -+ LOG_M("txsig2.m","txs2", &UE->common_vars.txdata[2][slot_offset],slot_length,1,1); -+ LOG_M("txsig3.m","txs3", &UE->common_vars.txdata[3][slot_offset],slot_length,1,1); -+ -+ LOG_M("txsig2F.m","txs2F", UE->common_vars.txdataF[2],frame_parms->ofdm_symbol_size*14,1,1); -+ LOG_M("txsig3F.m","txs3F", UE->common_vars.txdataF[3],frame_parms->ofdm_symbol_size*14,1,1); -+ } -+ } - } - /////////// - //////////////////////////////////////////////////// - tx_offset = frame_parms->get_samples_slot_timestamp(slot,frame_parms,0); -- -- txlev = signal_energy(&UE->common_vars.txdata[0][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], -+ txlev_sum = 0; -+ for (int aa=0; aanb_antennas_tx; aa++) -+ { -+ atxlev[aa] = signal_energy(&UE->common_vars.txdata[aa][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], - frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples); -- } -- else n_trials = 1; -- -- if (input_fd == NULL ) { -+ -+ txlev_sum += atxlev[aa]; -+ -+ if (n_trials==1) printf("txlev[%d] = %d (%f dB) txlev_sum %d\n",aa,atxlev[aa],10*log10((double)atxlev[aa]),txlev_sum); -+ } -+ } -+ else -+ { -+ n_trials = 1; -+ } - -- sigma_dB = 10 * log10((double)txlev * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; -+ if (input_fd == NULL ) -+ { -+ sigma_dB = 10 * log10((double)txlev_sum * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; - sigma = pow(10,sigma_dB/10); - - -- if(n_trials==1) printf("sigma %f (%f dB), txlev %f (factor %f)\n",sigma,sigma_dB,10*log10((double)txlev),(double)(double) -+ if(n_trials==1) printf("sigma %f (%f dB), txlev_sum %f (factor %f)\n",sigma,sigma_dB,10*log10((double)txlev_sum),(double)(double) - frame_parms->ofdm_symbol_size/(12*nb_rb)); - - for (i=0; imax_Doppler == 0) { -+ if (UE2gNB->max_Doppler == 0) -+ { - multipath_channel(UE2gNB, s_re, s_im, r_re, r_im, slot_length, 0, (n_trials==1)?1:0); - } else { - multipath_tv_channel(UE2gNB, s_re, s_im, r_re, r_im, 2*slot_length, 0); - } -- for (i=0; inb_antennas_rx; ap++) { -+ for (i=0; inb_antennas_rx; ap++) -+ { -+ if (channel_model == AWGN) -+ { -+ double H_awgn[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 -+ {0.0, 1.0, 0.0, 0.0}, //rx 1 -+ {0.0, 0.0, 1.0, 0.0}, //rx 2 -+ {0.0, 0.0, 0.0, 1.0}};//rx 3 -+ #if 0 -+ double H_awgn_mimo[4][4] ={{1.0, 0.5, 0.25, 0.125},//rx 0 -+ {0.5, 1.0, 0.5, 0.25}, //rx 1 -+ {0.25, 0.5, 1.0, 0.5}, //rx 2 -+ {0.125, 0.25, 0.5, 1.0}};//rx 3 -+ #endif -+ // sum up signals from different Tx antennas -+ r_re[ap][i] = 0; -+ r_im[ap][i] = 0; -+ -+ for (int aa=0; aaofdm_symbol_size/(12*nb_rb))) - SNR;; -+ sigma = pow(10,sigma_dB/10); -+ - ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); // convert to fixed point - ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); -+ -+ #if 0 -+ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i])); // convert to fixed point -+ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i])); -+ -+ -+ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) ] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i) ]; // convert to fixed point -+ ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i)+1 ]; -+ #endif -+ - /* Add phase noise if enabled */ - if (pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { - phase_noise(ts, &((int16_t*)&gNB->common_vars.rxdata[ap][slot_offset])[(2*i)], -@@ -1250,7 +1324,8 @@ int main(int argc, char **argv) - } /*End input_fd */ - - -- if(pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { -+ if(pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) -+ { - set_ptrs_symb_idx(&ptrsSymPos, - pusch_pdu->nr_of_symbols, - pusch_pdu->start_symbol_index, -@@ -1268,24 +1343,37 @@ int main(int argc, char **argv) - gNB->UL_INFO.rx_ind.number_of_pdus = 0; - gNB->UL_INFO.crc_ind.number_crcs = 0; - -- phy_procedures_gNB_common_RX(gNB, frame, slot); -+ phy_procedures_gNB_common_RX(gNB, frame, slot); - -- ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot); -+ ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot); - -- if (n_trials==1 && round==0) { -+ if (n_trials==1 && round==0) -+ { - LOG_M("rxsig0.m","rx0",&gNB->common_vars.rxdata[0][slot_offset],slot_length,1,1); - -- LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0]+start_symbol*frame_parms->ofdm_symbol_size,nb_symb_sch*frame_parms->ofdm_symbol_size,1,1); -- -+ LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0],14*frame_parms->ofdm_symbol_size,1,1); -+ if (precod_nbr_layers > 1) -+ { -+ LOG_M("rxsig1.m","rx1",&gNB->common_vars.rxdata[1][slot_offset],slot_length,1,1); -+ LOG_M("rxsigF1.m","rxsF1",gNB->common_vars.rxdataF[1],14*frame_parms->ofdm_symbol_size,1,1); -+ if (precod_nbr_layers==4) -+ { -+ LOG_M("rxsig2.m","rx2",&gNB->common_vars.rxdata[2][slot_offset],slot_length,1,1); -+ LOG_M("rxsig3.m","rx3",&gNB->common_vars.rxdata[3][slot_offset],slot_length,1,1); -+ -+ LOG_M("rxsigF2.m","rxsF2",gNB->common_vars.rxdataF[2],14*frame_parms->ofdm_symbol_size,1,1); -+ LOG_M("rxsigF3.m","rxsF3",gNB->common_vars.rxdataF[3],14*frame_parms->ofdm_symbol_size,1,1); -+ } -+ } - } - - - if (n_trials == 1 && round==0) { --#ifdef __AVX2__ -+ #ifdef __AVX2__ - int off = ((nb_rb&1) == 1)? 4:0; --#else -+ #else - int off = 0; --#endif -+ #endif - - LOG_M("rxsigF0_ext.m","rxsF0_ext", - &gNB->pusch_vars[0]->rxdataF_ext[0][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -@@ -1298,11 +1386,77 @@ int main(int argc, char **argv) - (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_M("rxsigF0_comp.m","rxsF0_comp", - &gNB->pusch_vars[0]->rxdataF_comp[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -- LOG_M("chmagF0.m","chmF0", -- &gNB->pusch_vars[0]->ul_ch_mag[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -- LOG_M("chmagbF0.m","chmbF0", -- &gNB->pusch_vars[0]->ul_ch_magb[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -- if (n_rx == 2) { -+ LOG_M("chmagF0.m","chmF0", -+ &gNB->pusch_vars[0]->ul_ch_mag[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("chmagbF0.m","chmbF0", -+ &gNB->pusch_vars[0]->ul_ch_magb[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("rxsigF0_llrlayers0.m","rxsF0_llrlayers0", -+ &gNB->pusch_vars[0]->llr_layers[0][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); -+ -+ if (precod_nbr_layers==2) -+ { -+ LOG_M("rxsigF1_ext.m","rxsF1_ext", -+ &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ -+ LOG_M("chestF3.m","chF3", -+ &gNB->pusch_vars[0]->ul_ch_estimates[3][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); -+ -+ LOG_M("chestF3_ext.m","chF3_ext", -+ &gNB->pusch_vars[0]->ul_ch_estimates_ext[3][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], -+ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ -+ LOG_M("rxsigF2_comp.m","rxsF2_comp", -+ &gNB->pusch_vars[0]->rxdataF_comp[2][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ -+ LOG_M("rxsigF0_llrlayers1.m","rxsF0_llrlayers1", -+ &gNB->pusch_vars[0]->llr_layers[1][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); -+ -+ } -+ -+ if (precod_nbr_layers==4) -+ { -+ LOG_M("rxsigF1_ext.m","rxsF1_ext", -+ &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("rxsigF2_ext.m","rxsF2_ext", -+ &gNB->pusch_vars[0]->rxdataF_ext[2][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("rxsigF3_ext.m","rxsF3_ext", -+ &gNB->pusch_vars[0]->rxdataF_ext[3][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ -+ LOG_M("chestF5.m","chF5", -+ &gNB->pusch_vars[0]->ul_ch_estimates[5][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); -+ LOG_M("chestF10.m","chF10", -+ &gNB->pusch_vars[0]->ul_ch_estimates[10][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); -+ LOG_M("chestF15.m","chF15", -+ &gNB->pusch_vars[0]->ul_ch_estimates[15][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); -+ -+ -+ LOG_M("chestF5_ext.m","chF5_ext", -+ &gNB->pusch_vars[0]->ul_ch_estimates_ext[5][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], -+ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("chestF10_ext.m","chF10_ext", -+ &gNB->pusch_vars[0]->ul_ch_estimates_ext[10][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], -+ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("chestF15_ext.m","chF15_ext", -+ &gNB->pusch_vars[0]->ul_ch_estimates_ext[15][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], -+ (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ -+ -+ LOG_M("rxsigF4_comp.m","rxsF4_comp", -+ &gNB->pusch_vars[0]->rxdataF_comp[4][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("rxsigF8_comp.m","rxsF8_comp", -+ &gNB->pusch_vars[0]->rxdataF_comp[8][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("rxsigF12_comp.m","rxsF12_comp", -+ &gNB->pusch_vars[0]->rxdataF_comp[12][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); -+ LOG_M("rxsigF0_llrlayers1.m","rxsF0_llrlayers1", -+ &gNB->pusch_vars[0]->llr_layers[1][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); -+ LOG_M("rxsigF0_llrlayers2.m","rxsF0_llrlayers2", -+ &gNB->pusch_vars[0]->llr_layers[2][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); -+ LOG_M("rxsigF0_llrlayers3.m","rxsF0_llrlayers3", -+ &gNB->pusch_vars[0]->llr_layers[3][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); -+ } -+ -+ #if 0 -+ if (n_rx == 2) { - LOG_MM("rxsigF0_comp.m","rxsF1_comp", - &gNB->pusch_vars[0]->rxdataF_comp[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("rxsigF0_ext.m","rxsF1_ext", -@@ -1349,9 +1503,10 @@ int main(int argc, char **argv) - LOG_MM("chmagbF0.m","chmbF3", - &gNB->pusch_vars[0]->ul_ch_magb[3][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - } -+ #endif - - LOG_M("rxsigF0_llr.m","rxsF0_llr", -- &gNB->pusch_vars[0]->llr[0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); -+ &gNB->pusch_vars[0]->llr[0],precod_nbr_layers*(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); - } - //////////////////////////////////////////////////////////// - -diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c -index 3f4be9dd58..7e5be9eeba 100644 ---- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c -+++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c -@@ -2708,6 +2708,9 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, - LOG_D(NR_MAC,"dci_pdu->srs_resource_indicator.nbits %d\n",dci_pdu->srs_resource_indicator.nbits); - // Precoding info and number of layers - long transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, ubwpd, (uint8_t*)&format, rnti_type, 0); -+ -+ pusch_antenna_ports = *pusch_Config->maxRank; -+ - dci_pdu->precoding_information.nbits=0; - if (pusch_Config && - pusch_Config->txConfig != NULL){ -diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c -index c23d4bb073..963723725f 100644 ---- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c -+++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c -@@ -190,7 +190,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con - else - transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; - } -- -+ pusch_config_pdu->transformPrecoder = transformPrecoder; - - /* PRECOD_NBR_LAYERS */ - if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_nonCodebook)); -@@ -198,11 +198,12 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con - - if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)){ - -- uint8_t n_antenna_port = 0; //FIXME!!! -+ uint8_t n_antenna_port = mac->phy_config.config_req.carrier_config.num_tx_ant; - - if (n_antenna_port == 1); // 1 antenna port and the higher layer parameter txConfig = codebook 0 bits - -- if (n_antenna_port == 4){ // 4 antenna port and the higher layer parameter txConfig = codebook -+ if (n_antenna_port == 4) -+ { // 4 antenna port and the higher layer parameter txConfig = codebook - - // Table 7.3.1.1.2-2: transformPrecoder=disabled and maxRank = 2 or 3 or 4 - if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) -@@ -212,54 +213,56 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][0]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1]; - } - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent){ - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][2]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3]; - } - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][4]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5]; - } - } - - // Table 7.3.1.1.2-3: transformPrecoder= enabled, or transformPrecoder=disabled and maxRank = 1 - if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) - || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) -- && (*pusch_Config->maxRank == 1)){ -+ && (*pusch_Config->maxRank == 1)) -+ { - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][6]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7]; - } - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent){ - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][8]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9]; - } - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][10]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11]; - } - } - } - -- if (n_antenna_port == 4){ // 2 antenna port and the higher layer parameter txConfig = codebook -+ if (n_antenna_port == 2) -+ { // 2 antenna port and the higher layer parameter txConfig = codebook - // Table 7.3.1.1.2-4: transformPrecoder=disabled and maxRank = 2 -- if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)){ -- -+ if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)) -+ { - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][12]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13]; - } - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][14]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15]; - } - - } -@@ -267,16 +270,17 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con - // Table 7.3.1.1.2-5: transformPrecoder= enabled, or transformPrecoder= disabled and maxRank = 1 - if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) - || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) -- && (*pusch_Config->maxRank == 1)){ -+ && (*pusch_Config->maxRank == 1)) -+ { - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][16]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17]; - } - - if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent){ - pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][18]; -- pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19]; -+ pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19]; - } - - } -@@ -285,9 +289,9 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con - - /*-------------------- Changed to enable Transform precoding in RF SIM------------------------------------------------*/ - -- /*if (pusch_config_pdu->transform_precoding == transform_precoder_enabled) { -+ /*if (pusch_config_pdu->transformPrecoder == transformPrecoder_enabled) { - -- pusch_config_dedicated->transform_precoder = transform_precoder_enabled; -+ pusch_config_dedicated->transform_precoder = transformPrecoder_enabled; - - if(pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA != NULL) { - -@@ -309,7 +313,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con - - } - } else -- pusch_config_dedicated->transform_precoder = transform_precoder_disabled;*/ -+ pusch_config_dedicated->transformPrecoder = ttransformPrecoder_disabled;*/ - } - - // todo: this function shall be reviewed completely because of the many comments left by the author -@@ -342,7 +346,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf - } - long *max_length = NULL; - long *dmrs_type = NULL; -- LOG_D(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); -+ LOG_I(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); - - if (pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA) { - max_length = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup->maxLength; -@@ -353,7 +357,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf - dmrs_type = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->dmrs_Type; - } - -- LOG_D(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n",pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); -+ LOG_I(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n",pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); - if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) && - (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-6 - pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC -@@ -369,35 +373,32 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf - } - - if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && -- (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-8/9/10/11 -+ (dmrs_type == NULL) && (max_length == NULL)) -+ { // tables 7.3.1.1.2-8/9/10/11 -+ -+ if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)) -+ { -+ rank = pusch_config_pdu->nrOfLayers; -+ } - - if (rank == 1) { -- pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC -- pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val); //TBC -+ pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; -+ pusch_config_pdu->dmrs_ports =1<<((dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val)); - } - - if (rank == 2){ -- pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; //TBC -- pusch_config_pdu->dmrs_ports = 0; //FIXME -- //pusch_config_pdu->dmrs_ports[0] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?0:2):0; -- //pusch_config_pdu->dmrs_ports[1] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?2:3):1; -+ pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; -+ pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?((dci->antenna_ports.val> 2)?0x5:0xc):0x3; - } - - if (rank == 3){ -- pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC -- pusch_config_pdu->dmrs_ports = 0; //FIXME -- //pusch_config_pdu->dmrs_ports[0] = 0; -- //pusch_config_pdu->dmrs_ports[1] = 1; -- //pusch_config_pdu->dmrs_ports[2] = 2; -+ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; -+ pusch_config_pdu->dmrs_ports = ((1<num_dmrs_cdm_grps_no_data = 2; //TBC -- pusch_config_pdu->dmrs_ports = 0; //FIXME -- //pusch_config_pdu->dmrs_ports[0] = 0; -- //pusch_config_pdu->dmrs_ports[1] = 1; -- //pusch_config_pdu->dmrs_ports[2] = 2; -- //pusch_config_pdu->dmrs_ports[3] = 3; -+ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; -+ pusch_config_pdu->dmrs_ports = ((1<n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME - } - } -- LOG_D(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); -+ LOG_I(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); - } - - // Configuration of Msg3 PDU according to clauses: -@@ -542,6 +543,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, - pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1; - pusch_config_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; - pusch_config_pdu->nrOfLayers = 1; -+ pusch_config_pdu->Tpmi = 0; - pusch_config_pdu->rnti = rnti; - NR_BWP_UplinkCommon_t *initialUplinkBWP; - if (mac->scc) initialUplinkBWP = mac->scc->uplinkConfigCommon->initialUplinkBWP; -@@ -634,7 +636,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, - pusch_config_pdu->scid = 0; - - // Transform precoding according to 6.1.3 UE procedure for applying transform precoding on PUSCH in 38.214 -- pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out -+ pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out - - // Resource allocation in frequency domain according to 6.1.2.2 in TS 38.214 - pusch_config_pdu->resource_alloc = (mac->cg) ? pusch_Config->resourceAllocation : 1; -@@ -680,7 +682,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, - - /* Transform precoding */ - if (rnti_type != NR_RNTI_CS || (rnti_type == NR_RNTI_CS && dci->ndi == 1)) { -- pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); -+ pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); - } - - /*DCI format-related configuration*/ -@@ -716,7 +718,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, - - /* TRANSFORM PRECODING ------------------------------------------------------------------------------------------*/ - -- if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled) { -+ if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) { - - pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; - -@@ -761,7 +763,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, - pusch_config_pdu->mcs_index = dci->mcs; - - /* MCS TABLE */ -- if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { -+ if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { - pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_Table : NULL, 0, *dci_format, rnti_type, target_ss, false); - } else { - pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_TableTransformPrecoder : NULL, 1, *dci_format, rnti_type, target_ss, false); -@@ -798,9 +800,14 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, - mappingtype, add_pos, dmrslength, - pusch_config_pdu->start_symbol_index, - mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); -- if ((mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled)) -- pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; -- else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled)) -+ if (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) -+ { -+ if (*dci_format != NR_UL_DCI_FORMAT_0_1) -+ { -+ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; -+ } -+ } -+ else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled)) - pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; - - // Num PRB Overhead from PUSCH-ServingCellConfig -@@ -821,7 +828,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, - mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup && - mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && - mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { -- if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { -+ if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { - nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; - pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; - valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, -@@ -909,6 +916,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in - - fill_scheduled_response(&scheduled_response, &dcireq.dl_config_req, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); - if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) -+ LOG_I(NR_MAC,"1# scheduled_response transmitted, %d, %d\n", rx_frame, rx_slot); - mac->if_module->scheduled_response(&scheduled_response); - } - else { -@@ -1022,6 +1030,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in - - fill_scheduled_response(&scheduled_response, NULL, ul_config, &tx_req, mod_id, cc_id, rx_frame, rx_slot, ul_info->thread_id); - if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){ -+ LOG_I(NR_MAC,"3# scheduled_response transmitted,%d, %d\n", frame_tx, slot_tx); - mac->if_module->scheduled_response(&scheduled_response); - } - pthread_mutex_lock(&ul_config->mutex_ul_config); -diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c -index 1cb3fdb708..d9c98852a3 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/config.c -+++ b/openair2/LAYER2/NR_MAC_gNB/config.c -@@ -388,7 +388,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts - cfg->carrier_config.num_rx_ant.value = pusch_AntennaPorts; - AssertFatal(pusch_AntennaPorts > 0 && pusch_AntennaPorts < 13, "pusch_AntennaPorts in 1...12\n"); - cfg->carrier_config.num_rx_ant.tl.tag = NFAPI_NR_CONFIG_NUM_RX_ANT_TAG; -- LOG_I(NR_MAC,"Set TX/RX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); -+ LOG_I(NR_MAC,"Set RX antenna number to %d, Set TX antenna number to %d (num ssb %d: %x,%x)\n",cfg->carrier_config.num_tx_ant.value,cfg->carrier_config.num_rx_ant.value,num_ssb,cfg->ssb_table.ssb_mask_list[0].ssb_mask.value,cfg->ssb_table.ssb_mask_list[1].ssb_mask.value); - AssertFatal(cfg->carrier_config.num_tx_ant.value > 0,"carrier_config.num_tx_ant.value %d !\n",cfg->carrier_config.num_tx_ant.value ); - cfg->num_tlv++; - cfg->num_tlv++; -diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c -index 6ac7f3f82e..5de36bb2db 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c -+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c -@@ -977,9 +977,9 @@ void fill_msg3_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, - pusch_pdu->target_code_rate = nr_get_code_rate_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); - pusch_pdu->qam_mod_order = nr_get_Qm_ul(pusch_pdu->mcs_index,pusch_pdu->mcs_table); - if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) -- pusch_pdu->transform_precoding = 1; -+ pusch_pdu->transformPrecoder = 1; - else -- pusch_pdu->transform_precoding = 0; -+ pusch_pdu->transformPrecoder = 0; - pusch_pdu->data_scrambling_id = *scc->physCellId; - pusch_pdu->nrOfLayers = 1; - pusch_pdu->ul_dmrs_symb_pos = get_l_prime(nr_of_symbols,mappingtype,pusch_dmrs_pos2,pusch_len1,start_symbol_index, scc->dmrs_TypeA_Position); -diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c -index abc007197d..9cbbe7d9a1 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c -+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c -@@ -1139,13 +1139,14 @@ void nr_schedule_ue_spec(module_id_t module_id, - dci_payload.dmrs_sequence_initialization.val = pdsch_pdu->SCID; - LOG_D(NR_MAC, - "%4d.%2d DCI type 1 payload: freq_alloc %d (%d,%d,%d), " -- "time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", -+ "nrOfLayers %d, time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", - frame, - slot, - dci_payload.frequency_domain_assignment.val, - pdsch_pdu->rbStart, - pdsch_pdu->rbSize, - pdsch_pdu->BWPSize, -+ pdsch_pdu->nrOfLayers, - dci_payload.time_domain_assignment.val, - dci_payload.vrb_to_prb_mapping.val, - dci_payload.mcs, -diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c -index f1005cea70..8f7c856a30 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c -+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c -@@ -405,6 +405,7 @@ void nr_preprocessor_phytest(module_id_t module_id, - - uint32_t target_ul_mcs = 9; - uint32_t target_ul_bw = 50; -+uint32_t target_ul_Nl = 1; - uint64_t ulsch_slot_bitmap = (1 << 8); - bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_t slot) - { -@@ -446,15 +447,21 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ - - const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats; - const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; -- const uint8_t num_dmrs_cdm_grps_no_data = 1; -+ uint8_t num_dmrs_cdm_grps_no_data = 1; -+ if ((target_ul_Nl==4)||(target_ul_Nl==3)) -+ { -+ num_dmrs_cdm_grps_no_data = 2; -+ } -+ - /* we want to avoid a lengthy deduction of DMRS and other parameters in - * every TTI if we can save it, so check whether dci_format, TDA, or - * num_dmrs_cdm_grps_no_data has changed and only then recompute */ - NR_pusch_semi_static_t *ps = &sched_ctrl->pusch_semi_static; - if (ps->time_domain_allocation != tda - || ps->dci_format != dci_format -+ || ps->nrOfLayers != target_ul_Nl - || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) -- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, NULL,dci_format, tda, num_dmrs_cdm_grps_no_data, ps); -+ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, NULL,dci_format, tda, num_dmrs_cdm_grps_no_data,target_ul_Nl,ps); - - uint16_t rbStart = 0; - uint16_t rbSize; -@@ -525,6 +532,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ - sched_pusch->ul_harq_pid = sched_ctrl->retrans_ul_harq.head; - - /* Calculate TBS from MCS */ -+ ps->nrOfLayers = target_ul_Nl; - sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table); - sched_pusch->Qm = nr_get_Qm_ul(mcs, ps->mcs_table); - if (ps->pusch_Config->tp_pi2BPSK -@@ -539,7 +547,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ - ps->N_PRB_DMRS * ps->num_dmrs_symb, - 0, // nb_rb_oh - 0, -- 1 /* NrOfLayers */) -+ ps->nrOfLayers /* NrOfLayers */) - >> 3; - - /* mark the corresponding RBs as used */ -diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c -index 1c0cb0353e..4583da876f 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c -+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c -@@ -355,6 +355,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, - long dci_format, - int tda, - uint8_t num_dmrs_cdm_grps_no_data, -+ uint8_t nrOfLayers, - NR_pusch_semi_static_t *ps) - { - ps->dci_format = dci_format; -@@ -371,11 +372,11 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, - - ps->pusch_Config = ubwp?ubwp->bwp_Dedicated->pusch_Config->choice.setup:(ubwpd ? ubwpd->pusch_Config->choice.setup : NULL); - if (ps->pusch_Config == NULL || !ps->pusch_Config->transformPrecoder) -- ps->transform_precoding = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; -+ ps->transformPrecoder = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; - else -- ps->transform_precoding = *ps->pusch_Config->transformPrecoder; -+ ps->transformPrecoder = *ps->pusch_Config->transformPrecoder; - const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; -- if (ps->transform_precoding) -+ if (ps->transformPrecoder) - ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config ? ps->pusch_Config->mcs_Table : NULL, - 0, - ps->dci_format, -@@ -392,6 +393,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, - num_dmrs_cdm_grps_no_data = 2; // in case of transform precoding - no Data sent in DMRS symbol - } - -+ ps->nrOfLayers = nrOfLayers; - ps->num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; - - /* DMRS calculations */ -@@ -689,8 +691,21 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, - "Non Codebook configuration non supported\n"); - dci_pdu_rel15->srs_resource_indicator.val = 0; // taking resource 0 for SRS - } -+ AssertFatal((pusch_pdu->Tpmi==0), "unsupport Tpmi\n"); -+ dci_pdu_rel15->precoding_information.val= 0; -+ if (pusch_pdu->nrOfLayers == 2) -+ { -+ dci_pdu_rel15->precoding_information.val = 4; -+ } -+ else if (pusch_pdu->nrOfLayers == 4) -+ { -+ dci_pdu_rel15->precoding_information.val = 11; -+ } -+ -+ // antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4 - // Antenna Ports -- dci_pdu_rel15->antenna_ports.val = 0; // TODO for now it is hardcoded, it should depends on cdm group no data and rank -+ dci_pdu_rel15->antenna_ports.val = 0; -+ - // DMRS sequence initialization - dci_pdu_rel15->dmrs_sequence_initialization.val = pusch_pdu->scid; - break; -@@ -699,11 +714,14 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, - } - - LOG_D(NR_MAC, -- "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n", -+ "%s() ULDCI type 0 payload: dci_format %d, freq_alloc %d, time_alloc %d, freq_hop_flag %d, precoding_information.val %d antenna_ports.val %d mcs %d tpc %d ndi %d rv %d\n", - __func__, -+ dci_format, - dci_pdu_rel15->frequency_domain_assignment.val, - dci_pdu_rel15->time_domain_assignment.val, - dci_pdu_rel15->frequency_hopping_flag.val, -+ dci_pdu_rel15->precoding_information.val, -+ dci_pdu_rel15->antenna_ports.val, - dci_pdu_rel15->mcs, - dci_pdu_rel15->tpc, - dci_pdu_rel15->ndi, -diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c -index 9a6fa32b5f..269d6c5359 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c -+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c -@@ -905,7 +905,7 @@ bool allocate_ul_retransmission(module_id_t module_id, - NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters : &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; - int rbStart = 0; // wrt BWP start - const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); -- -+ const uint8_t nrOfLayers = 1; - const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_bwp || ubwpd) ? 1 : 2; - const int tda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; - LOG_D(NR_MAC,"retInfo->time_domain_allocation = %d, tda = %d\n", retInfo->time_domain_allocation, tda); -@@ -926,8 +926,9 @@ bool allocate_ul_retransmission(module_id_t module_id, - - if (ps->time_domain_allocation != tda - || ps->dci_format != dci_format -+ || ps->nrOfLayers != nrOfLayers - || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) -- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); -+ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers,ps); - LOG_D(NR_MAC, "%s(): retransmission keeping TDA %d and TBS %d\n", __func__, tda, retInfo->tb_size); - } else { - /* the retransmission will use a different time domain allocation, check -@@ -939,7 +940,7 @@ bool allocate_ul_retransmission(module_id_t module_id, - rbSize++; - NR_pusch_semi_static_t temp_ps; - int dci_format = get_dci_format(sched_ctrl); -- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp,ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, &temp_ps); -+ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp,ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers, &temp_ps); - uint32_t new_tbs; - uint16_t new_rbSize; - bool success = nr_find_nb_rb(retInfo->Qm, -@@ -1094,7 +1095,7 @@ void pf_ul(module_id_t module_id, - if (max_num_ue < 0) - return; - -- LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d\n", min_rb, rbStart); -+ LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d num_dmrs_cdm_grps_no_data %d\n", min_rb, rbStart, ps->num_dmrs_cdm_grps_no_data); - while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++; - if (rbStart + min_rb >= bwpSize) { - LOG_W(NR_MAC, "cannot allocate continuous UL data for UE %d/RNTI %04x: no resources (rbStart %d, min_rb %d, bwpSize %d\n", -@@ -1106,13 +1107,15 @@ void pf_ul(module_id_t module_id, - /* we want to avoid a lengthy deduction of DMRS and other parameters in - * every TTI if we can save it, so check whether dci_format, TDA, or - * num_dmrs_cdm_grps_no_data has changed and only then recompute */ -+ const uint8_t nrOfLayers = 1; - const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_ubwp || ubwpd) ? 1 : 2; - int dci_format = get_dci_format(sched_ctrl); - const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; - if (ps->time_domain_allocation != tda - || ps->dci_format != dci_format -+ || ps->nrOfLayers != nrOfLayers - || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) -- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); -+ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data,nrOfLayers, ps); - NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; - sched_pusch->mcs = 9; - update_ul_ue_R_Qm(sched_pusch, ps); -@@ -1125,7 +1128,7 @@ void pf_ul(module_id_t module_id, - ps->N_PRB_DMRS * ps->num_dmrs_symb, - 0, // nb_rb_oh - 0, -- 1 /* NrOfLayers */) -+ ps->nrOfLayers) - >> 3; - - /* Mark the corresponding RBs as used */ -@@ -1205,13 +1208,15 @@ void pf_ul(module_id_t module_id, - /* we want to avoid a lengthy deduction of DMRS and other parameters in - * every TTI if we can save it, so check whether dci_format, TDA, or - * num_dmrs_cdm_grps_no_data has changed and only then recompute */ -+ const uint8_t nrOfLayers = 1; - const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_ubwp || ubwpd) ? 1 : 2; - int dci_format = get_dci_format(sched_ctrl); - const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; - if (ps->time_domain_allocation != tda - || ps->dci_format != dci_format -+ || ps->nrOfLayers != nrOfLayers - || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) -- nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); -+ nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, nrOfLayers,ps); - update_ul_ue_R_Qm(sched_pusch, ps); - - /* Calculate the current scheduling bytes and the necessary RBs */ -@@ -1473,7 +1478,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) - sched_ctrl->last_ul_slot = sched_pusch->slot; - - LOG_D(NR_MAC, -- "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d startSymbol %2d nb_symbol %2d dmrs_pos %x MCS %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched %6d est BSR %6d TPC %d\n", -+ "ULSCH/PUSCH: %4d.%2d RNTI %04x UL sched %4d.%2d DCI L %d start %2d RBS %3d startSymbol %2d nb_symbol %2d dmrs_pos %x MCS %2d nrOfLayers %2d num_dmrs_cdm_grps_no_data %2d TBS %4d HARQ PID %2d round %d RV %d NDI %d est %6d sched %6d est BSR %6d TPC %d\n", - frame, - slot, - rnti, -@@ -1486,6 +1491,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) - ps->nrOfSymbols, - ps->ul_dmrs_symb_pos, - sched_pusch->mcs, -+ ps->nrOfLayers, -+ ps->num_dmrs_cdm_grps_no_data, - sched_pusch->tb_size, - harq_id, - cur_harq->round, -@@ -1532,17 +1539,18 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) - pusch_pdu->qam_mod_order = sched_pusch->Qm; - pusch_pdu->mcs_index = sched_pusch->mcs; - pusch_pdu->mcs_table = ps->mcs_table; -- pusch_pdu->transform_precoding = ps->transform_precoding; -+ pusch_pdu->transformPrecoder = ps->transformPrecoder; - if (ps->pusch_Config && ps->pusch_Config->dataScramblingIdentityPUSCH) - pusch_pdu->data_scrambling_id = *ps->pusch_Config->dataScramblingIdentityPUSCH; - else - pusch_pdu->data_scrambling_id = *scc->physCellId; -- pusch_pdu->nrOfLayers = 1; -+ pusch_pdu->nrOfLayers = ps->nrOfLayers; -+ pusch_pdu->num_dmrs_cdm_grps_no_data = ps->num_dmrs_cdm_grps_no_data; - - /* FAPI: DMRS */ - pusch_pdu->ul_dmrs_symb_pos = ps->ul_dmrs_symb_pos; - pusch_pdu->dmrs_config_type = ps->dmrs_config_type; -- if (pusch_pdu->transform_precoding) { // transform precoding disabled -+ if (pusch_pdu->transformPrecoder) { // transform precoding disabled - long *scramblingid=NULL; - if (ps->NR_DMRS_UplinkConfig && pusch_pdu->scid == 0) - scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0; -@@ -1562,7 +1570,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) - } - pusch_pdu->scid = 0; // DMRS sequence initialization [TS38.211, sec 6.4.1.1.1] - pusch_pdu->num_dmrs_cdm_grps_no_data = ps->num_dmrs_cdm_grps_no_data; -- pusch_pdu->dmrs_ports = 1; -+ pusch_pdu->dmrs_ports = ((1<nrOfLayers) - 1); - - /* FAPI: Pusch Allocation in frequency domain */ - pusch_pdu->resource_alloc = 1; //type 1 -@@ -1588,7 +1596,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) - LOG_D(NR_MAC,"PUSCH PDU : data_scrambling_identity %x, dmrs_scrambling_id %x\n",pusch_pdu->data_scrambling_id,pusch_pdu->ul_dmrs_scrambling_id); - /* TRANSFORM PRECODING --------------------------------------------------------*/ - -- if (pusch_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled){ -+ if (pusch_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled){ - - // U as specified in section 6.4.1.1.1.2 in 38.211, if sequence hopping and group hopping are disabled - pusch_pdu->dfts_ofdm.low_papr_group_number = pusch_pdu->pusch_identity % 30; -diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h -index 91221276b0..8a560580c9 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h -+++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h -@@ -298,6 +298,7 @@ void nr_set_pusch_semi_static(const NR_ServingCellConfigCommon_t *scc, - long dci_format, - int tda, - uint8_t num_dmrs_cdm_grps_no_data, -+ uint8_t nrOfLayers, - NR_pusch_semi_static_t *ps); - - uint8_t nr_get_tpc(int target, uint8_t cqi, int incr); -diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h -index 27f42f4b1d..66b10dad42 100644 ---- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h -+++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h -@@ -318,13 +318,14 @@ typedef struct NR_sched_pucch { - typedef struct NR_pusch_semi_static_t { - int dci_format; - int time_domain_allocation; -+ uint8_t nrOfLayers; - uint8_t num_dmrs_cdm_grps_no_data; - - int startSymbolIndex; - int nrOfSymbols; - - NR_PUSCH_Config_t *pusch_Config; -- uint8_t transform_precoding; -+ uint8_t transformPrecoder; - uint8_t mcs_table; - - long mapping_type; -diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c -index ff6923429e..52d03b5e56 100755 ---- a/openair2/RRC/NR/MESSAGES/asn1_msg.c -+++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c -@@ -1867,6 +1867,7 @@ int16_t do_RRCReconfiguration( - // 1, - // 1, - // carrier->pdsch_AntennaPorts, -+ // carrier->pusch_AntennaPorts, - // carrier->initial_csi_index[ue_context_p->local_uid + 1], - // ue_context_pP->local_uid); - -diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h -index 9465b9b4b0..6a94450450 100644 ---- a/openair2/RRC/NR/nr_rrc_proto.h -+++ b/openair2/RRC/NR/nr_rrc_proto.h -@@ -79,6 +79,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco - int scg_id, - int servCellIndex, - int dl_antenna_ports, -+ int ul_antenna_ports, - int do_csirs, - int initial_csi_index, - int uid); -@@ -90,6 +91,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon - NR_RRCReconfiguration_IEs_t *reconfig, - NR_CellGroupConfig_t *secondaryCellGroup, - int dl_antenna_ports, -+ int ul_antenna_ports, - int do_csirs, - int initial_csi_index, - int uid); -diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c -index f9fce48f41..22e43cbb6a 100644 ---- a/openair2/RRC/NR/rrc_gNB_nsa.c -+++ b/openair2/RRC/NR/rrc_gNB_nsa.c -@@ -241,6 +241,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ - reconfig_ies, - ue_context_p->ue_context.secondaryCellGroup, - carrier->pdsch_AntennaPorts, -+ carrier->pusch_AntennaPorts, - carrier->do_CSIRS, - carrier->initial_csi_index[ue_context_p->local_uid + 1], - ue_context_p->local_uid); -@@ -250,6 +251,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ - reconfig_ies, - ue_context_p->ue_context.secondaryCellGroup, - carrier->pdsch_AntennaPorts, -+ carrier->pusch_AntennaPorts, - carrier->do_CSIRS, - carrier->initial_csi_index[ue_context_p->local_uid + 1], - ue_context_p->local_uid); -@@ -348,7 +350,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ - rrc_mac_config_req_gNB(rrc->module_id, - rrc->carrier.ssb_SubcarrierOffset, - rrc->carrier.pdsch_AntennaPorts, -- rrc->carrier.pusch_AntennaPorts, -+ rrc->carrier.pusch_AntennaPorts, - rrc->carrier.sib1_tda, - rrc->carrier.servingcellconfigcommon, - &rrc->carrier.mib, -diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c -index 980bd39c37..d03f361502 100644 ---- a/openair2/RRC/NR/rrc_gNB_reconfig.c -+++ b/openair2/RRC/NR/rrc_gNB_reconfig.c -@@ -55,6 +55,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco - int scg_id, - int servCellIndex, - int dl_antenna_ports, -+ int ul_antenna_ports, - int do_csirs, - int initial_csi_index, - int uid) { -@@ -745,14 +746,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco - pusch_Config->codebookSubset=calloc(1,sizeof(*pusch_Config->codebookSubset)); - *pusch_Config->codebookSubset = NR_PUSCH_Config__codebookSubset_nonCoherent; - pusch_Config->maxRank=calloc(1,sizeof(*pusch_Config->maxRank)); -- *pusch_Config->maxRank= 1; -+ *pusch_Config->maxRank= ul_antenna_ports; - pusch_Config->rbg_Size=NULL; - pusch_Config->uci_OnPUSCH=NULL; - pusch_Config->tp_pi2BPSK=NULL; - - /*------------------------------TRANSFORM PRECODING- -----------------------------------------------------------------------*/ - -- uint8_t transform_precoding = NR_PUSCH_Config__transformPrecoder_disabled; -+ uint8_t transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; - - // TBD: configure this from .conf file, Dedicated params cannot yet be configured in .conf file. - // Enable this to test transform precoding enabled from dedicated config. -@@ -764,13 +765,13 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco - - if (pusch_Config->transformPrecoder == NULL) { - if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder != NULL) -- transform_precoding = NR_PUSCH_Config__transformPrecoder_enabled; -+ transformPrecoder = NR_PUSCH_Config__transformPrecoder_enabled; - } - else -- transform_precoding = *pusch_Config->transformPrecoder; -+ transformPrecoder = *pusch_Config->transformPrecoder; - - -- if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled ) { -+ if (transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled ) { - /* Enable DMRS uplink config for transform precoding enabled */ - NR_DMRS_UplinkConfig->transformPrecodingEnabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingEnabled)); - NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity = NULL; -@@ -1348,6 +1349,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon - NR_RRCReconfiguration_IEs_t *reconfig, - NR_CellGroupConfig_t *secondaryCellGroup, - int dl_antenna_ports, -+ int ul_antenna_ports, - int do_csirs, - int initial_csi_index, - int uid) { -@@ -1363,6 +1365,7 @@ void fill_default_reconfig(NR_ServingCellConfigCommon_t *servingcellconfigcommon - 1, - 1, - dl_antenna_ports, -+ ul_antenna_ports, - do_csirs, - initial_csi_index, - uid); -diff --git a/targets/ARCH/rfsimulator/simulator.c b/targets/ARCH/rfsimulator/simulator.c -index 13945998d7..20faa86591 100644 ---- a/targets/ARCH/rfsimulator/simulator.c -+++ b/targets/ARCH/rfsimulator/simulator.c -@@ -429,7 +429,7 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp - if (!alreadyLocked) - pthread_mutex_lock(&Sockmutex); - -- LOG_D(HW,"sending %d samples at time: %ld\n", nsamps, timestamp); -+ LOG_D(HW,"sending %d samples at time: %ld, nbAnt %d\n", nsamps, timestamp, nbAnt); - - for (int i=0; ibuf[i]; -@@ -623,7 +623,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest - } - - rfsimulator_state_t *t = device->priv; -- LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld\n", nsamps, t->nextTimestamp+nsamps); -+ LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld, nbAnt %d\n", nsamps, t->nextTimestamp+nsamps, nbAnt); - // deliver data from received data - // check if a UE is connected - int first_sock; -@@ -728,6 +728,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest - - for (int a=0; achannel_model != NULL ) // apply a channel model -+ { - rxAddInput( ptr->circularBuf, (struct complex16 *) samplesVoid[a], - a, - ptr->channel_model, -@@ -735,11 +736,21 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest - t->nextTimestamp, - CirSize - ); -- else { // no channel modeling -+ } -+ else -+ { // no channel modeling -+ -+ #if 0 - double H_awgn_mimo[4][4] ={{1.0, 0.5, 0.25, 0.125},//rx 0 - {0.5, 1.0, 0.5, 0.25}, //rx 1 - {0.25, 0.5, 1.0, 0.5}, //rx 2 - {0.125, 0.25, 0.5, 1.0}};//rx 3 -+ #else -+ double H_awgn_mimo[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 -+ {0.0, 1.0, 0.0, 0.0},//rx 1 -+ {0.0, 0.0, 1.0, 0.0},//rx 2 -+ {0.0, 0.0, 0.0, 1.0}};//rx 3 -+ #endif - - sample_t *out=(sample_t *)samplesVoid[a]; - int nbAnt_tx = ptr->th.nbAnt;//number of Tx antennas diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 8314c83ad9..f41b51161b 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -1969,9 +1969,9 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->cl_done = 0; bwp_start_subcarrier = ((rel15_ul->rb_start + rel15_ul->bwp_start)*NR_NB_SC_PER_RB + frame_parms->first_carrier_offset) % frame_parms->ofdm_symbol_size; - LOG_I(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); - LOG_I(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); - LOG_I(PHY,"ulsch RX %x : start_rb %d nb_rb %d mcs %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_ports %d\n", + LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); + LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); + LOG_D(PHY,"ulsch RX %x : start_rb %d nb_rb %d mcs %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_ports %d\n", rel15_ul->rnti,rel15_ul->rb_start,rel15_ul->rb_size,rel15_ul->mcs_index, rel15_ul->nrOfLayers,0,rel15_ul->bwp_start,0,rel15_ul->start_symbol_index,rel15_ul->nr_of_symbols, rel15_ul->num_dmrs_cdm_grps_no_data,rel15_ul->ul_dmrs_symb_pos,rel15_ul->dmrs_ports); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 5d76e654ba..eda1711f6b 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -98,7 +98,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, uint8_t thread_id, int gNB_id) { - LOG_I(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); + LOG_D(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); uint32_t available_bits; uint8_t cwd_index, l; @@ -150,7 +150,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, nb_dmrs_re_per_rb = ((dmrs_type == pusch_dmrs_type1) ? 6:4)*cdm_grps_no_data; - LOG_I(PHY,"ulsch TX %x : start_rb %d nb_rb %d mod_order %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", + LOG_D(PHY,"ulsch TX %x : start_rb %d nb_rb %d mod_order %d Nl %d Tpmi %d bwp_start %d start_sc %d start_symbol %d num_symbols %d cdmgrpsnodata %d num_dmrs %d dmrs_re_per_rb %d\n", rnti,start_rb,nb_rb,mod_order,Nl,pusch_pdu->Tpmi,pusch_pdu->bwp_start,start_sc,start_symbol,number_of_symbols,cdm_grps_no_data,number_dmrs_symbols,nb_dmrs_re_per_rb); // TbD num_of_mod_symbols is set but never used diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 63c708e565..d77fb94bc1 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -253,17 +253,17 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { // if all segments are done if (rdata->nbSegments == ulsch_harq->processedSegments) { if (decodeSuccess) { - LOG_I(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", + LOG_D(PHY,"[gNB %d] ULSCH: Setting ACK for SFN/SF %d.%d (pid %d, ndi %d, status %d, round %d, TBS %d, Max interation (all seg) %d)\n", gNB->Mod_id,ulsch_harq->frame,ulsch_harq->slot,rdata->harq_pid,pusch_pdu->pusch_data.new_data_indicator,ulsch_harq->status,ulsch_harq->round,ulsch_harq->TBS,rdata->decodeIterations); ulsch_harq->status = SCH_IDLE; ulsch_harq->round = 0; ulsch->harq_mask &= ~(1 << rdata->harq_pid); - LOG_I(PHY, "ULSCH received ok \n"); + LOG_D(PHY, "ULSCH received ok \n"); nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 0,0); //dumpsig=1; } else { - LOG_I(PHY,"[gNB %d] ULSCH: Setting NAK for SFN/SF %d/%d (pid %d, ndi %d, status %d, round %d, RV %d, prb_start %d, prb_size %d, TBS %d) r %d\n", + LOG_D(PHY,"[gNB %d] ULSCH: Setting NAK for SFN/SF %d/%d (pid %d, ndi %d, status %d, round %d, RV %d, prb_start %d, prb_size %d, TBS %d) r %d\n", gNB->Mod_id, ulsch_harq->frame, ulsch_harq->slot, rdata->harq_pid, pusch_pdu->pusch_data.new_data_indicator, ulsch_harq->status, ulsch_harq->round, @@ -281,7 +281,7 @@ void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) { } ulsch_harq->handled = 1; - LOG_I(PHY, "ULSCH %d in error\n",rdata->ulsch_id); + LOG_D(PHY, "ULSCH %d in error\n",rdata->ulsch_id); nr_fill_indication(gNB,ulsch_harq->frame, ulsch_harq->slot, rdata->ulsch_id, rdata->harq_pid, 1,0); } /* @@ -739,7 +739,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { (ulsch_harq->slot == slot_rx) && (ulsch_harq->handled == 0)){ - LOG_I(PHY, "PUSCH detection started in frame %d slot %d\n", + LOG_D(PHY, "PUSCH detection started in frame %d slot %d\n", frame_rx,slot_rx); int num_dmrs=0; for (int s=0;srx_pusch_stats); no_sig = nr_rx_pusch(gNB, ULSCH_id, frame_rx, slot_rx, harq_pid); if (no_sig) { - LOG_I(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); + LOG_D(PHY, "PUSCH not detected in frame %d, slot %d\n", frame_rx, slot_rx); nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, harq_pid, 1,1); return 1; } @@ -791,7 +791,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot) + gNB->pusch_thres) { NR_gNB_SCH_STATS_t *stats=get_ulsch_stats(gNB,ulsch); - LOG_I(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, + LOG_D(PHY, "PUSCH not detected in %d.%d (%d,%d,%d)\n",frame_rx,slot_rx, dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_power_tot), dB_fixed_x10(gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot),gNB->pusch_thres); gNB->pusch_vars[ULSCH_id]->ulsch_power_tot = gNB->pusch_vars[ULSCH_id]->ulsch_noise_power_tot; diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index d24b90ab3c..d602e38b4b 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -158,7 +158,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ for (i = 0; i < ul_config->number_pdus; ++i){ AssertFatal(ul_config->ul_config_list[i].pdu_type <= FAPI_NR_UL_CONFIG_TYPES,"pdu_type %d out of bounds\n",ul_config->ul_config_list[i].pdu_type); - LOG_I(PHY, "In %s i %d: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, i, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); + LOG_D(PHY, "In %s i %d: processing %s PDU of %d total UL PDUs (ul_config %p) \n", __FUNCTION__, i, ul_pdu_type[ul_config->ul_config_list[i].pdu_type - 1], ul_config->number_pdus, ul_config); uint8_t pdu_type = ul_config->ul_config_list[i].pdu_type, current_harq_pid, gNB_id = 0; /* PRACH */ @@ -182,7 +182,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &harq_process_ul_ue->pusch_pdu; - LOG_I(PHY, "In %s i %d: copy pusch_config_pdu nrOfLayers:%d, num_dmrs_cdm_grps_no_data:%d \n", __FUNCTION__, i, pusch_config_pdu->nrOfLayers,pusch_config_pdu->num_dmrs_cdm_grps_no_data); + LOG_D(PHY, "In %s i %d: copy pusch_config_pdu nrOfLayers:%d, num_dmrs_cdm_grps_no_data:%d \n", __FUNCTION__, i, pusch_config_pdu->nrOfLayers,pusch_config_pdu->num_dmrs_cdm_grps_no_data); memcpy(pusch_pdu, pusch_config_pdu, sizeof(nfapi_nr_ue_pusch_pdu_t)); From 2f7e7b9823c29d517a49ddc4b44d9b8b5dc01dd4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 10 Dec 2021 10:54:10 +0100 Subject: [PATCH 050/233] remove some more logs --- cmake_targets/autotests/test_case_list.xml | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml index 00fcfc4140..6c9508b736 100755 --- a/cmake_targets/autotests/test_case_list.xml +++ b/cmake_targets/autotests/test_case_list.xml @@ -1331,7 +1331,7 @@ -n100 -m9 -r106 -s10 -W2 -y2 -z2 -n100 -m9 -r106 -s13 -W4 -y4 -z4 - nr_ulsim.test1 nr_ulsim.test2 nr_ulsim.test3 nr_ulsim.test4 nr_ulsim.test5 nr_ulsim.test6 nr_ulsim.test7 nr_ulsim.test8 nr_ulsim.test9 nr_ulsim.test10 nr_ulsim.test11 nr_ulsim.test12 nr_ulsim.test13 nr_ulsim.test14 nr_ulsim.test15 nr_ulsim.test16 nr_ulsim.test16 + nr_ulsim.test1 nr_ulsim.test2 nr_ulsim.test3 nr_ulsim.test4 nr_ulsim.test5 nr_ulsim.test6 nr_ulsim.test7 nr_ulsim.test8 nr_ulsim.test9 nr_ulsim.test10 nr_ulsim.test11 nr_ulsim.test12 nr_ulsim.test13 nr_ulsim.test14 nr_ulsim.test15 nr_ulsim.test16 nr_ulsim.test17 PUSCH test OK segmentation fault|assertion|exiting|fatal 3 diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 05c1318814..84989ee6dc 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -346,7 +346,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf } long *max_length = NULL; long *dmrs_type = NULL; - LOG_I(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); + LOG_D(NR_MAC,"transformPrecoder %s\n",transformPrecoder==NR_PUSCH_Config__transformPrecoder_disabled?"disabled":"enabled"); if (pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA) { max_length = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup->maxLength; @@ -357,7 +357,8 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf dmrs_type = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->dmrs_Type; } - LOG_I(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n",pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); + LOG_D(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n", + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) && (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-6 pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC @@ -509,7 +510,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME } } - LOG_I(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); + LOG_D(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); } // Configuration of Msg3 PDU according to clauses: @@ -916,7 +917,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in fill_scheduled_response(&scheduled_response, &dcireq.dl_config_req, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id); if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) - LOG_I(NR_MAC,"1# scheduled_response transmitted, %d, %d\n", rx_frame, rx_slot); + LOG_D(NR_MAC,"1# scheduled_response transmitted, %d, %d\n", rx_frame, rx_slot); mac->if_module->scheduled_response(&scheduled_response); } else { @@ -1030,7 +1031,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in fill_scheduled_response(&scheduled_response, NULL, ul_config, &tx_req, mod_id, cc_id, rx_frame, rx_slot, ul_info->thread_id); if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){ - LOG_I(NR_MAC,"3# scheduled_response transmitted,%d, %d\n", frame_tx, slot_tx); + LOG_D(NR_MAC,"3# scheduled_response transmitted,%d, %d\n", frame_tx, slot_tx); mac->if_module->scheduled_response(&scheduled_response); } pthread_mutex_lock(&ul_config->mutex_ul_config); From 35ebb0e0453b761558c13021d448f4d196df97d3 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Mon, 27 Dec 2021 11:26:21 +0000 Subject: [PATCH 051/233] Update min_rxtxtime in config file after merge --- .../GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 05ba28b3f7..670740ea49 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -24,6 +24,7 @@ gNBs = ssb_SubcarrierOffset = 0; pdsch_AntennaPorts = 1; pusch_AntennaPorts = 1; + min_rxtxtime = 6; servingCellConfigCommon = ( { From 7f6e5c6fffed9fd5ea88cf61475f3e9a751f971b Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 24 Dec 2021 12:24:35 +0000 Subject: [PATCH 052/233] Fix warnings after merge with develop --- .../conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf | 2 +- .../conf_files/gnb.band78.sa.fr1.162PRB.usrpn310.conf | 2 +- openair1/PHY/INIT/nr_init_ue.c | 11 +---------- openair1/PHY/LTE_UE_TRANSPORT/pcfich_ue.c | 11 +++++------ .../CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf | 1 - 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf index 70bcf88efe..9bf8bd20cd 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf @@ -11,7 +11,7 @@ gNBs = # cell_type = "CELL_MACRO_GNB"; gNB_name = "gNB-OAI"; - min_rxtxtime_pdsch = 6; + min_rxtxtime = 6; // Tracking area code, 0x0000 and 0xfffe are reserved values tracking_area_code = 1; diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.usrpn310.conf index 70bcf88efe..9bf8bd20cd 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.usrpn310.conf @@ -11,7 +11,7 @@ gNBs = # cell_type = "CELL_MACRO_GNB"; gNB_name = "gNB-OAI"; - min_rxtxtime_pdsch = 6; + min_rxtxtime = 6; // Tracking area code, 0x0000 and 0xfffe are reserved values tracking_area_code = 1; diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index f664a03ba0..fd5cc962b1 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -20,23 +20,14 @@ */ #include "phy_init.h" -#include "SCHED_UE/sched_UE.h" #include "PHY/phy_extern_nr_ue.h" -//#include "SIMULATION/TOOLS/sim.h" -/*#include "RadioResourceConfigCommonSIB.h" -#include "RadioResourceConfigDedicated.h" -#include "TDD-Config.h" -#include "MBSFN-SubframeConfigList.h"*/ #include "openair1/PHY/defs_RU.h" #include "openair1/PHY/impl_defs_nr.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "assertions.h" -#include +#include "PHY/MODULATION/nr_modulation.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" -//#include "PHY/LTE_REFSIG/lte_refsig.h" -#include "PHY/CODING/nrPolar_tools/nr_polar_pbch_defs.h" -#include "PHY/INIT/phy_init.h" #include "PHY/NR_REFSIG/pss_nr.h" #include "PHY/NR_REFSIG/ul_ref_seq_nr.h" #include "PHY/NR_REFSIG/refsig_defs_ue.h" diff --git a/openair1/PHY/LTE_UE_TRANSPORT/pcfich_ue.c b/openair1/PHY/LTE_UE_TRANSPORT/pcfich_ue.c index 82bb0a2659..66ed4593d8 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/pcfich_ue.c +++ b/openair1/PHY/LTE_UE_TRANSPORT/pcfich_ue.c @@ -44,15 +44,14 @@ void pcfich_unscrambling(LTE_DL_FRAME_PARMS *frame_parms, int16_t *d) { - uint32_t i; - uint8_t reset; - uint32_t x1, x2, s=0; + uint8_t reset = 1; + uint32_t x1 = 0; + uint32_t s = 0; - reset = 1; // x1 is set in lte_gold_generic - x2 = ((((2*frame_parms->Nid_cell)+1)*(1+subframe))<<9) + frame_parms->Nid_cell; //this is c_init in 36.211 Sec 6.7.1 + uint32_t x2 = ((((2*frame_parms->Nid_cell)+1)*(1+subframe))<<9) + frame_parms->Nid_cell; //this is c_init in 36.211 Sec 6.7.1 - for (i=0; i<32; i++) { + for (uint32_t i=0; i<32; i++) { if ((i&0x1f)==0) { s = lte_gold_generic(&x1, &x2, reset); //printf("lte_gold[%d]=%x\n",i,s); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index bed5d6e318..88cd8e3a74 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -37,7 +37,6 @@ gNBs = pusch_AntennaPorts = 1; min_rxtxtime = 6; sib1_tda = 0; - do_SRS = 1; pdcch_ConfigSIB1 = ( { From 882c4b34a2a43a26f62994dc3673b217d94e8783 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 7 Jan 2022 18:03:35 +0100 Subject: [PATCH 053/233] cleanup --- openair1/PHY/INIT/nr_init.c | 11 +-- openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 21 ++--- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 18 ++--- .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 8 -- openair1/SIMULATION/NR_PHY/ulsim.c | 80 ++----------------- 5 files changed, 24 insertions(+), 114 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 2ac3230215..2beda80d36 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -284,10 +284,8 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, pusch_vars[ULSCH_id]->rxdataF_ext2[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); pusch_vars[ULSCH_id]->rho[i] = (int32_t **)malloc16_clear( NR_MAX_NB_LAYERS*NR_MAX_NB_LAYERS*sizeof(int32_t*)); - for (int j=0; j< max_ul_mimo_layers; j++) - { - for (int k=0; krho[i][j*max_ul_mimo_layers+k]=(int32_t *)malloc16_clear( sizeof(int32_t) * fp->N_RB_UL*12*7*2 ); } } @@ -303,11 +301,10 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, pusch_vars[ULSCH_id]->ul_ch_mag0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); pusch_vars[ULSCH_id]->ul_ch_magb0[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); pusch_vars[ULSCH_id]->ul_ch_mag[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); - pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); + pusch_vars[ULSCH_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 ); } - for (i=0; i< max_ul_mimo_layers; i++) - { + for (i=0; i< max_ul_mimo_layers; i++) { pusch_vars[ULSCH_id]->llr_layers[i] = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear } pusch_vars[ULSCH_id]->llr = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index 3ea19a9970..6bfcc1d50c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -145,25 +145,20 @@ void nr_ulsch_layer_demapping(int16_t *llr_cw, { switch (Nl) { - case 1: memcpy((void*)llr_cw, (void*)llr_layers[0], (length)*sizeof(int16_t)); - break; - + break; case 2: case 3: case 4: - for (int i=0; i<(length/Nl/mod_order); i++) - { - for (int l=0; lnb_antennas_rx; aarx++) - { + for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol * frame_parms->ofdm_symbol_size)]; rxF_ext = (int16_t *)&pusch_vars->rxdataF_ext[aarx][symbol * nb_re_pusch2]; // [hna] rxdataF_ext isn't contiguous in order to solve an alignment problem ib llr computation in case of mod_order = 4, 6 - if (is_dmrs_symbol == 0) - { + if (is_dmrs_symbol == 0) { // //rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) @@ -466,14 +464,10 @@ void nr_ulsch_scale_channel(int **ul_ch_estimates_ext, int off = 0; #endif -for (aatx = 0; aatx < nrOfLayers; aatx++) -{ - for (aarx=0; aarx < frame_parms->nb_antennas_rx; aarx++) - { + for (aatx = 0; aatx < nrOfLayers; aatx++) { + for (aarx=0; aarx < frame_parms->nb_antennas_rx; aarx++) { ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*NR_NB_SC_PER_RB))]; - - for (rb=0;rb < nb_rb_0;rb++) - { + for (rb=0;rb < nb_rb_0;rb++) { ul_ch128[0] = _mm_mulhi_epi16(ul_ch128[0], ch_amp128); ul_ch128[0] = _mm_slli_epi16(ul_ch128[0], 3); @@ -484,8 +478,8 @@ for (aatx = 0; aatx < nrOfLayers; aatx++) ul_ch128[2] = _mm_slli_epi16(ul_ch128[2], 3); ul_ch128+=3; } + } } -} #endif } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index a293eec6d8..036c1deaa6 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -259,14 +259,6 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, Tbslbrm = 950984; //max tbs Coderate = 0.0; - #if 0 - harq_process->a[0] = 0x31; - for (int i = 1; i < harq_process->pusch_pdu.pusch_data.tb_size; i++) - { - harq_process->a[i] = (i&0xff); - } - #endif - trace_NRpdu(DIRECTION_UPLINK, harq_process->a, harq_process->pusch_pdu.pusch_data.tb_size, 0, WS_C_RNTI, 0, 0, 0,0, 0); /////////// ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 957a624800..6caa18ae33 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -488,32 +488,26 @@ int main(int argc, char **argv) case 'y': n_tx = atoi(optarg); - if ((n_tx == 0) || (n_tx > 4)) { - printf("Unsupported number of tx antennas %d\n", n_tx); - exit(-1); + printf("Unsupported number of tx antennas %d\n", n_tx); + exit(-1); } - break; case 'z': n_rx = atoi(optarg); - if ((n_rx == 0) || (n_rx > 8)) { - printf("Unsupported number of rx antennas %d\n", n_rx); - exit(-1); + printf("Unsupported number of rx antennas %d\n", n_rx); + exit(-1); } - break; case 'F': input_fd = fopen(optarg, "r"); - if (input_fd == NULL) { - printf("Problem with filename %s\n", optarg); - exit(-1); + printf("Problem with filename %s\n", optarg); + exit(-1); } - break; case 'G': @@ -571,13 +565,10 @@ int main(int argc, char **argv) break; case 'Z': - transformPrecoder = transformPrecoder_enabled; num_dmrs_cdm_grps_no_data = 2; mcs_table = 3; - printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transformPrecoder); - break; default: @@ -1300,15 +1291,6 @@ int main(int argc, char **argv) ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); // convert to fixed point ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); - #if 0 - ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i])); // convert to fixed point - ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i])); - - - ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) ] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i) ]; // convert to fixed point - ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1] = ((int16_t*) &UE->common_vars.txdata[ap][slot_offset])[(2*i)+1 ]; - #endif - /* Add phase noise if enabled */ if (pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { phase_noise(ts, &((int16_t*)&gNB->common_vars.rxdata[ap][slot_offset])[(2*i)], @@ -1451,56 +1433,6 @@ int main(int argc, char **argv) &gNB->pusch_vars[0]->llr_layers[3][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); } - #if 0 - if (n_rx == 2) { - LOG_MM("rxsigF0_comp.m","rxsF1_comp", - &gNB->pusch_vars[0]->rxdataF_comp[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("rxsigF0_ext.m","rxsF1_ext", - &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chestF0_ext.m","chF1_ext", - &gNB->pusch_vars[0]->ul_ch_estimates_ext[1][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], - (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagF0.m","chmF1", - &gNB->pusch_vars[0]->ul_ch_mag[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagbF0.m","chmbF1", - &gNB->pusch_vars[0]->ul_ch_magb[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - } else if (n_rx == 4) { - LOG_MM("rxsigF0_comp.m","rxsF1_comp", - &gNB->pusch_vars[0]->rxdataF_comp[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("rxsigF0_comp.m","rxsF2_comp", - &gNB->pusch_vars[0]->rxdataF_comp[2][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("rxsigF0_comp.m","rxsF3_comp", - &gNB->pusch_vars[0]->rxdataF_comp[3][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("rxsigF0_ext.m","rxsF1_ext", - &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("rxsigF0_ext.m","rxsF2_ext", - &gNB->pusch_vars[0]->rxdataF_ext[2][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("rxsigF0_ext.m","rxsF3_ext", - &gNB->pusch_vars[0]->rxdataF_ext[3][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chestF0_ext.m","chF1_ext", - &gNB->pusch_vars[0]->ul_ch_estimates_ext[1][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], - (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chestF0_ext.m","chF2_ext", - &gNB->pusch_vars[0]->ul_ch_estimates_ext[2][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], - (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chestF0_ext.m","chF3_ext", - &gNB->pusch_vars[0]->ul_ch_estimates_ext[3][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], - (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagF0.m","chmF1", - &gNB->pusch_vars[0]->ul_ch_mag[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagF0.m","chmF2", - &gNB->pusch_vars[0]->ul_ch_mag[2][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagF0.m","chmF3", - &gNB->pusch_vars[0]->ul_ch_mag[3][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagbF0.m","chmbF1", - &gNB->pusch_vars[0]->ul_ch_magb[1][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagbF0.m","chmbF2", - &gNB->pusch_vars[0]->ul_ch_magb[2][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_MM("chmagbF0.m","chmbF3", - &gNB->pusch_vars[0]->ul_ch_magb[3][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - } - #endif - LOG_M("rxsigF0_llr.m","rxsF0_llr", &gNB->pusch_vars[0]->llr[0],precod_nbr_layers*(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); } From 68d4daf7d9c9a7eb8d4787db40b4328ded1a488f Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 11 Jan 2022 10:56:34 +0000 Subject: [PATCH 054/233] Fix variable wrongly replaced during the merge with develop --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 456e9e3ce2..02ed905a81 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -624,8 +624,8 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr RA_config_t *ra = &mac->ra; fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request; uint8_t is_Msg3 = 0; - NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id ; - NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id ; + NR_BWP_Id_t dl_bwp_id = mac->DL_BWP_Id; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; int default_abc = 1; uint16_t n_RB_DLBWP; @@ -912,14 +912,14 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr if (dci->tpc == 3) dlsch_config_pdu_1_0->accumulated_delta_PUCCH = 3; // Sanity check for pucch_resource_indicator value received to check for false DCI. valid = 0; - if (mac->ULbwp[dl_bwp_id-1] && - mac->ULbwp[dl_bwp_id-1]->bwp_Dedicated && - mac->ULbwp[dl_bwp_id-1]->bwp_Dedicated->pucch_Config && - mac->ULbwp[dl_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup&& - mac->ULbwp[dl_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList) { - pucch_res_set_cnt = mac->ULbwp[dl_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; + if (mac->ULbwp[ul_bwp_id-1] && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config && + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup&& + mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList) { + pucch_res_set_cnt = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.count; for (int id = 0; id < pucch_res_set_cnt; id++) { - if (dci->pucch_resource_indicator < mac->ULbwp[dl_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { + if (dci->pucch_resource_indicator < mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated->pucch_Config->choice.setup->resourceSetToAddModList->list.array[id]->resourceList.list.count) { valid = 1; break; } From 20b6ee70ebd7bfa6794d2d35261741f5d769126b Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 11 Jan 2022 10:57:16 +0000 Subject: [PATCH 055/233] Fix build warning after merge with develop --- openair2/RRC/LTE/MESSAGES/asn1_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/RRC/LTE/MESSAGES/asn1_msg.c b/openair2/RRC/LTE/MESSAGES/asn1_msg.c index b1f2e6d9b3..b069591316 100644 --- a/openair2/RRC/LTE/MESSAGES/asn1_msg.c +++ b/openair2/RRC/LTE/MESSAGES/asn1_msg.c @@ -4293,7 +4293,7 @@ ssize_t do_nrMeasurementReport(uint8_t *buffer, LTE_MeasResultListEUTRA_t *measResultListEUTRA2=&measResultNeighCells->choice.measResultListEUTRA; asn1cSequenceAdd(measResultListEUTRA2->list, struct LTE_MeasResultEUTRA, measresulteutra_list); measresulteutra_list->physCellId = phy_id; - asn1cCalloc(measresulteutra_list->cgi_Info, measresult_cgi2); + //asn1cCalloc(measresulteutra_list->cgi_Info, measresult_cgi2); //measresult_cgi2->cellGlobalId= {0}; //measresult_cgi2->trackingAreaCode= {0}; struct LTE_MeasResultEUTRA__measResult* measResult= &measresulteutra_list->measResult; From 296357fe1ba8256e7752b09c3882667fee5f36fd Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 11 Jan 2022 14:27:07 +0000 Subject: [PATCH 056/233] BWP fixes --- openair2/GNB_APP/gnb_config.c | 5 +++-- openair2/LAYER2/NR_MAC_UE/config_ue.c | 8 +++---- .../NR_MAC_UE/nr_ue_dci_configuration.c | 6 +++--- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 4 ++-- openair2/RRC/NR/rrc_gNB_reconfig.c | 21 +++++++++++-------- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 4 ++-- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 0c2cfd872b..2c3e8be1e5 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -372,8 +372,9 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { scd->firstActiveDownlinkBWP_Id = CALLOC(1, sizeof(*scd->firstActiveDownlinkBWP_Id)); *scd->firstActiveDownlinkBWP_Id = 1; *scd->uplinkConfig->firstActiveUplinkBWP_Id = 1; - - + scd->defaultDownlinkBWP_Id = CALLOC(1, sizeof(*scd->defaultDownlinkBWP_Id)); + *scd->defaultDownlinkBWP_Id = 0; + for (int j = 0 ;j < 4 ; j++) { diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index cf5f74ad4b..c98338e413 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -467,10 +467,10 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format if (bwp_ind && dci_format){ switch(*dci_format){ case NR_UL_DCI_FORMAT_0_1: - mac->UL_BWP_Id = n_ubwp < 4 ? *bwp_ind : *bwp_ind + 1;; + mac->UL_BWP_Id = n_ubwp < 4 ? *bwp_ind : *bwp_ind + 1; break; case NR_DL_DCI_FORMAT_1_1: - mac->DL_BWP_Id = n_ubwp < 4 ? *bwp_ind : *bwp_ind + 1;; + mac->DL_BWP_Id = n_ubwp < 4 ? *bwp_ind : *bwp_ind + 1; break; default: LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); @@ -587,7 +587,7 @@ void configure_ss_coreset(NR_UE_MAC_INST_t *mac, AssertFatal(ss->searchSpaceType != NULL, "ss->searchSpaceType is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot->buf != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id][ss->searchSpaceId - 1] = ss; + mac->SSpace[dl_bwp_id-1][ss->searchSpaceId - 1] = ss; } struct NR_PDCCH_ConfigCommon__commonSearchSpaceList *commonSearchSpaceList = pdcch_ConfigCommon->choice.setup->commonSearchSpaceList; @@ -602,7 +602,7 @@ void configure_ss_coreset(NR_UE_MAC_INST_t *mac, AssertFatal(css->searchSpaceType != NULL, "css->searchSpaceType is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot != NULL, "css->monitoringSymbolsWithinSlot is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot->buf != NULL, "css->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id][css->searchSpaceId - 1] = css; + mac->SSpace[dl_bwp_id-1][css->searchSpaceId - 1] = css; } } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index ad1aadb3f2..8119508751 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -102,7 +102,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t ss_id,mac->ra.ss->searchSpaceId); } else - ss = mac->SSpace[dl_bwp_id][ss_id-1]; + ss = mac->SSpace[dl_bwp_id-1][ss_id-1]; } else ss = mac->search_space_zero; @@ -275,9 +275,9 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl // loop over all available SS for bwp_id if (bwpd) { - for (ss_id = 1; ss_id <= FAPI_NR_MAX_SS && mac->SSpace[bwp_id][ss_id-1] != NULL; ss_id++){ + for (ss_id = 1; ss_id <= FAPI_NR_MAX_SS && mac->SSpace[bwp_id-1][ss_id-1] != NULL; ss_id++){ LOG_D(NR_MAC, "[DCI_CONFIG] ss_id %d\n",ss_id); - NR_SearchSpace_t *ss = mac->SSpace[bwp_id][ss_id-1]; + NR_SearchSpace_t *ss = mac->SSpace[bwp_id-1][ss_id-1]; AssertFatal(ss_id == ss->searchSpaceId,"SS IDs don't correspond\n"); fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 02ed905a81..33b231cfd9 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -1022,8 +1022,8 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr * 47 DMRS_SEQ_INI: */ - if (dci->bwp_indicator.val > 1) { - LOG_W(NR_MAC,"[%d.%d] bwp_indicator %d > 1 Possibly due to false DCI. Ignoring DCI!\n", frame, slot,dci->bwp_indicator.val); + if (dci->bwp_indicator.val > 4) { + LOG_W(NR_MAC,"[%d.%d] bwp_indicator %d > 4 Possibly due to false DCI. Ignoring DCI!\n", frame, slot,dci->bwp_indicator.val); return -1; } config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 653f15da66..beedceed58 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -678,15 +678,18 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } - -if (!servingcellconfigdedicated) { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; - } - secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; - secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = NULL; - secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; + + if (!servingcellconfigdedicated) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + } + + secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; + + if (!servingcellconfigdedicated) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; + } if (!servingcellconfigdedicated) { secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf index 670740ea49..2d8886bc46 100644 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf @@ -203,7 +203,7 @@ servingCellConfigDedicated = ({ # BWP 4 Configuration dl_bwp-Id_4 = 4; - dl_bwp4_locationAndBandwidth = 28899; // RBstart=24, L=106 + dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp4_subcarrierSpacing = 1; @@ -241,7 +241,7 @@ servingCellConfigDedicated = ({ # BWP 4 Configuration ul_bwp-Id_4 = 4; - ul_bwp4_locationAndBandwidth = 28899; // RBstart=24, L=106 + ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp4_subcarrierSpacing = 1; From 2bab0057994f0d34208de034f7b228a577cd825d Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 11 Jan 2022 17:59:30 +0000 Subject: [PATCH 057/233] Remove unused BWPs --- openair2/GNB_APP/RRC_nr_paramsvalues.h | 1 - openair2/GNB_APP/gnb_config.c | 66 +++++++--- openair2/LAYER2/NR_MAC_UE/config_ue.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 4 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 5 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 2 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 2 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 4 +- openair2/RRC/NR/rrc_gNB_reconfig.c | 120 ++++++++---------- 11 files changed, 114 insertions(+), 102 deletions(-) diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index 045e48ddbd..089569f84e 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -160,7 +160,6 @@ #define GNB_CONFIG_STRING_SCS_ULBWP4 "ul_bwp4_subcarrierSpacing" #define GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID "firstActiveUplinkBWP-Id" - /*--------------------------------------------------------------------------------------------------------------------*/ /* pdcch_ConfigSIB1 parameters */ /*--------------------------------------------------------------------------------------------------------------------*/ diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 2c3e8be1e5..c27727286b 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -375,8 +375,7 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { scd->defaultDownlinkBWP_Id = CALLOC(1, sizeof(*scd->defaultDownlinkBWP_Id)); *scd->defaultDownlinkBWP_Id = 0; -for (int j = 0 ;j < 4 ; j++) -{ +for (int j = 0; j < NR_MAX_NUM_BWP; j++) { // Downlink bandwidth part NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); @@ -384,8 +383,6 @@ for (int j = 0 ;j < 4 ; j++) // Allocate downlink dedicated bandwidth part and PDSCH structures bwp->bwp_Common = calloc(1, sizeof(*bwp->bwp_Common)); - // bwp->bwp_Common->genericParameters.locationAndBandwidth = calloc(1, sizeof(*bwp->bwp_Common->genericParameters.locationAndBandwidth) ); - // bwp->bwp_Common->genericParameters.subcarrierSpacing = calloc(1, sizeof(*bwp->bwp_Common->genericParameters.subcarrierSpacing) ); bwp->bwp_Common->pdcch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Dedicated = calloc(1, sizeof(*bwp->bwp_Dedicated)); @@ -458,14 +455,15 @@ for (int j = 0 ;j < 4 ; j++) } } -/* This function checks dedicated serving cell configuration and performs fixes as needed */ +/* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { + // Check for DL PTRS parameters validity - for (int bwp_i = 0 ; bwp_i< 4; bwp_i++) - { + for (int bwp_i = 0 ; bwp_idownlinkBWP_ToAddModList->list.count; bwp_i++) { + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.count-1; i >= 0; i--) { + for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.count - 1; i >= 0; i--) { if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] < 1) || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] > 276)) { LOG_I(RRC, "DL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); @@ -475,9 +473,11 @@ void fix_scd(NR_ServingCellConfig_t *scd) { } } } + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.count-1; i >= 0; i--) { + for (int i = + scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.count - 1; i >= 0; i--) { if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] < 0) || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] > 29)) { LOG_I(RRC, "DL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); @@ -487,6 +487,7 @@ void fix_scd(NR_ServingCellConfig_t *scd) { } } } + if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { if (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset > 2) { LOG_I(RRC, "Freeing DL PTRS resourceElementOffset \n"); @@ -499,11 +500,15 @@ void fix_scd(NR_ServingCellConfig_t *scd) { scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio = NULL; } } + } + + // Check for UL PTRS parameters validity + for (int bwp_i = 0 ; bwp_iuplinkConfig->uplinkBWP_ToAddModList->list.count; bwp_i++) { if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.count-1; i >= 0; i--) { - if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) + if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] > 276)) { LOG_I(RRC, "UL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); @@ -525,14 +530,16 @@ void fix_scd(NR_ServingCellConfig_t *scd) { } } } + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { - // Check for UL PTRS parameters validity - if (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { - LOG_I(RRC, "Freeing UL PTRS resourceElementOffset \n"); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; + // Check for UL PTRS parameters validity + if (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { + LOG_I(RRC, "Freeing UL PTRS resourceElementOffset \n"); + free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; + } } - } + } } @@ -987,11 +994,9 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { prepare_scd(scd); paramdef_t SCDsParams[] = SCDPARAMS_DESC(scd); paramlist_def_t SCDsParamList = {GNB_CONFIG_STRING_SERVINGCELLCONFIGDEDICATED, NULL, 0}; - ////////// Physical parameters - /* get global parameters, defined outside any section in the config file */ config_get( GNBSParams,sizeof(GNBSParams)/sizeof(paramdef_t),NULL); @@ -1081,6 +1086,31 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { (int)scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth ); } + + for(int bb = 0; bbuplinkConfig->uplinkBWP_ToAddModList->list.count; bb++) { + printf("scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bb]->bwp_Common->genericParameters = %li\n", + scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bb]->bwp_Common->genericParameters.locationAndBandwidth); + } + + // Remove unused BWPs + int b = 0; + while (bdownlinkBWP_ToAddModList->list.count) { + if (scd->downlinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == 0) { + asn_sequence_del(&scd->downlinkBWP_ToAddModList->list,b,1); + } else { + b++; + } + } + + b = 0; + while (buplinkConfig->uplinkBWP_ToAddModList->list.count) { + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == 0) { + asn_sequence_del(&scd->uplinkConfig->uplinkBWP_ToAddModList->list,b,1); + } else { + b++; + } + } + fix_scd(scd); printf("NRRRC %d: Southbound Transport %s\n",i,*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr)); diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index c98338e413..49aff8d58b 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -476,9 +476,7 @@ void config_bwp_ue(NR_UE_MAC_INST_t *mac, uint16_t *bwp_ind, uint8_t *dci_format LOG_E(MAC, "In %s: failed to configure BWP Id from DCI with format %d \n", __FUNCTION__, *dci_format); } // configure ss coreset after switching BWP - configure_ss_coreset(mac, - scd, - mac->DL_BWP_Id); + configure_ss_coreset(mac, scd, mac->DL_BWP_Id); } else { if (scd->firstActiveDownlinkBWP_Id) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 33b231cfd9..ac5758729b 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -1022,8 +1022,8 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr * 47 DMRS_SEQ_INI: */ - if (dci->bwp_indicator.val > 4) { - LOG_W(NR_MAC,"[%d.%d] bwp_indicator %d > 4 Possibly due to false DCI. Ignoring DCI!\n", frame, slot,dci->bwp_indicator.val); + if (dci->bwp_indicator.val > NR_MAX_NUM_BWP) { + LOG_W(NR_MAC,"[%d.%d] bwp_indicator %d > NR_MAX_NUM_BWP Possibly due to false DCI. Ignoring DCI!\n", frame, slot,dci->bwp_indicator.val); return -1; } config_bwp_ue(mac, &dci->bwp_indicator.val, &dci_format); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 9706e09e42..9c3d851a2a 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -684,16 +684,14 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, int target_ss; bool valid_ptrs_setup = 0; - uint16_t n_RB_ULBWP; - n_RB_ULBWP = NRRIV2BW(ubwpc->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + uint16_t n_RB_ULBWP = NRRIV2BW(ubwpc->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); pusch_config_pdu->bwp_start = NRRIV2PRBOFFSET(ubwpc->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); pusch_config_pdu->bwp_size = n_RB_ULBWP; AssertFatal(ubwpd->pusch_Config != NULL,"pusch_Config shouldn't be null\n"); NR_PUSCH_Config_t *pusch_Config = ubwpd->pusch_Config->choice.setup; - // Basic sanity check for MCS value to check for a false or erroneous DCI if (dci->mcs > 28) { LOG_W(NR_MAC, "MCS value %d out of bounds! Possibly due to false DCI. Ignoring DCI!\n", dci->mcs); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 1bfc90b357..6098cc9d20 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -307,9 +307,8 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, } memset(RC.nrmac[module_idP]->cce_list[0][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - memset(RC.nrmac[module_idP]->cce_list[0][1],0,MAX_NUM_CCE*sizeof(int)); // coreset1 on initialBWP - for(int i_bwp = 1; i_bwp < MAX_NUM_BWP; i_bwp++) - memset(RC.nrmac[module_idP]->cce_list[i_bwp][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid i + for(int i_bwp = 0; i_bwp < MAX_NUM_BWP; i_bwp++) + memset(RC.nrmac[module_idP]->cce_list[i_bwp][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid i (0 to coreset1 on initialBWP) NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 4a31da7f75..95a9b4e7ea 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1120,7 +1120,7 @@ void nr_schedule_ue_spec(module_id_t module_id, memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t)); // bwp indicator const int n_dl_bwp = bwp ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count : 0; - AssertFatal(n_dl_bwp <= 4, "downlinkBWP_ToAddModList has %d BWP!\n", n_dl_bwp); + AssertFatal(n_dl_bwp <= NR_MAX_NUM_BWP, "downlinkBWP_ToAddModList has %d BWP!\n", n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 dci_payload.bwp_indicator.val = bwp ? (n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1) : 0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 6dff4a4a6d..5a46081d3c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -297,7 +297,7 @@ void nr_preprocessor_phytest(module_id_t module_id, while (rbStart + rbSize < bwpSize && !vrb_map[rbStart + rbSize + BWPStart] && rbSize < target_dl_bw) rbSize++; /* found target_dl_bw? */ - if (rbStart+ rbSize == target_dl_bw) + if (rbStart+rbSize == target_dl_bw) break; /* at end and below target_dl_bw? */ if (rbStart + rbSize >= bwpSize) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 449acb29e0..bf1236e017 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1969,7 +1969,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG /* Set default BWPs */ const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; - if (bwpList) AssertFatal(bwpList->list.count <= 4, + if (bwpList) AssertFatal(bwpList->list.count <= NR_MAX_NUM_BWP, "downlinkBWP_ToAddModList has %d BWP!\n", bwpList->list.count); const int bwp_id = servingCellConfig ? *servingCellConfig->firstActiveDownlinkBWP_Id : 0; @@ -1982,7 +1982,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG sched_ctrl->active_bwp ? (void*)sched_ctrl->active_bwp->bwp_Dedicated : NULL, sched_ctrl->search_space, target_ss); const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; - if (ubwpList) AssertFatal(ubwpList->list.count <= 4, + if (ubwpList) AssertFatal(ubwpList->list.count <= NR_MAX_NUM_BWP, "uplinkBWP_ToAddModList has %d BWP!\n", ubwpList->list.count); const int ul_bwp_id = servingCellConfig ? *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index 7ae7faadff..f8aa8fc09f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -623,7 +623,7 @@ int checkTargetSSBInTCIStates_pdcchConfig(int ssb_index_t, int Mod_idP, int UE_i int nb_tci_states = CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.count; NR_TCI_State_t *tci =NULL; NR_TCI_StateId_t *tci_id = NULL; - int bwp_id = 1;//TODO + int bwp_id = 1; //TODO NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; NR_ControlResourceSet_t *coreset = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]; int i; @@ -691,7 +691,7 @@ void tci_handling(module_id_t Mod_idP, int UE_id, frame_t frame, slot_t slot) { int ssb_index[MAX_NUM_SSB] = {0}; int ssb_rsrp[MAX_NUM_SSB] = {0}; uint8_t idx = 0; - int bwp_id = 1;//TODO + int bwp_id = 1; //TODO NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index beedceed58..0ed0710340 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -370,27 +370,31 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; NR_BWP_Downlink_t *bwp = NULL; - for (int bwp_loop = 0; bwp_loop < 4 ; bwp_loop++ ) - { - + + int n_bwp = 1; + if (servingcellconfigdedicated) { + n_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; + } + + for (int bwp_loop = 0; bwp_loop < n_bwp; bwp_loop++) { + + /// Downlink BWPs + if (servingcellconfigdedicated) { - bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; + bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; } else { if(secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList == NULL){ secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); } bwp = calloc(1,sizeof(*bwp)); - bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); bwp->bwp_Id = bwp_loop + 1; - } - - if ( bwp->bwp_Common->genericParameters.subcarrierSpacing == 0 && bwp->bwp_Common->genericParameters.locationAndBandwidth == 0 ) { + bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); // copy common BWP size from initial BWP except for bandwdith memcpy((void*)&bwp->bwp_Common->genericParameters, &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, sizeof(bwp->bwp_Common->genericParameters)); bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - bwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + bwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); } bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); @@ -449,7 +453,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco *ss->controlResourceSetId=1; ss->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*ss->monitoringSlotPeriodicityAndOffset)); ss->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; - ss->duration=NULL; + ss->duration=NULL; ss->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss->monitoringSymbolsWithinSlot)); ss->monitoringSymbolsWithinSlot->buf = calloc(1,2); // should be '1100 0000 0000 00'B (LSB first!), first two symols in slot, adjust if needed @@ -487,7 +491,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); // copy PDSCH TimeDomainResourceAllocation from InitialBWP - NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; for (int i=0;idownlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) { pdschi= calloc(1,sizeof(*pdschi)); @@ -500,19 +503,16 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); } - if (!servingcellconfigdedicated) { bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); } - bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, - coreset); + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset); bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); @@ -547,20 +547,16 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ss2->searchSpaceType->choice.ue_Specific = calloc(1,sizeof(*ss2->searchSpaceType->choice.ue_Specific)); ss2->searchSpaceType->choice.ue_Specific->dci_Formats=NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_1_And_1_1; - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, - ss2); - + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss2); bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToReleaseList = NULL; if (!servingcellconfigdedicated) { bwp->bwp_Dedicated->pdsch_Config = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); - bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); } bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; @@ -597,7 +593,36 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco } } - + bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; + if (!get_softmodem_params()->use_256qam_table) { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; + } else { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = NR_PDSCH_Config__mcs_Table_qam256; + } + bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = + calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; @@ -629,38 +654,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; - if (!get_softmodem_params()->use_256qam_table) { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; - } else { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = NR_PDSCH_Config__mcs_Table_qam256; - } - bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = - calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; bwp->bwp_Dedicated->sps_Config = NULL; //calloc(1,sizeof(struct NR_SetupRelease_SPS_Config)); - bwp->bwp_Dedicated->radioLinkMonitoringConfig = NULL; #if 0 bwp->bwp_Dedicated->radioLinkMonitoringConfig = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig)); @@ -674,7 +668,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer)); *bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer = NR_RadioLinkMonitoringConfig__beamFailureDetectionTimer_pbfd2; #endif - + if (!servingcellconfigdedicated) { ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } @@ -691,6 +685,8 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; } + /// Uplink BWPs + if (!servingcellconfigdedicated) { secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); } @@ -706,7 +702,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco } else { pusch_Config = calloc(1,sizeof(*pusch_Config)); } - initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; @@ -787,7 +782,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco else transform_precoding = *pusch_Config->transformPrecoder; - if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled ) { /* Enable DMRS uplink config for transform precoding enabled */ NR_DMRS_UplinkConfig->transformPrecodingEnabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingEnabled)); @@ -799,14 +793,13 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco LOG_I(RRC,"TRANSFORM PRECODING ENABLED......\n"); } - /*----------------------------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------------------------*/ initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); initialUplinkBWP->srs_Config->choice.setup=srs_Config; srs_Config->srs_ResourceSetToReleaseList=NULL; - srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); srs_resset0->srs_ResourceSetId = 0; @@ -836,7 +829,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_res0->srs_ResourceId=0; srs_res0->nrofSRS_Ports=NR_SRS_Resource__nrofSRS_Ports_port1; srs_res0->ptrs_PortIndex=NULL; - srs_res0->transmissionComb.present=NR_SRS_Resource__transmissionComb_PR_n2; + srs_res0->transmissionComb.present=NR_SRS_Resource__transmissionComb_PR_n2; srs_res0->transmissionComb.choice.n2=calloc(1,sizeof(*srs_res0->transmissionComb.choice.n2)); srs_res0->transmissionComb.choice.n2->combOffset_n2=0; srs_res0->transmissionComb.choice.n2->cyclicShift_n2=0; @@ -858,7 +851,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; NR_BWP_Uplink_t *ubwp = NULL; if (servingcellconfigdedicated) { @@ -868,11 +860,10 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); } ubwp = calloc(1,sizeof(*ubwp)); - ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); ubwp->bwp_Id = bwp_loop + 1; - } - if ( ubwp->bwp_Common->genericParameters.subcarrierSpacing == 0 && ubwp->bwp_Common->genericParameters.locationAndBandwidth == 0 ) { + ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); + // copy bwp_Common from Initial UL BWP except for bandwidth memcpy((void*)&ubwp->bwp_Common->genericParameters, (void*)&servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters, @@ -884,12 +875,11 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ubwp->bwp_Common->rach_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon; ubwp->bwp_Common->pusch_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; ubwp->bwp_Common->pucch_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; - + if (!servingcellconfigdedicated) { ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); } - ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); @@ -1047,9 +1037,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco pusch_scc->ext1->processingType2Enabled = NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching = NULL; - secondaryCellGroup->spCellConfig->spCellConfigDedicated->supplementaryUplink=NULL; - secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdcch_ServingCellConfig=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig)); From 4f6938839adbef97c46537ae288852ae24af963e Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 11 Jan 2022 18:24:52 +0000 Subject: [PATCH 058/233] Remove hardcoded controlResourceSetId --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 9 +++++---- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 3 ++- .../LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 12 ++++++++---- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 9 ++++++--- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 3 ++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 3 ++- .../LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 14 ++++++++------ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 3 ++- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 6 ++++-- 9 files changed, 39 insertions(+), 23 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 3b6ebb1391..8bb30afabf 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2536,9 +2536,10 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, const NR_CellGroupConfig_t *cg, dci_pdu_rel15_t *dci_pdu, nr_dci_format_t format, - nr_rnti_type_t rnti_type, - uint16_t N_RB, - int bwp_id) { + nr_rnti_type_t rnti_type, + uint16_t N_RB, + int bwp_id, + NR_ControlResourceSetId_t controlResourceSetId) { uint16_t size = 0; uint16_t numRBG = 0; @@ -2586,7 +2587,7 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, /// fixed: Format identifier 1, Hop flag 1, MCS 5, NDI 1, RV 2, HARQ PID 4, PUSCH TPC 2 Time Domain assgnmt 4 --20 size += 20; size += (uint8_t)ceil( log2( (N_RB*(N_RB+1))>>1 ) ); // Freq domain assignment -- hopping scenario to be updated - size += nr_dci_size(initialDownlinkBWP,initialUplinkBWP,cg,dci_pdu,NR_DL_DCI_FORMAT_1_0, rnti_type, N_RB, bwp_id) - size; // Padding to match 1_0 size + size += nr_dci_size(initialDownlinkBWP,initialUplinkBWP,cg,dci_pdu,NR_DL_DCI_FORMAT_1_0, rnti_type, N_RB, bwp_id, controlResourceSetId) - size; // Padding to match 1_0 size // UL/SUL indicator assumed to be 0 break; diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index 8182e1c930..2beb87ac7a 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -54,7 +54,8 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDLBWP, nr_dci_format_t format, nr_rnti_type_t rnti_type, uint16_t N_RB, - int bwp_id); + int bwp_id, + NR_ControlResourceSetId_t controlResourceSetId); void find_aggregation_candidates(uint8_t *aggregation_level, uint8_t *nr_of_candidates, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 8119508751..3cca55b7c7 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -169,7 +169,8 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->SubcarrierSpacing = bwp_Common->genericParameters.subcarrierSpacing; } for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, dl_bwp_id); + rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], + rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, dl_bwp_id, coreset_id); } break; case NR_RNTI_RA: @@ -185,7 +186,8 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPStart = NRRIV2PRBOFFSET(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; - rel15->dci_length_options[0] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, dl_bwp_id); + rel15->dci_length_options[0] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], + rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, dl_bwp_id, coreset_id); break; case NR_RNTI_P: break; @@ -200,7 +202,8 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPStart = mac->type0_PDCCH_CSS_config.cset_start_rb; rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_TC, rel15->BWPSize, dl_bwp_id); + rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], + rel15->dci_format_options[i], NR_RNTI_TC, rel15->BWPSize, dl_bwp_id, coreset_id); } break; case NR_RNTI_SP_CSI: @@ -223,7 +226,8 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon + 2; for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_SI, rel15->BWPSize, 0); + rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], + rel15->dci_format_options[i], NR_RNTI_SI, rel15->BWPSize, 0, coreset_id); } break; case NR_RNTI_SFI: diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 41e69a876e..917d0cd0dd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -851,7 +851,8 @@ void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t NR_UL_DCI_FORMAT_0_0, NR_RNTI_TC, pusch_pdu->bwp_size, - ra->ul_bwp_id); + ra->ul_bwp_id, + coreset->controlResourceSetId); // Mark the corresponding RBs as used for (int rb = 0; rb < ra->msg3_nb_rb; rb++) { @@ -1360,7 +1361,8 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra NR_DL_DCI_FORMAT_1_0, NR_RNTI_RA, BWPSize, - bwpid); + bwpid, + coreset->controlResourceSetId); // DL TX request nfapi_nr_pdu_t *tx_req = &nr_mac->TX_req[CC_id].pdu_list[nr_mac->TX_req[CC_id].Number_of_PDUs]; @@ -1737,7 +1739,8 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra NR_DL_DCI_FORMAT_1_0, NR_RNTI_TC, pdsch_pdu_rel15->BWPSize, - bwpid); + bwpid, + coreset->controlResourceSetId); // Add padding header and zero rest out if there is space left if (ra->mac_pdu_length < harq->tb_size) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index df25b085e6..b941cb96f9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -535,7 +535,8 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP, dci_format, rnti_type, pdsch_pdu_rel15->BWPSize, - 0); + 0, + gNB_mac->sched_ctrlCommon->coreset->controlResourceSetId); LOG_D(MAC,"BWPSize: %i\n", pdcch_pdu_rel15->BWPSize); LOG_D(MAC,"BWPStart: %i\n", pdcch_pdu_rel15->BWPStart); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 95a9b4e7ea..6aabbec5dd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1178,7 +1178,8 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_format, rnti_type, pdsch_pdu->BWPSize, - bwp? bwp->bwp_Id : 0); + bwp? bwp->bwp_Id : 0, + sched_ctrl->coreset->controlResourceSetId); LOG_D(NR_MAC, "coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index bf1236e017..6083b197d8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1062,7 +1062,8 @@ void nr_configure_pucch(nfapi_nr_pucch_pdu_t* pucch_pdu, void prepare_dci(const NR_CellGroupConfig_t *CellGroup, dci_pdu_rel15_t *dci_pdu_rel15, nr_dci_format_t format, - int bwp_id) { + int bwp_id, + NR_ControlResourceSetId_t controlResourceSetId) { AssertFatal(CellGroup!=NULL,"CellGroup shouldn't be null here\n"); @@ -1116,8 +1117,7 @@ void prepare_dci(const NR_CellGroupConfig_t *CellGroup, if (pdsch_Config->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList != NULL) AssertFatal(1==0,"Aperiodic ZP CSI-RS currently not supported\n"); // transmission configuration indication - int corset_id = 1; //TODO : corset_id to come from higher layers - if (pdcch_Config->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI != NULL) + if (pdcch_Config->controlResourceSetToAddModList->list.array[controlResourceSetId-1]->tci_PresentInDCI != NULL) AssertFatal(1==0,"TCI in DCI currently not supported\n"); //srs resource set if (CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching!=NULL) { @@ -1159,16 +1159,18 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, int dci_format, int rnti_type, int N_RB, - int bwp_id) { + int bwp_id, + NR_ControlResourceSetId_t controlResourceSetId) { uint8_t fsize = 0, pos = 0; uint64_t *dci_pdu = (uint64_t *)pdcch_dci_pdu->Payload; *dci_pdu=0; - int dci_size = nr_dci_size(scc->downlinkConfigCommon->initialDownlinkBWP,scc->uplinkConfigCommon->initialUplinkBWP, CellGroup, dci_pdu_rel15, dci_format, rnti_type, N_RB, bwp_id); + int dci_size = nr_dci_size(scc->downlinkConfigCommon->initialDownlinkBWP,scc->uplinkConfigCommon->initialUplinkBWP, + CellGroup, dci_pdu_rel15, dci_format, rnti_type, N_RB, bwp_id, controlResourceSetId); pdcch_dci_pdu->PayloadSizeBits = dci_size; AssertFatal(dci_size <= 64, "DCI sizes above 64 bits not yet supported"); if (dci_format == NR_DL_DCI_FORMAT_1_1 || dci_format == NR_UL_DCI_FORMAT_0_1) - prepare_dci(CellGroup, dci_pdu_rel15, dci_format, bwp_id); + prepare_dci(CellGroup, dci_pdu_rel15, dci_format, bwp_id, controlResourceSetId); /// Payload generation switch (dci_format) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index bde6c6109d..b61c68c813 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1731,7 +1731,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) ps->dci_format, rnti_types[0], pusch_pdu->bwp_size, - bwpid); + bwpid, + coreset->controlResourceSetId); memset(sched_pusch, 0, sizeof(*sched_pusch)); } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 91221276b0..efb511948c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -263,12 +263,14 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, int dci_formats, int rnti_types, int N_RB, - int bwp_id); + int bwp_id, + NR_ControlResourceSetId_t controlResourceSetId); void prepare_dci(const NR_CellGroupConfig_t *CellGroup, dci_pdu_rel15_t *dci_pdu_rel15, nr_dci_format_t format, - int bwp_id); + int bwp_id, + NR_ControlResourceSetId_t controlResourceSetId); /* find coreset within the search space */ NR_ControlResourceSet_t *get_coreset(module_id_t module_idP, From 212f00fce7153d867b91905745a0c06113321167 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 11 Jan 2022 19:03:35 +0000 Subject: [PATCH 059/233] Fix segmentation faults for SA --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index ac5758729b..e909be2a55 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -629,7 +629,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr int default_abc = 1; uint16_t n_RB_DLBWP; - if (mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else if (mac->scc_SIB) n_RB_DLBWP = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); else n_RB_DLBWP = mac->type0_PDCCH_CSS_config.num_rbs; @@ -799,7 +799,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.rnti = rnti; fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config_pdu_1_0 = &dl_config->dl_config_list[dl_config->number_pdus].dlsch_config_pdu.dlsch_config_rel15; - NR_PDSCH_Config_t *pdsch_config= (mac->DLbwp[dl_bwp_id-1]) ? mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; + NR_PDSCH_Config_t *pdsch_config= (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1]) ? mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup : NULL; int is_common=0; if(rnti == SI_RNTI) { NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config = mac->type0_PDCCH_CSS_config; @@ -829,7 +829,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; } - } else if (mac->DLbwp[dl_bwp_id-1]) { + } else if (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1]) { dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; @@ -851,12 +851,13 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList = NULL; - if (mac->DLbwp[dl_bwp_id-1] && + if (dl_bwp_id>0 && + mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config && mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList->choice.setup; - else if (mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) + else if (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1] && mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList) pdsch_TimeDomainAllocationList = mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; else if (mac->scc_SIB && mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup) pdsch_TimeDomainAllocationList = mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList; @@ -874,7 +875,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->number_symbols, dlsch_config_pdu_1_0->start_symbol, mappingtype); - dlsch_config_pdu_1_0->dmrsConfigType = (mac->DLbwp[dl_bwp_id-1] != NULL) ? + dlsch_config_pdu_1_0->dmrsConfigType = (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1] != NULL) ? (mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1) : 0; /* number of DM-RS CDM groups without data according to subclause 5.1.6.2 of 3GPP TS 38.214 version 15.9.0 Release 15 */ if (dlsch_config_pdu_1_0->number_symbols == 2) @@ -2659,7 +2660,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, #endif // check BWP id - if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB=NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); // Freq domain assignment (275rb >> fsize = 16) @@ -2842,7 +2843,7 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, case NR_RNTI_TC: // check BWP id - if (mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1]) N_RB=NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); else N_RB=mac->type0_PDCCH_CSS_config.num_rbs; From 6e4ff32cc547cf8e2befea610051a9a5c9cce1ac Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 12 Jan 2022 14:40:50 +0000 Subject: [PATCH 060/233] Add Dedicated Serving Cell Configuration to config files --- .../gnb.sa.band41.fr1.106PRB.usrpb210.conf | 72 +++++++++++++++++++ ...gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 72 +++++++++++++++++++ .../gnb.sa.band78.fr1.106PRB.usrpb210.conf | 72 +++++++++++++++++++ 3 files changed, 216 insertions(+) diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf index 4075fefcfe..afecad980c 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf @@ -173,6 +173,78 @@ gNBs = ); +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + dl_bwp-Id_2 = 2; + dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + dl_bwp-Id_3 = 3; + dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + dl_bwp-Id_4 = 4; + dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp4_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + ul_bwp-Id_2 = 2; + ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + ul_bwp-Id_3 = 3; + ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + ul_bwp-Id_4 = 4; + ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp4_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); # ------- SCTP definitions SCTP : diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index 1c57d9523f..99cb1c1712 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -172,6 +172,78 @@ gNBs = ); +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + dl_bwp-Id_2 = 2; + dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + dl_bwp-Id_3 = 3; + dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + dl_bwp-Id_4 = 4; + dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp4_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + ul_bwp-Id_2 = 2; + ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + ul_bwp-Id_3 = 3; + ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + ul_bwp-Id_4 = 4; + ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp4_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); # ------- SCTP definitions SCTP : diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index 88cd8e3a74..8ab3096a8b 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -173,6 +173,78 @@ gNBs = ); +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + dl_bwp-Id_2 = 2; + dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + dl_bwp-Id_3 = 3; + dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + dl_bwp-Id_4 = 4; + dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp4_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + ul_bwp-Id_2 = 2; + ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + ul_bwp-Id_3 = 3; + ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + ul_bwp-Id_4 = 4; + ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp4_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); # ------- SCTP definitions SCTP : From 4f50fcce2a5fb1d201d49b157f1c68be46b6afef Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 12 Jan 2022 17:21:20 +0000 Subject: [PATCH 061/233] Add dedicated BWPs in fill_initial_SpCellConfig --- openair2/LAYER2/NR_MAC_gNB/config.c | 5 +- openair2/RRC/NR/L2_nr_interface.c | 3 +- openair2/RRC/NR/MESSAGES/asn1_msg.c | 667 +++++++++++++++++++++++++--- openair2/RRC/NR/MESSAGES/asn1_msg.h | 17 +- openair2/RRC/NR/rrc_gNB.c | 6 +- 5 files changed, 635 insertions(+), 63 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index ae3a3bba24..3a793eb73f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -535,6 +535,9 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, if (CellGroup) { + // Preferred DL TDA for InitialBWP + calculate_preferred_dl_tda(Mod_idP, NULL); + const NR_ServingCellConfig_t *servingCellConfig = CellGroup->spCellConfig->spCellConfigDedicated; const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig->downlinkBWP_ToAddModList; @@ -544,8 +547,6 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, const NR_BWP_Downlink_t *bwp = bwpList->list.array[i]; calculate_preferred_dl_tda(Mod_idP, bwp); } - } else { - calculate_preferred_dl_tda(Mod_idP, NULL); } const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList; diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index eb402092b9..c81dd70928 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -323,9 +323,10 @@ int8_t nr_mac_rrc_data_ind(const module_id_t module_idP, // call do_RRCSetup like full procedure and extract masterCellGroup NR_CellGroupConfig_t cellGroupConfig; NR_ServingCellConfigCommon_t *scc=RC.nrrrc[module_idP]->carrier.servingcellconfigcommon; + NR_ServingCellConfig_t *servingcellconfigdedicated = RC.nrrrc[module_idP]->configuration.scd; memset(&cellGroupConfig,0,sizeof(cellGroupConfig)); - fill_initial_cellGroupConfig(rntiP,&cellGroupConfig,scc,&RC.nrrrc[module_idP]->carrier); + fill_initial_cellGroupConfig(rntiP,-1,&cellGroupConfig,scc,servingcellconfigdedicated,&RC.nrrrc[module_idP]->carrier); MessageDef* tmp=itti_alloc_new_message_sized(TASK_RRC_GNB, 0, F1AP_INITIAL_UL_RRC_MESSAGE, sizeof(f1ap_initial_ul_rrc_message_t) + sdu_lenP); f1ap_initial_ul_rrc_message_t *msg = &F1AP_INITIAL_UL_RRC_MESSAGE(tmp); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 84a0cf75d1..767f6a912e 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -39,6 +39,7 @@ #include #include /* for _ASN_DEFAULT_STACK_MAX */ #include +#include #include "asn1_msg.h" #include "../nr_rrc_proto.h" @@ -976,19 +977,331 @@ uint8_t do_RRCReject(uint8_t Mod_id, return((enc_rval.encoded+7)/8); } -void fill_initial_SpCellConfig(rnti_t rnti, - NR_SpCellConfig_t *SpCellConfig, - NR_ServingCellConfigCommon_t *scc, - rrc_gNB_carrier_data_t *carrier) { - int curr_bwp = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); - SpCellConfig->servCellIndex = NULL; - SpCellConfig->reconfigurationWithSync = NULL; - SpCellConfig->rlmInSyncOutOfSyncThreshold = NULL; - SpCellConfig->rlf_TimersAndConstants = NULL; - SpCellConfig->spCellConfigDedicated = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated)); - SpCellConfig->spCellConfigDedicated->uplinkConfig = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig)); +void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, + int bwp_loop, + NR_ServingCellConfig_t *servingcellconfigdedicated, + NR_ServingCellConfigCommon_t *scc, + rrc_gNB_carrier_data_t *carrier) { + + uint64_t bitmap=0; + switch (scc->ssb_PositionsInBurst->present) { + case 1 : + bitmap = ((uint64_t) scc->ssb_PositionsInBurst->choice.shortBitmap.buf[0])<<56; + break; + case 2 : + bitmap = ((uint64_t) scc->ssb_PositionsInBurst->choice.mediumBitmap.buf[0])<<56; + break; + case 3 : + for (int i=0; i<8; i++) { + bitmap |= (((uint64_t) scc->ssb_PositionsInBurst->choice.longBitmap.buf[i])<<((7-i)*8)); + } + break; + default: + AssertFatal(1==0,"SSB bitmap size value %d undefined (allowed values 1,2,3) \n", scc->ssb_PositionsInBurst->present); + } + + if (servingcellconfigdedicated) { + bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; + } else { + if(servingcellconfigdedicated->downlinkBWP_ToAddModList == NULL){ + servingcellconfigdedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*servingcellconfigdedicated->downlinkBWP_ToAddModList)); + } + bwp = calloc(1,sizeof(*bwp)); + bwp->bwp_Id = bwp_loop + 1; + bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); + // copy common BWP size from initial BWP except for bandwdith + memcpy((void*)&bwp->bwp_Common->genericParameters, + &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters, + sizeof(bwp->bwp_Common->genericParameters)); + bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + bwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + } + + bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); + bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet)); + + int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); + + NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); + coreset->controlResourceSetId=1; + // frequency domain resources depends on BWP size + // options are 24, 48 or 96 + coreset->frequencyDomainResources.buf = calloc(1,6); + if (curr_bwp < 48) + coreset->frequencyDomainResources.buf[0] = 0xf0; + else + coreset->frequencyDomainResources.buf[0] = 0xff; + if (curr_bwp < 96) + coreset->frequencyDomainResources.buf[1] = 0; + else + coreset->frequencyDomainResources.buf[1] = 0xff; + coreset->frequencyDomainResources.buf[2] = 0; + coreset->frequencyDomainResources.buf[3] = 0; + coreset->frequencyDomainResources.buf[4] = 0; + coreset->frequencyDomainResources.buf[5] = 0; + coreset->frequencyDomainResources.size = 6; + coreset->frequencyDomainResources.bits_unused = 3; + coreset->duration=1; + coreset->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; + coreset->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; + + coreset->tci_StatesPDCCH_ToAddList=calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); + NR_TCI_StateId_t *tci[64]; + for (int i=0;i<64;i++) { + if ((bitmap>>(63-i))&0x01){ + tci[i]=calloc(1,sizeof(*tci[i])); + *tci[i] = i; + ASN_SEQUENCE_ADD(&coreset->tci_StatesPDCCH_ToAddList->list,tci[i]); + } + } + coreset->tci_StatesPDCCH_ToReleaseList = NULL; + coreset->tci_PresentInDCI = NULL; + coreset->pdcch_DMRS_ScramblingID = NULL; + + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; + + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); + + NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); + ss->searchSpaceId = 1; + ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); + *ss->controlResourceSetId=1; + ss->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*ss->monitoringSlotPeriodicityAndOffset)); + ss->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; + ss->duration=NULL; + ss->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss->monitoringSymbolsWithinSlot)); + ss->monitoringSymbolsWithinSlot->buf = calloc(1,2); + // should be '1100 0000 0000 00'B (LSB first!), first two symols in slot, adjust if needed + ss->monitoringSymbolsWithinSlot->buf[1] = 0; + ss->monitoringSymbolsWithinSlot->buf[0] = (1<<7) | (1<<6); + ss->monitoringSymbolsWithinSlot->size = 2; + ss->monitoringSymbolsWithinSlot->bits_unused = 2; + ss->nrofCandidates = calloc(1,sizeof(*ss->nrofCandidates)); + ss->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; + ss->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; + ss->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; + ss->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; + ss->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; + ss->searchSpaceType = calloc(1,sizeof(*ss->searchSpaceType)); + ss->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_common; + ss->searchSpaceType->choice.common=calloc(1,sizeof(*ss->searchSpaceType->choice.common)); + ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0 = calloc(1,sizeof(*ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0)); + + ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list,ss); + + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=NULL; + if(get_softmodem_params()->sa) { + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1)); + *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=0; + } + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->pagingSearchSpace=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); + *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; + + bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); + bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; + bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); + bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); + + // copy PDSCH TimeDomainResourceAllocation from InitialBWP + NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; + for (int i=0;idownlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) { + pdschi= calloc(1,sizeof(*pdschi)); + if(scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0){ + pdschi->k0 = calloc(1,sizeof(*pdschi->k0)); + *pdschi->k0 = *scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0; + } + pdschi->mappingType = scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->mappingType; + pdschi->startSymbolAndLength = scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; + ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); + } + + if (!servingcellconfigdedicated) { + bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); + } + + bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); + bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; + bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); + bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); + + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset); + + bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); + + NR_SearchSpace_t *ss2 = calloc(1,sizeof(*ss2)); + + ss2->searchSpaceId=2; + ss2->controlResourceSetId=calloc(1,sizeof(*ss2->controlResourceSetId)); + *ss2->controlResourceSetId=1; + ss2->monitoringSlotPeriodicityAndOffset=calloc(1,sizeof(*ss2->monitoringSlotPeriodicityAndOffset)); + ss2->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; + ss2->monitoringSlotPeriodicityAndOffset->choice.sl1=(NULL_t)0; + ss2->duration=NULL; + ss2->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss2->monitoringSymbolsWithinSlot)); + ss2->monitoringSymbolsWithinSlot->buf = calloc(1,2); + ss2->monitoringSymbolsWithinSlot->size = 2; + ss2->monitoringSymbolsWithinSlot->bits_unused = 2; + ss2->monitoringSymbolsWithinSlot->buf[0]=0xc0; + ss2->monitoringSymbolsWithinSlot->buf[1]=0x0; + ss2->nrofCandidates=calloc(1,sizeof(*ss2->nrofCandidates)); + ss2->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; + ss2->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; + if (curr_bwp < 48) + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; + else if (curr_bwp < 96) + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n2; + else + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n4; + ss2->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; + ss2->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; + ss2->searchSpaceType=calloc(1,sizeof(*ss2->searchSpaceType)); + ss2->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_ue_Specific; + ss2->searchSpaceType->choice.ue_Specific = calloc(1,sizeof(*ss2->searchSpaceType->choice.ue_Specific)); + ss2->searchSpaceType->choice.ue_Specific->dci_Formats=NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_1_And_1_1; + + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss2); + + bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToReleaseList = NULL; + + if (!servingcellconfigdedicated) { + bwp->bwp_Dedicated->pdsch_Config = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); + bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; + bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); + } + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; + + if (carrier->pdsch_AntennaPorts > 1) // for MIMO, we use DMRS Config Type 2 + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type)); + else + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID0=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID1=NULL; + if (!servingcellconfigdedicated) { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS=NULL; + } + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0; + + bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList)); + + int n_ssb = 0; + NR_TCI_State_t *tcid[64]; + for (int i=0;i<64;i++) { + if ((bitmap>>(63-i))&0x01){ + tcid[i]=calloc(1,sizeof(*tcid[i])); + tcid[i]->tci_StateId=n_ssb; + tcid[i]->qcl_Type1.cell=NULL; + tcid[i]->qcl_Type1.bwp_Id=calloc(1,sizeof(*tcid[i]->qcl_Type1.bwp_Id)); + *tcid[i]->qcl_Type1.bwp_Id=1; + tcid[i]->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; + tcid[i]->qcl_Type1.referenceSignal.choice.ssb = i; + tcid[i]->qcl_Type1.qcl_Type=NR_QCL_Info__qcl_Type_typeC; + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList->list,tcid[i]); + n_ssb++; + } + } + + bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = + calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = + calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; + + bwp->bwp_Dedicated->sps_Config = NULL; + bwp->bwp_Dedicated->radioLinkMonitoringConfig = NULL; +} + +void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, + int bwp_loop, + NR_ServingCellConfig_t *servingcellconfigdedicated, + NR_ServingCellConfigCommon_t *scc, + rrc_gNB_carrier_data_t *carrier, + int uid) { + + if (servingcellconfigdedicated) { + ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; + } else { + if (servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList == NULL) { + servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList)); + } + ubwp = calloc(1,sizeof(*ubwp)); + ubwp->bwp_Id = bwp_loop + 1; + + ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); + + // copy bwp_Common from Initial UL BWP except for bandwidth + memcpy((void*)&ubwp->bwp_Common->genericParameters, + (void*)&scc->uplinkConfigCommon->initialUplinkBWP->genericParameters, + sizeof(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters)); + ubwp->bwp_Common->genericParameters.subcarrierSpacing = 1; + ubwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + } + + int curr_bwp = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + + if (!servingcellconfigdedicated) { + servingcellconfigdedicated->uplinkConfig=calloc(1,sizeof(*servingcellconfigdedicated->uplinkConfig)); + } + NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); - SpCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; + servingcellconfigdedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; initialUplinkBWP->pucch_Config = calloc(1,sizeof(*initialUplinkBWP->pucch_Config)); initialUplinkBWP->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); @@ -1002,16 +1315,14 @@ void fill_initial_SpCellConfig(rnti_t rnti, ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); pucchresset0->maxPayloadSize=NULL; ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); - + pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); pucch_Config->resourceToReleaseList = NULL; // configure one single PUCCH0 opportunity for initial connection procedure // one symbol (13) NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); pucchres0->pucch_ResourceId=0; - //pucchres0->startingPRB=0; - pucchres0->startingPRB=(8+rnti) % curr_bwp; - LOG_D(NR_RRC, "pucchres0->startPRB %ld rnti %d curr_bwp %d\n", pucchres0->startingPRB, rnti, curr_bwp); + pucchres0->startingPRB=(8+uid) % curr_bwp; pucchres0->intraSlotFrequencyHopping=NULL; pucchres0->secondHopPRB=NULL; pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; @@ -1020,22 +1331,62 @@ void fill_initial_SpCellConfig(rnti_t rnti, pucchres0->format.choice.format0->nrofSymbols=1; pucchres0->format.choice.format0->startingSymbolIndex=13; ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); + + // configure Scheduling request + // 40 slot period + pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); + NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); + schedulingRequestResourceConfig->schedulingRequestResourceId = 1; + schedulingRequestResourceConfig->schedulingRequestID= 0; + schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); + schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; + // note: make sure that there is no issue here. Later choose the RNTI accordingly. + // Here we would be limited to 3 UEs on this resource (1 1/2 Frames 30 kHz SCS, 5 ms TDD periodicity => slots 7,8,9). + // This should be a temporary resource until the first RRCReconfiguration gives new pucch resources. + // Check for above configuration and exit for now if it is not the case + AssertFatal(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing==NR_SubcarrierSpacing_kHz30, + "SCS != 30kHz\n"); + AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity==NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms5, + "TDD period != 5ms : %ld\n",scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); + + schedulingRequestResourceConfig->periodicityAndOffset->choice.sl40 = 8; + schedulingRequestResourceConfig->resource = calloc(1,sizeof(*schedulingRequestResourceConfig->resource)); + *schedulingRequestResourceConfig->resource = 0; + ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); + + pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); + long *delay[8]; + for (int i=0;i<8;i++) { + delay[i] = calloc(1,sizeof(*delay[i])); + AssertFatal(carrier->minRXTXTIME >=2 && carrier->minRXTXTIME <7, "check minRXTXTIME %d\n",carrier->minRXTXTIME); + *delay[i] = (i+carrier->minRXTXTIME); + ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); + } + initialUplinkBWP->pusch_Config = calloc(1,sizeof(*initialUplinkBWP->pusch_Config)); initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - NR_PUSCH_Config_t *pusch_Config = calloc(1,sizeof(*pusch_Config)); + NR_PUSCH_Config_t *pusch_Config = NULL; + if (servingcellconfigdedicated) { + pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Dedicated->pusch_Config->choice.setup; + } else { + pusch_Config = calloc(1,sizeof(*pusch_Config)); + } initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; - pusch_Config->dataScramblingIdentityPUSCH = NULL; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA = NULL; - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); + if (!servingcellconfigdedicated) { + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); + } NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; NR_DMRS_UplinkConfig->dmrs_Type = NULL; - NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NULL; /*calloc(1,sizeof(*NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)); - *NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos0;*/ - NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; + NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = calloc(1,sizeof(*NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)); + *NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos0; + if (!servingcellconfigdedicated) { + NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; + } NR_DMRS_UplinkConfig->maxLength=NULL; NR_DMRS_UplinkConfig->transformPrecodingDisabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingDisabled)); NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0 = NULL; @@ -1046,7 +1397,7 @@ void fill_initial_SpCellConfig(rnti_t rnti, pusch_Config->pusch_PowerControl->msg3_Alpha = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->msg3_Alpha)); *pusch_Config->pusch_PowerControl->msg3_Alpha = NR_Alpha_alpha1; pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant)); - *pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = -76; + *pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = -98; pusch_Config->pusch_PowerControl->p0_AlphaSets = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->p0_AlphaSets)); NR_P0_PUSCH_AlphaSet_t *aset = calloc(1,sizeof(*aset)); aset->p0_PUSCH_AlphaSetId=0; @@ -1092,6 +1443,26 @@ void fill_initial_SpCellConfig(rnti_t rnti, pusch_Config->uci_OnPUSCH=NULL; pusch_Config->tp_pi2BPSK=NULL; + uint8_t transform_precoding = NR_PUSCH_Config__transformPrecoder_disabled; + + if (pusch_Config->transformPrecoder == NULL) { + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder != NULL) + transform_precoding = NR_PUSCH_Config__transformPrecoder_enabled; + } + else { + transform_precoding = *pusch_Config->transformPrecoder; + } + + if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled) { + /* Enable DMRS uplink config for transform precoding enabled */ + NR_DMRS_UplinkConfig->transformPrecodingEnabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingEnabled)); + NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity = NULL; + NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceGroupHopping = NULL; + NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceHopping = NULL; + NR_DMRS_UplinkConfig->transformPrecodingEnabled->ext1 = NULL; + LOG_I(NR_RRC,"Transform precoding enabled\n"); + } + initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); @@ -1148,12 +1519,87 @@ void fill_initial_SpCellConfig(rnti_t rnti, srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); + servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; + + ubwp->bwp_Common->rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon; + ubwp->bwp_Common->pusch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; + ubwp->bwp_Common->pucch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; + + if (!servingcellconfigdedicated) { + ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); + } + + ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); + ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; + pucch_Config = calloc(1,sizeof(*pucch_Config)); + ubwp->bwp_Dedicated->pucch_Config->choice.setup=pucch_Config; + pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); + pucch_Config->resourceSetToReleaseList = NULL; + pucchresset0=calloc(1,sizeof(*pucchresset0)); + NR_PUCCH_ResourceSet_t *pucchresset1=calloc(1,sizeof(*pucchresset1)); + pucchresset0->pucch_ResourceSetId = 0; + pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); + *pucchresset0id0=1; + ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); + pucchresset0->maxPayloadSize=NULL; + + ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); + + pucchresset1->pucch_ResourceSetId = 1; + NR_PUCCH_ResourceId_t *pucchresset1id0=calloc(1,sizeof(*pucchresset1id0)); + *pucchresset1id0=2; + ASN_SEQUENCE_ADD(&pucchresset1->resourceList.list,pucchresset1id0); + pucchresset1->maxPayloadSize=NULL; + ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset1); + + pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); + pucch_Config->resourceToReleaseList = NULL; + pucchres0=calloc(1,sizeof(*pucchres0)); + NR_PUCCH_Resource_t *pucchres1=calloc(1,sizeof(*pucchres1)); + NR_PUCCH_Resource_t *pucchres2=calloc(1,sizeof(*pucchres2)); + NR_PUCCH_Resource_t *pucchres3=calloc(1,sizeof(*pucchres3)); + + pucchres0->pucch_ResourceId=1; + pucchres0->startingPRB= (8 + uid) % curr_bwp; + pucchres0->intraSlotFrequencyHopping=NULL; + pucchres0->secondHopPRB=NULL; + pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; + pucchres0->format.choice.format0=calloc(1,sizeof(*pucchres0->format.choice.format0)); + pucchres0->format.choice.format0->initialCyclicShift=0; + pucchres0->format.choice.format0->nrofSymbols=1; + pucchres0->format.choice.format0->startingSymbolIndex=13; + ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); + + pucchres2->pucch_ResourceId=2; + pucchres2->startingPRB=0; + pucchres2->intraSlotFrequencyHopping=NULL; + pucchres2->secondHopPRB=NULL; + pucchres2->format.present= NR_PUCCH_Resource__format_PR_format2; + pucchres2->format.choice.format2=calloc(1,sizeof(*pucchres2->format.choice.format2)); + pucchres2->format.choice.format2->nrofPRBs=8; + pucchres2->format.choice.format2->nrofSymbols=1; + pucchres2->format.choice.format2->startingSymbolIndex=13; + ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres2); + + pucch_Config->format2=calloc(1,sizeof(*pucch_Config->format2)); + pucch_Config->format2->present=NR_SetupRelease_PUCCH_FormatConfig_PR_setup; + NR_PUCCH_FormatConfig_t *pucchfmt2 = calloc(1,sizeof(*pucchfmt2)); + pucch_Config->format2->choice.setup = pucchfmt2; + pucchfmt2->interslotFrequencyHopping=NULL; + pucchfmt2->additionalDMRS=NULL; + pucchfmt2->maxCodeRate=calloc(1,sizeof(*pucchfmt2->maxCodeRate)); + *pucchfmt2->maxCodeRate=NR_PUCCH_MaxCodeRate_zeroDot35; + pucchfmt2->nrofSlots=NULL; + pucchfmt2->pi2BPSK=NULL; + pucchfmt2->simultaneousHARQ_ACK_CSI=calloc(1,sizeof(*pucchfmt2->simultaneousHARQ_ACK_CSI)); + *pucchfmt2->simultaneousHARQ_ACK_CSI=NR_PUCCH_FormatConfig__simultaneousHARQ_ACK_CSI_true; + // configure Scheduling request - // 40 slot period + // 40 slot period pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); - NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); + schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); schedulingRequestResourceConfig->schedulingRequestResourceId = 1; - schedulingRequestResourceConfig->schedulingRequestID= 0; + schedulingRequestResourceConfig->schedulingRequestID = 0; schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; // note: make sure that there is no issue here. Later choose the RNTI accordingly. @@ -1170,15 +1616,84 @@ void fill_initial_SpCellConfig(rnti_t rnti, *schedulingRequestResourceConfig->resource = 0; ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); - pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); - long *delay[8]; - for (int i=0;i<8;i++) { - delay[i] = calloc(1,sizeof(*delay[i])); - AssertFatal(carrier->minRXTXTIME >=2 && carrier->minRXTXTIME <7, - "check minRXTXTIME %d\n",carrier->minRXTXTIME); - *delay[i] = (i+carrier->minRXTXTIME); - ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); - } + pucch_Config->schedulingRequestResourceToReleaseList=NULL; + pucch_Config->multi_CSI_PUCCH_ResourceList=NULL; + pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); + for (int i=0;i<8;i++) { + delay[i] = calloc(1,sizeof(*delay[i])); + *delay[i] = i+carrier->minRXTXTIME; + ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); + } + pucch_Config->spatialRelationInfoToAddModList = calloc(1,sizeof(*pucch_Config->spatialRelationInfoToAddModList)); + NR_PUCCH_SpatialRelationInfo_t *pucchspatial = calloc(1,sizeof(*pucchspatial)); + pucchspatial->pucch_SpatialRelationInfoId = 1; + pucchspatial->servingCellId = NULL; + if(carrier->do_CSIRS) { + pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; + pucchspatial->referenceSignal.choice.csi_RS_Index = 0; + } + else { + pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_ssb_Index; + pucchspatial->referenceSignal.choice.ssb_Index = 0; + } + pucchspatial->pucch_PathlossReferenceRS_Id = 0; + pucchspatial->p0_PUCCH_Id = 1; + pucchspatial->closedLoopIndex = NR_PUCCH_SpatialRelationInfo__closedLoopIndex_i0; + ASN_SEQUENCE_ADD(&pucch_Config->spatialRelationInfoToAddModList->list,pucchspatial); + pucch_Config->spatialRelationInfoToReleaseList=NULL; + pucch_Config->pucch_PowerControl=calloc(1,sizeof(*pucch_Config->pucch_PowerControl)); + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = 0; + pucch_Config->pucch_PowerControl->p0_Set = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->p0_Set)); + NR_P0_PUCCH_t *p00 = calloc(1,sizeof(*p00)); + p00->p0_PUCCH_Id=1; + p00->p0_PUCCH_Value = 0; + ASN_SEQUENCE_ADD(&pucch_Config->pucch_PowerControl->p0_Set->list,p00); + pucch_Config->pucch_PowerControl->pathlossReferenceRSs = NULL; + + if (!servingcellconfigdedicated) { + // copy pusch_Config from dedicated initialBWP + ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); + ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; + ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; + } + ubwp->bwp_Dedicated->configuredGrantConfig = NULL; + ubwp->bwp_Dedicated->srs_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->srs_Config)); + ubwp->bwp_Dedicated->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; + ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; + + ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; +} + +void fill_initial_SpCellConfig(rnti_t rnti, + int uid, + NR_SpCellConfig_t *SpCellConfig, + NR_ServingCellConfigCommon_t *scc, + NR_ServingCellConfig_t *servingcellconfigdedicated, + rrc_gNB_carrier_data_t *carrier) { + + SpCellConfig->servCellIndex = NULL; + SpCellConfig->reconfigurationWithSync = NULL; + SpCellConfig->rlmInSyncOutOfSyncThreshold = NULL; + SpCellConfig->rlf_TimersAndConstants = NULL; + + if (servingcellconfigdedicated) { + SpCellConfig->spCellConfigDedicated = servingcellconfigdedicated; + } else { + SpCellConfig->spCellConfigDedicated = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated)); + } + + if (!SpCellConfig->spCellConfigDedicated->uplinkConfig) { + SpCellConfig->spCellConfigDedicated->uplinkConfig = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig)); + } SpCellConfig->spCellConfigDedicated->initialDownlinkBWP = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->initialDownlinkBWP)); NR_BWP_DownlinkDedicated_t *bwp_Dedicated = SpCellConfig->spCellConfigDedicated->initialDownlinkBWP; @@ -1196,18 +1711,18 @@ void fill_initial_SpCellConfig(rnti_t rnti, // options are 24, 48 or 96 coreset->frequencyDomainResources.buf = calloc(1,6); if (0) { - int curr_bwp = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; - if (curr_bwp < 48) - coreset->frequencyDomainResources.buf[0] = 0xf0; - else - coreset->frequencyDomainResources.buf[0] = 0xff; - if (curr_bwp < 96) - coreset->frequencyDomainResources.buf[1] = 0; - else - coreset->frequencyDomainResources.buf[1] = 0xff; + int curr_bwp = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + if (curr_bwp < 48) + coreset->frequencyDomainResources.buf[0] = 0xf0; + else + coreset->frequencyDomainResources.buf[0] = 0xff; + if (curr_bwp < 96) + coreset->frequencyDomainResources.buf[1] = 0; + else + coreset->frequencyDomainResources.buf[1] = 0xff; } else { - coreset->frequencyDomainResources.buf[0] = 0xf0; - coreset->frequencyDomainResources.buf[1] = 0; + coreset->frequencyDomainResources.buf[0] = 0xf0; + coreset->frequencyDomainResources.buf[1] = 0; } coreset->frequencyDomainResources.buf[2] = 0; coreset->frequencyDomainResources.buf[3] = 0; @@ -1304,6 +1819,51 @@ void fill_initial_SpCellConfig(rnti_t rnti, pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1,sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers)); *pdsch_servingcellconfig->ext1->maxMIMO_Layers = 2; + if(SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList == NULL){ + SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); + } + if(SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList == NULL){ + SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); + } + + int n_dl_bwp = 1; + if (servingcellconfigdedicated) { + n_dl_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; + } + + for (int bwp_loop = 0; bwp_loop < n_dl_bwp; bwp_loop++) { + NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); + fill_default_downlinkBWP(bwp, bwp_loop, servingcellconfigdedicated, scc, carrier); + if (!servingcellconfigdedicated) { + ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); + } + } + + if (SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id==NULL) { + SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + } + + int n_ul_bwp = 1; + if (servingcellconfigdedicated) { + n_ul_bwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; + } + + for (int bwp_loop = 0; bwp_loop < n_ul_bwp; bwp_loop++) { + NR_BWP_Uplink_t *ubwp = calloc(1, sizeof(*ubwp)); + fill_default_uplinkBWP(ubwp, bwp_loop, servingcellconfigdedicated, scc, carrier, uid); + if (!servingcellconfigdedicated) { + ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list, ubwp); + } + } + + if (SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id==NULL) { + SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=1; + } + + xer_fprint(stdout, &asn_DEF_NR_SpCellConfig, (void *)SpCellConfig); + } void fill_mastercellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, NR_CellGroupConfig_t *ue_context_mastercellGroup) { @@ -1397,8 +1957,10 @@ void fill_mastercellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, NR_CellGr } void fill_initial_cellGroupConfig(rnti_t rnti, + int uid, NR_CellGroupConfig_t *cellGroupConfig, NR_ServingCellConfigCommon_t *scc, + NR_ServingCellConfig_t *servingcellconfigdedicated, rrc_gNB_carrier_data_t *carrier) { NR_RLC_BearerConfig_t *rlc_BearerConfig = NULL; @@ -1487,10 +2049,10 @@ void fill_initial_cellGroupConfig(rnti_t rnti, *physicalCellGroupConfig->p_NR_FR1 = 10; physicalCellGroupConfig->pdsch_HARQ_ACK_Codebook = NR_PhysicalCellGroupConfig__pdsch_HARQ_ACK_Codebook_dynamic; cellGroupConfig->physicalCellGroupConfig = physicalCellGroupConfig; - + cellGroupConfig->spCellConfig = calloc(1,sizeof(*cellGroupConfig->spCellConfig)); - fill_initial_SpCellConfig(rnti,cellGroupConfig->spCellConfig,scc,carrier); + fill_initial_SpCellConfig(rnti,uid,cellGroupConfig->spCellConfig,scc,servingcellconfigdedicated,carrier); cellGroupConfig->sCellToAddModList = NULL; cellGroupConfig->sCellToReleaseList = NULL; @@ -1502,7 +2064,8 @@ uint8_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, const uint8_t transaction_id, OCTET_STRING_t *masterCellGroup_from_DU, NR_ServingCellConfigCommon_t *scc, - rrc_gNB_carrier_data_t *carrier) + NR_ServingCellConfig_t *servingcellconfigdedicated, + rrc_gNB_carrier_data_t *carrier) //------------------------------------------------------------------------------ { asn_enc_rval_t enc_rval; @@ -1569,7 +2132,7 @@ uint8_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, } else { cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t)); - fill_initial_cellGroupConfig(ue_context_pP->ue_context.rnti,cellGroupConfig,scc,carrier); + fill_initial_cellGroupConfig(ue_context_pP->ue_context.rnti,ue_context_pP->local_uid,cellGroupConfig,scc,servingcellconfigdedicated,carrier); enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 6803cf21d7..49e1023994 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -101,14 +101,18 @@ uint8_t do_RRCReject(uint8_t Mod_id, uint8_t *const buffer); void fill_initial_SpCellConfig(rnti_t rnti, - NR_SpCellConfig_t *SpCellConfig, - NR_ServingCellConfigCommon_t *scc, + int uid, + NR_SpCellConfig_t *SpCellConfig, + NR_ServingCellConfigCommon_t *scc, + NR_ServingCellConfig_t *servingcellconfigdedicated, rrc_gNB_carrier_data_t *carrier); void fill_initial_cellGroupConfig(rnti_t rnti, - NR_CellGroupConfig_t *cellGroupConfig, - NR_ServingCellConfigCommon_t *scc, - rrc_gNB_carrier_data_t *carrier); + int uid, + NR_CellGroupConfig_t *cellGroupConfig, + NR_ServingCellConfigCommon_t *scc, + NR_ServingCellConfig_t *servingcellconfigdedicated, + rrc_gNB_carrier_data_t *carrier) ; void fill_mastercellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, NR_CellGroupConfig_t *ue_context_mastercellGroup); @@ -117,7 +121,8 @@ uint8_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, const uint8_t transaction_id, OCTET_STRING_t *masterCellGroup_from_DU, NR_ServingCellConfigCommon_t *scc, - rrc_gNB_carrier_data_t *carrier); + NR_ServingCellConfig_t *servingcellconfigdedicated, + rrc_gNB_carrier_data_t *carrier); uint8_t do_NR_SecurityModeCommand( const protocol_ctxt_t *const ctxt_pP, diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 392a4036fa..46c070ed5b 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -398,11 +398,12 @@ rrc_gNB_generate_RRCSetup( // T_INT(ctxt_pP->rnti)); gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; + NR_ServingCellConfig_t *servingcellconfigdedicated = rrc->configuration.scd; ue_p->Srb0.Tx_buffer.payload_size = do_RRCSetup(ue_context_pP, (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), masterCellGroup_from_DU, - scc,&rrc->carrier); + scc,servingcellconfigdedicated,&rrc->carrier); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), @@ -513,6 +514,7 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( rrc_gNB_ue_context_t *ue_context_pP = NULL; gNB_RRC_INST *rrc_instance_p = RC.nrrrc[ctxt_pP->module_id]; NR_ServingCellConfigCommon_t *scc=rrc_instance_p->carrier.servingcellconfigcommon; + NR_ServingCellConfig_t *servingcellconfigdedicated = rrc_instance_p->configuration.scd; ue_context_pP = rrc_gNB_get_next_free_ue_context(ctxt_pP, rrc_instance_p, 0); @@ -521,7 +523,7 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), NULL, - scc,&rrc_instance_p->carrier); + scc,servingcellconfigdedicated,&rrc_instance_p->carrier); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), From b15f1f1830b14d5d10affd9560386b3c30f16e0b Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 12 Jan 2022 17:51:32 +0000 Subject: [PATCH 062/233] Fix number of RRCSetup encoded bytes --- openair2/RRC/LTE/rrc_defs.h | 2 +- openair2/RRC/NR/MESSAGES/asn1_msg.c | 2 +- openair2/RRC/NR/MESSAGES/asn1_msg.h | 2 +- openair2/RRC/NR/rrc_gNB.c | 28 ++++++++++++++++++---------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/openair2/RRC/LTE/rrc_defs.h b/openair2/RRC/LTE/rrc_defs.h index f537dcbbd2..3d472fa931 100644 --- a/openair2/RRC/LTE/rrc_defs.h +++ b/openair2/RRC/LTE/rrc_defs.h @@ -484,7 +484,7 @@ typedef struct MEASUREMENT_INFO_s { typedef struct { char Payload[RRC_BUFFER_SIZE_MAX]; char Header[RRC_HEADER_SIZE_MAX]; - char payload_size; + uint16_t payload_size; } RRC_BUFFER; #define RRC_BUFFER_SIZE sizeof(RRC_BUFFER) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 767f6a912e..8800b8f907 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -2059,7 +2059,7 @@ void fill_initial_cellGroupConfig(rnti_t rnti, } //------------------------------------------------------------------------------ -uint8_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, +int16_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, uint8_t *const buffer, const uint8_t transaction_id, OCTET_STRING_t *masterCellGroup_from_DU, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 49e1023994..1745433b7b 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -116,7 +116,7 @@ void fill_initial_cellGroupConfig(rnti_t rnti, void fill_mastercellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, NR_CellGroupConfig_t *ue_context_mastercellGroup); -uint8_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, +int16_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, uint8_t *const buffer, const uint8_t transaction_id, OCTET_STRING_t *masterCellGroup_from_DU, diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 46c070ed5b..2f5e4ec36a 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -399,11 +399,15 @@ rrc_gNB_generate_RRCSetup( gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; NR_ServingCellConfig_t *servingcellconfigdedicated = rrc->configuration.scd; - ue_p->Srb0.Tx_buffer.payload_size = do_RRCSetup(ue_context_pP, - (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, - rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), - masterCellGroup_from_DU, - scc,servingcellconfigdedicated,&rrc->carrier); + int16_t ret = do_RRCSetup(ue_context_pP, + (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, + rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), + masterCellGroup_from_DU, + scc,servingcellconfigdedicated,&rrc->carrier); + + AssertFatal(ret>0,"Error generating RRCSetup for RRCSetupRequest\n"); + + ue_p->Srb0.Tx_buffer.payload_size = ret; LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), @@ -519,11 +523,15 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( ue_context_pP = rrc_gNB_get_next_free_ue_context(ctxt_pP, rrc_instance_p, 0); gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; - ue_p->Srb0.Tx_buffer.payload_size = do_RRCSetup(ue_context_pP, - (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, - rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), - NULL, - scc,servingcellconfigdedicated,&rrc_instance_p->carrier); + int16_t ret = do_RRCSetup(ue_context_pP, + (uint8_t *) ue_p->Srb0.Tx_buffer.Payload, + rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id), + NULL, + scc,servingcellconfigdedicated,&rrc_instance_p->carrier); + + AssertFatal(ret>0,"Error generating RRCSetup for RRCReestablishmentRequest\n"); + + ue_p->Srb0.Tx_buffer.payload_size = ret; LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), From c1f6dbfb2fb2f3d388472a93e344ea364e0be704 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 13 Jan 2022 11:16:11 +0000 Subject: [PATCH 063/233] Fix Msg4 for mac subheader long, RB calculation improvement, and fix variable types to support bigger payload lengths --- openair2/COMMON/mac_messages_types.h | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 48 ++++++++++++++----- openair2/RRC/NR/L2_nr_interface.c | 18 +++---- openair2/RRC/NR/nr_rrc_defs.h | 2 +- openair2/RRC/NR/nr_rrc_proto.h | 14 +++--- 5 files changed, 53 insertions(+), 31 deletions(-) diff --git a/openair2/COMMON/mac_messages_types.h b/openair2/COMMON/mac_messages_types.h index cad7b9f172..e43d0007e7 100644 --- a/openair2/COMMON/mac_messages_types.h +++ b/openair2/COMMON/mac_messages_types.h @@ -58,7 +58,7 @@ // Some constants from "LAYER2/MAC/defs.h" #define BCCH_SDU_SIZE (512) #define BCCH_SDU_MBMS_SIZE (512) -#define CCCH_SDU_SIZE (512) +#define CCCH_SDU_SIZE (1024) #define MCCH_SDU_SIZE (512) #define PCCH_SDU_SIZE (512) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 917d0cd0dd..842c582677 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1518,13 +1518,26 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra } else { uint16_t mac_pdu_length = nr_write_ce_dlsch_pdu(module_idP, nr_mac->sched_ctrlCommon, buf, 255, ra->cont_res_id); LOG_D(NR_MAC,"Encoded contention resolution mac_pdu_length %d\n",mac_pdu_length); - uint16_t mac_sdu_length = mac_rrc_nr_data_req(module_idP, CC_id, frameP, CCCH, ra->rnti, 1, &buf[mac_pdu_length+2]); - ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->R = 0; - ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->F = 0; - ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->LCID = DL_SCH_LCID_CCCH; - ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->L = mac_sdu_length; - ra->mac_pdu_length = mac_pdu_length + mac_sdu_length + sizeof(NR_MAC_SUBHEADER_SHORT); - LOG_D(NR_MAC,"Encoded RRCSetup Piggyback (%d + %d bytes), mac_pdu_length %d\n", mac_sdu_length, (int)sizeof(NR_MAC_SUBHEADER_SHORT), ra->mac_pdu_length); + uint8_t buffer[CCCH_SDU_SIZE]; + uint8_t mac_subheader_len = sizeof(NR_MAC_SUBHEADER_SHORT); + uint16_t mac_sdu_length = mac_rrc_nr_data_req(module_idP, CC_id, frameP, CCCH, ra->rnti, 1, buffer); + if (mac_sdu_length < 256) { + ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->R = 0; + ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->F = 0; + ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->LCID = DL_SCH_LCID_CCCH; + ((NR_MAC_SUBHEADER_SHORT *) &buf[mac_pdu_length])->L = mac_sdu_length; + ra->mac_pdu_length = mac_pdu_length + mac_sdu_length + sizeof(NR_MAC_SUBHEADER_SHORT); + } else { + mac_subheader_len = sizeof(NR_MAC_SUBHEADER_LONG); + ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->R = 0; + ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->F = 1; + ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->LCID = DL_SCH_LCID_CCCH; + ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->L1 = (mac_sdu_length >> 8) & 0xff; + ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->L2 = mac_sdu_length & 0xff; + ra->mac_pdu_length = mac_pdu_length + mac_sdu_length + sizeof(NR_MAC_SUBHEADER_LONG); + } + LOG_I(NR_MAC,"Encoded RRCSetup Piggyback (%d + %d bytes), mac_pdu_length %d\n", mac_sdu_length, mac_subheader_len, ra->mac_pdu_length); + memcpy(&buf[mac_pdu_length + mac_subheader_len], buffer, mac_sdu_length); } } @@ -1581,13 +1594,22 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra int rbSize = 0; uint8_t tb_scaling = 0; uint16_t *vrb_map = cc[CC_id].vrb_map; + do { - rbSize++; - LOG_D(NR_MAC,"Calling nr_compute_tbs with N_PRB_DMRS %d, N_DMRS_SLOT %d\n",N_PRB_DMRS,N_DMRS_SLOT); - harq->tb_size = nr_compute_tbs(nr_get_Qm_dl(mcsIndex, mcsTableIdx), - nr_get_code_rate_dl(mcsIndex, mcsTableIdx), - rbSize, nrOfSymbols, N_PRB_DMRS * N_DMRS_SLOT, 0, tb_scaling,1) >> 3; - } while (rbSize < BWPSize && harq->tb_size < ra->mac_pdu_length); + do { + rbSize++; + LOG_D(NR_MAC,"Calling nr_compute_tbs with N_PRB_DMRS %d, N_DMRS_SLOT %d\n",N_PRB_DMRS,N_DMRS_SLOT); + harq->tb_size = nr_compute_tbs(nr_get_Qm_dl(mcsIndex, mcsTableIdx), + nr_get_code_rate_dl(mcsIndex, mcsTableIdx), + rbSize, nrOfSymbols, N_PRB_DMRS * N_DMRS_SLOT, 0, tb_scaling,1) >> 3; + } while (rbSize < BWPSize && harq->tb_size < ra->mac_pdu_length); + if(harq->tb_size < ra->mac_pdu_length) { + rbSize = 0; + mcsIndex++; + } + } while(rbSize < BWPSize && harq->tb_size < ra->mac_pdu_length && mcsIndex<=28); + + AssertFatal(harq->tb_size >= ra->mac_pdu_length,"Cannot allocate Msg4\n"); int i = 0; while ((i < rbSize) && (rbStart + rbSize <= BWPSize)) { diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index c81dd70928..04bf01ce1d 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -218,13 +218,13 @@ nr_rrc_data_req( return TRUE; // TODO should be changed to a CNF message later, currently RRC lite does not used the returned value anyway. } -int8_t mac_rrc_nr_data_req(const module_id_t Mod_idP, - const int CC_id, - const frame_t frameP, - const rb_id_t Srb_id, - const rnti_t rnti, - const uint8_t Nb_tb, - uint8_t *const buffer_pP ){ +uint16_t mac_rrc_nr_data_req(const module_id_t Mod_idP, + const int CC_id, + const frame_t frameP, + const rb_id_t Srb_id, + const rnti_t rnti, + const uint8_t Nb_tb, + uint8_t *const buffer_pP ){ #ifdef DEBUG_RRC LOG_D(RRC,"[eNB %d] mac_rrc_data_req to SRB ID=%ld\n",Mod_idP,Srb_id); @@ -280,13 +280,13 @@ int8_t mac_rrc_nr_data_req(const module_id_t Mod_idP, if( (Srb_id & RAB_OFFSET ) == CCCH) { char *payload_pP; - uint8_t Sdu_size = 0; + uint16_t Sdu_size = 0; struct rrc_gNB_ue_context_s *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_idP], rnti); LOG_D(NR_RRC,"[gNB %d] Frame %d CCCH request (Srb_id %ld)\n", Mod_idP, frameP, Srb_id); AssertFatal(ue_context_p!=NULL,"failed to get ue_context, rnti %x\n",rnti); - char payload_size = ue_context_p->ue_context.Srb0.Tx_buffer.payload_size; + uint16_t payload_size = ue_context_p->ue_context.Srb0.Tx_buffer.payload_size; // check if data is there for MAC if (payload_size > 0) { diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 67e56f31a6..f30b507820 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -213,7 +213,7 @@ typedef struct HANDOVER_INFO_NR_s { typedef struct { char Payload[NR_RRC_BUFFER_SIZE_MAX]; char Header[NR_RRC_HEADER_SIZE_MAX]; - char payload_size; + uint16_t payload_size; } NR_RRC_BUFFER; #define NR_RRC_BUFFER_SIZE sizeof(RRC_BUFFER_NR) diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 22109c3029..73fb58c5dc 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -47,13 +47,13 @@ void rrc_config_nr_buffer(NR_SRB_INFO* Srb_info, uint8_t Lchan_type, uint8_t Role); -int8_t mac_rrc_nr_data_req(const module_id_t Mod_idP, - const int CC_id, - const frame_t frameP, - const rb_id_t Srb_id, - const rnti_t rnti, - const uint8_t Nb_tb, - uint8_t *const buffer_pP ); +uint16_t mac_rrc_nr_data_req(const module_id_t Mod_idP, + const int CC_id, + const frame_t frameP, + const rb_id_t Srb_id, + const rnti_t rnti, + const uint8_t Nb_tb, + uint8_t *const buffer_pP); void rrc_gNB_process_SgNBAdditionRequest( const protocol_ctxt_t *const ctxt_pP, From 46181eca7d15201ff11cebd55da69fb5c45d524d Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 13 Jan 2022 17:22:37 +0000 Subject: [PATCH 064/233] Dedicated BWPs working for SA in RFsim --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 8 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 21 ++ openair2/RRC/NR/MESSAGES/asn1_msg.c | 189 +++++++----------- 3 files changed, 92 insertions(+), 126 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index e909be2a55..2c4fad5820 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -2591,12 +2591,12 @@ uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id ; int N_RB_UL = 0; - if(mac->scc_SIB) { - N_RB_UL = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - } else if(mac->ULbwp[ul_bwp_id-1]) { - N_RB_UL = NRRIV2BW(mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if(ul_bwp_id > 0 && mac->ULbwp[ul_bwp_id-1]) { + N_RB_UL = NRRIV2BW(mac->ULbwp[ul_bwp_id - 1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } else if(mac->scc) { N_RB_UL = NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } else if(mac->scc_SIB) { + N_RB_UL = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } LOG_D(MAC,"nr_extract_dci_info : dci_pdu %lx, size %d\n",*dci_pdu,dci_size); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 842c582677..35294ce775 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1846,6 +1846,27 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram LOG_A(NR_MAC, "(ue %i, rnti 0x%04x) Received Ack of RA-Msg4. CBRA procedure succeeded!\n", UE_id, ra->rnti); UE_info->active[UE_id] = true; UE_info->Msg4_ACKed[UE_id] = true; + + // TODO: To be improved the BWP selection + // For now, we are just changing to the first active BWP + + const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; + + const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; + const int bwp_id = servingCellConfig ? *servingCellConfig->firstActiveDownlinkBWP_Id : 0; + sched_ctrl->active_bwp = bwpList && bwp_id > 0 ? bwpList->list.array[bwp_id - 1] : NULL; + + const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; + const int ubwp_id = servingCellConfig ? *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; + sched_ctrl->active_ubwp = ubwpList && ubwp_id > 0 ? ubwpList->list.array[ubwp_id - 1] : NULL; + + if(sched_ctrl->active_bwp) { + LOG_I(NR_MAC, "Changing to DL-BWP %i\n", bwp_id); + } + + if(sched_ctrl->active_ubwp) { + LOG_I(NR_MAC, "Changing to UL-BWP %i\n", ubwp_id); + } } else { LOG_I(NR_MAC, "(ue %i, rnti 0x%04x) RA Procedure failed at Msg4!\n", UE_id, ra->rnti); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 8800b8f907..eda7f77a47 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1130,8 +1130,42 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); + bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); + coreset = calloc(1,sizeof(*coreset)); + coreset->controlResourceSetId=1; + // frequency domain resources depends on BWP size + // options are 24, 48 or 96 + coreset->frequencyDomainResources.buf = calloc(1,6); + if (0) { + int curr_bwp = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + if (curr_bwp < 48) + coreset->frequencyDomainResources.buf[0] = 0xf0; + else + coreset->frequencyDomainResources.buf[0] = 0xff; + if (curr_bwp < 96) + coreset->frequencyDomainResources.buf[1] = 0; + else + coreset->frequencyDomainResources.buf[1] = 0xff; + } else { + coreset->frequencyDomainResources.buf[0] = 0xf0; + coreset->frequencyDomainResources.buf[1] = 0; + } + coreset->frequencyDomainResources.buf[2] = 0; + coreset->frequencyDomainResources.buf[3] = 0; + coreset->frequencyDomainResources.buf[4] = 0; + coreset->frequencyDomainResources.buf[5] = 0; + coreset->frequencyDomainResources.size = 6; + coreset->frequencyDomainResources.bits_unused = 3; + coreset->duration=1; + coreset->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; + coreset->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; + coreset->tci_StatesPDCCH_ToAddList=NULL; + coreset->tci_StatesPDCCH_ToReleaseList = NULL; + coreset->tci_PresentInDCI = NULL; + coreset->pdcch_DMRS_ScramblingID = NULL; + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset); bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); @@ -1148,18 +1182,13 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ss2->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss2->monitoringSymbolsWithinSlot)); ss2->monitoringSymbolsWithinSlot->buf = calloc(1,2); ss2->monitoringSymbolsWithinSlot->size = 2; - ss2->monitoringSymbolsWithinSlot->bits_unused = 2; - ss2->monitoringSymbolsWithinSlot->buf[0]=0xc0; + ss2->monitoringSymbolsWithinSlot->buf[0]=0x80; ss2->monitoringSymbolsWithinSlot->buf[1]=0x0; + ss2->monitoringSymbolsWithinSlot->bits_unused = 2; ss2->nrofCandidates=calloc(1,sizeof(*ss2->nrofCandidates)); ss2->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; - ss2->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; - if (curr_bwp < 48) - ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; - else if (curr_bwp < 96) - ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n2; - else - ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n4; + ss2->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n2; + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; ss2->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; ss2->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; ss2->searchSpaceType=calloc(1,sizeof(*ss2->searchSpaceType)); @@ -1169,103 +1198,38 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss2); - bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToReleaseList = NULL; - if (!servingcellconfigdedicated) { - bwp->bwp_Dedicated->pdsch_Config = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); + bwp->bwp_Dedicated->pdsch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); } - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; - if (carrier->pdsch_AntennaPorts > 1) // for MIMO, we use DMRS Config Type 2 - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type)); - else - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID0=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID1=NULL; - if (!servingcellconfigdedicated) { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS=NULL; - } bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0; + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation = NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = + calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList)); - int n_ssb = 0; - NR_TCI_State_t *tcid[64]; - for (int i=0;i<64;i++) { - if ((bitmap>>(63-i))&0x01){ - tcid[i]=calloc(1,sizeof(*tcid[i])); - tcid[i]->tci_StateId=n_ssb; - tcid[i]->qcl_Type1.cell=NULL; - tcid[i]->qcl_Type1.bwp_Id=calloc(1,sizeof(*tcid[i]->qcl_Type1.bwp_Id)); - *tcid[i]->qcl_Type1.bwp_Id=1; - tcid[i]->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; - tcid[i]->qcl_Type1.referenceSignal.choice.ssb = i; - tcid[i]->qcl_Type1.qcl_Type=NR_QCL_Info__qcl_Type_typeC; - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList->list,tcid[i]); - n_ssb++; - } - } + NR_TCI_State_t *tcic = calloc(1,sizeof(*tcic)); + tcic->tci_StateId=0; + tcic->qcl_Type1.cell=NULL; + tcic->qcl_Type1.bwp_Id=NULL; + tcic->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; + tcic->qcl_Type1.referenceSignal.choice.ssb = 0; + tcic->qcl_Type1.qcl_Type=NR_QCL_Info__qcl_Type_typeD; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = - calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = - calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; - - bwp->bwp_Dedicated->sps_Config = NULL; - bwp->bwp_Dedicated->radioLinkMonitoringConfig = NULL; + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList->list,tcic); } void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, @@ -1359,7 +1323,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, for (int i=0;i<8;i++) { delay[i] = calloc(1,sizeof(*delay[i])); AssertFatal(carrier->minRXTXTIME >=2 && carrier->minRXTXTIME <7, "check minRXTXTIME %d\n",carrier->minRXTXTIME); - *delay[i] = (i+carrier->minRXTXTIME); + *delay[i] = i+carrier->minRXTXTIME; ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); } @@ -1536,31 +1500,20 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); pucch_Config->resourceSetToReleaseList = NULL; pucchresset0=calloc(1,sizeof(*pucchresset0)); - NR_PUCCH_ResourceSet_t *pucchresset1=calloc(1,sizeof(*pucchresset1)); pucchresset0->pucch_ResourceSetId = 0; pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); - *pucchresset0id0=1; + *pucchresset0id0=0; ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); pucchresset0->maxPayloadSize=NULL; - ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); - pucchresset1->pucch_ResourceSetId = 1; - NR_PUCCH_ResourceId_t *pucchresset1id0=calloc(1,sizeof(*pucchresset1id0)); - *pucchresset1id0=2; - ASN_SEQUENCE_ADD(&pucchresset1->resourceList.list,pucchresset1id0); - pucchresset1->maxPayloadSize=NULL; - ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset1); - pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); pucch_Config->resourceToReleaseList = NULL; + // configure one single PUCCH0 opportunity for initial connection procedure + // one symbol (13) pucchres0=calloc(1,sizeof(*pucchres0)); - NR_PUCCH_Resource_t *pucchres1=calloc(1,sizeof(*pucchres1)); - NR_PUCCH_Resource_t *pucchres2=calloc(1,sizeof(*pucchres2)); - NR_PUCCH_Resource_t *pucchres3=calloc(1,sizeof(*pucchres3)); - - pucchres0->pucch_ResourceId=1; - pucchres0->startingPRB= (8 + uid) % curr_bwp; + pucchres0->pucch_ResourceId=0; + pucchres0->startingPRB=(8+uid) % curr_bwp; pucchres0->intraSlotFrequencyHopping=NULL; pucchres0->secondHopPRB=NULL; pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; @@ -1570,17 +1523,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pucchres0->format.choice.format0->startingSymbolIndex=13; ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); - pucchres2->pucch_ResourceId=2; - pucchres2->startingPRB=0; - pucchres2->intraSlotFrequencyHopping=NULL; - pucchres2->secondHopPRB=NULL; - pucchres2->format.present= NR_PUCCH_Resource__format_PR_format2; - pucchres2->format.choice.format2=calloc(1,sizeof(*pucchres2->format.choice.format2)); - pucchres2->format.choice.format2->nrofPRBs=8; - pucchres2->format.choice.format2->nrofSymbols=1; - pucchres2->format.choice.format2->startingSymbolIndex=13; - ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres2); - +/* pucch_Config->format2=calloc(1,sizeof(*pucch_Config->format2)); pucch_Config->format2->present=NR_SetupRelease_PUCCH_FormatConfig_PR_setup; NR_PUCCH_FormatConfig_t *pucchfmt2 = calloc(1,sizeof(*pucchfmt2)); @@ -1593,6 +1536,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pucchfmt2->pi2BPSK=NULL; pucchfmt2->simultaneousHARQ_ACK_CSI=calloc(1,sizeof(*pucchfmt2->simultaneousHARQ_ACK_CSI)); *pucchfmt2->simultaneousHARQ_ACK_CSI=NR_PUCCH_FormatConfig__simultaneousHARQ_ACK_CSI_true; +*/ // configure Scheduling request // 40 slot period @@ -1621,6 +1565,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); for (int i=0;i<8;i++) { delay[i] = calloc(1,sizeof(*delay[i])); + AssertFatal(carrier->minRXTXTIME >=2 && carrier->minRXTXTIME <7, "check minRXTXTIME %d\n",carrier->minRXTXTIME); *delay[i] = i+carrier->minRXTXTIME; ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); } @@ -1756,9 +1701,9 @@ void fill_initial_SpCellConfig(rnti_t rnti, ss2->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss2->monitoringSymbolsWithinSlot)); ss2->monitoringSymbolsWithinSlot->buf = calloc(1,2); ss2->monitoringSymbolsWithinSlot->size = 2; - ss2->monitoringSymbolsWithinSlot->bits_unused = 2; ss2->monitoringSymbolsWithinSlot->buf[0]=0x80; ss2->monitoringSymbolsWithinSlot->buf[1]=0x0; + ss2->monitoringSymbolsWithinSlot->bits_unused = 2; ss2->nrofCandidates=calloc(1,sizeof(*ss2->nrofCandidates)); ss2->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; ss2->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n2; @@ -2162,7 +2107,7 @@ int16_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, (void *)&dl_ccch_msg, buffer, 1000); - + if(enc_rval.encoded == -1) { LOG_E(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); From a8e7927cde98af5f9421d136a3c0e29c70fb36db Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 13 Jan 2022 17:40:17 +0000 Subject: [PATCH 065/233] Fix scope build warnings --- openair1/PHY/TOOLS/lte_enb_scope.c | 4 ++-- openair1/PHY/TOOLS/lte_ue_scope.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openair1/PHY/TOOLS/lte_enb_scope.c b/openair1/PHY/TOOLS/lte_enb_scope.c index bed507feee..8d333d9730 100644 --- a/openair1/PHY/TOOLS/lte_enb_scope.c +++ b/openair1/PHY/TOOLS/lte_enb_scope.c @@ -50,8 +50,8 @@ void reset_stats(FL_OBJECT *button, long arg) { PHY_VARS_eNB *phy_vars_eNB = RC.eNB[0][0]; for (i=0; idlsch[i][0]->Mlimit; j++) { + for (k=0; kdlsch[k][0]->Mlimit; j++) { phy_vars_eNB->UE_stats[i].dlsch_NAK[k][j]=0; phy_vars_eNB->UE_stats[i].dlsch_ACK[k][j]=0; phy_vars_eNB->UE_stats[i].dlsch_trials[k][j]=0; diff --git a/openair1/PHY/TOOLS/lte_ue_scope.c b/openair1/PHY/TOOLS/lte_ue_scope.c index 00560f2d74..2c45437cdf 100644 --- a/openair1/PHY/TOOLS/lte_ue_scope.c +++ b/openair1/PHY/TOOLS/lte_ue_scope.c @@ -52,8 +52,8 @@ void reset_stats(FL_OBJECT *button, long arg) { PHY_VARS_eNB *phy_vars_eNB = RC.eNB[0][0]; for (i=0; idlsch[i][0]->Mlimit; j++) { + for (k=0; kdlsch[k][0]->Mlimit; j++) { phy_vars_eNB->UE_stats[i].dlsch_NAK[k][j]=0; phy_vars_eNB->UE_stats[i].dlsch_ACK[k][j]=0; phy_vars_eNB->UE_stats[i].dlsch_trials[k][j]=0; From 96ae9a915f28904a225ac2845b1556d97c90a1e2 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 13 Jan 2022 19:51:48 +0000 Subject: [PATCH 066/233] InitialContextSetupResponse with COTS UE --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index eda7f77a47..d26818fc4a 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1026,7 +1026,7 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); - coreset->controlResourceSetId=1; + coreset->controlResourceSetId=2; // frequency domain resources depends on BWP size // options are 24, 48 or 96 coreset->frequencyDomainResources.buf = calloc(1,6); @@ -1068,9 +1068,9 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); - ss->searchSpaceId = 1; + ss->searchSpaceId = 3; ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); - *ss->controlResourceSetId=1; + *ss->controlResourceSetId=2; ss->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*ss->monitoringSlotPeriodicityAndOffset)); ss->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; ss->duration=NULL; @@ -1096,13 +1096,11 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=NULL; if(get_softmodem_params()->sa) { - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1)); - *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=0; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=NULL; } bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->pagingSearchSpace=NULL; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); - *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); @@ -1134,7 +1132,7 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); coreset = calloc(1,sizeof(*coreset)); - coreset->controlResourceSetId=1; + coreset->controlResourceSetId=3; // frequency domain resources depends on BWP size // options are 24, 48 or 96 coreset->frequencyDomainResources.buf = calloc(1,6); @@ -1172,9 +1170,9 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, NR_SearchSpace_t *ss2 = calloc(1,sizeof(*ss2)); - ss2->searchSpaceId=2; + ss2->searchSpaceId=4; ss2->controlResourceSetId=calloc(1,sizeof(*ss2->controlResourceSetId)); - *ss2->controlResourceSetId=1; + *ss2->controlResourceSetId=3; ss2->monitoringSlotPeriodicityAndOffset=calloc(1,sizeof(*ss2->monitoringSlotPeriodicityAndOffset)); ss2->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; ss2->monitoringSlotPeriodicityAndOffset->choice.sl1=(NULL_t)0; From 1a6b464331e3c09490a7b6634b3eb542ed5bd905 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Fri, 14 Jan 2022 12:07:26 +0000 Subject: [PATCH 067/233] Avoid segmentation fault in the BWP selection --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 35294ce775..875b872f96 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1853,11 +1853,13 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; - const int bwp_id = servingCellConfig ? *servingCellConfig->firstActiveDownlinkBWP_Id : 0; + const int bwp_id = servingCellConfig && servingCellConfig->firstActiveDownlinkBWP_Id ? + *servingCellConfig->firstActiveDownlinkBWP_Id : 0; sched_ctrl->active_bwp = bwpList && bwp_id > 0 ? bwpList->list.array[bwp_id - 1] : NULL; const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; - const int ubwp_id = servingCellConfig ? *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; + const int ubwp_id = servingCellConfig && servingCellConfig->uplinkConfig && servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id ? + *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; sched_ctrl->active_ubwp = ubwpList && ubwp_id > 0 ? ubwpList->list.array[ubwp_id - 1] : NULL; if(sched_ctrl->active_bwp) { From 370b1f4d798f62c0702153b65d8827f76b5ea4a5 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Mon, 17 Jan 2022 10:30:35 +0000 Subject: [PATCH 068/233] Fix build warning after merge with develop --- openair1/PHY/TOOLS/lte_enb_scope.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openair1/PHY/TOOLS/lte_enb_scope.c b/openair1/PHY/TOOLS/lte_enb_scope.c index 33772a5d0d..0e873addbc 100644 --- a/openair1/PHY/TOOLS/lte_enb_scope.c +++ b/openair1/PHY/TOOLS/lte_enb_scope.c @@ -49,12 +49,6 @@ void reset_stats(FL_OBJECT *button, long arg) { int i,j,k; PHY_VARS_eNB *phy_vars_eNB = RC.eNB[0][0]; - printf("XXX %d %d %d\n", - sizeofArray(phy_vars_eNB->UE_stats), - sizeofArray( phy_vars_eNB->UE_stats[i].dlsch_NAK), - sizeofArray( *phy_vars_eNB->UE_stats[i].dlsch_NAK) - ); - for (i=0; iUE_stats); i++) { for (k=0; kUE_stats[i].dlsch_NAK); k++) { //harq_processes for (j=0; jUE_stats[i].dlsch_NAK); j++) { From 65698f6a8f813d2a5bf53978d533f0f24db756a0 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 17 Jan 2022 15:15:47 +0100 Subject: [PATCH 069/233] some improvements in ul-mimo code --- executables/nr-uesoftmodem.c | 2 - .../nfapi/public_inc/nfapi_nr_interface_scf.h | 1 - nfapi/open-nFAPI/nfapi/src/nfapi_p7.c | 2 - openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 2 +- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 311 ++++++++---------- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 8 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 38 ++- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 4 + openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 75 ++--- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 1 - 10 files changed, 219 insertions(+), 225 deletions(-) diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 1d36ff9017..ac89bcfd4e 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -493,8 +493,6 @@ int main( int argc, char **argv ) { set_options(CC_id, UE[CC_id]); NR_UE_MAC_INST_t *mac = get_mac_inst(0); - NR_DL_FRAME_PARMS *fp = &UE[CC_id]->frame_parms; - mac->phy_config.config_req.carrier_config.num_tx_ant = fp->nb_antennas_tx; if (get_softmodem_params()->sa) { // set frame config to initial values from command line and assume that the SSB is centered on the grid uint16_t nr_band = get_band(downlink_frequency[CC_id][0],uplink_frequency_offset[CC_id][0]); diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index 4e8d3b9401..e3b7a55f7b 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -1213,7 +1213,6 @@ typedef struct uint8_t transformPrecoder; uint16_t data_scrambling_id; uint8_t nrOfLayers; - uint8_t Tpmi; //DMRS uint16_t ul_dmrs_symb_pos; uint8_t dmrs_config_type; diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c index 9b844d22cf..8d83151ce8 100644 --- a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c +++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c @@ -950,7 +950,6 @@ static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, ui push8(pusch_pdu->transformPrecoder, ppWritePackedMsg, end) && push16(pusch_pdu->data_scrambling_id, ppWritePackedMsg, end) && push8(pusch_pdu->nrOfLayers, ppWritePackedMsg, end) && - push8(pusch_pdu->Tpmi, ppWritePackedMsg, end) && push16(pusch_pdu->ul_dmrs_symb_pos, ppWritePackedMsg, end) && push8(pusch_pdu->dmrs_config_type, ppWritePackedMsg, end) && push16(pusch_pdu->ul_dmrs_scrambling_id, ppWritePackedMsg, end) && @@ -4449,7 +4448,6 @@ static uint8_t unpack_ul_tti_request_pusch_pdu(void *tlv, uint8_t **ppReadPacked pull8(ppReadPackedMsg, &pusch_pdu->transformPrecoder, end) && pull16(ppReadPackedMsg, &pusch_pdu->data_scrambling_id, end) && pull8(ppReadPackedMsg, &pusch_pdu->nrOfLayers, end) && - pull8(ppReadPackedMsg, &pusch_pdu->Tpmi, end) && pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_symb_pos, end) && pull8(ppReadPackedMsg, &pusch_pdu->dmrs_config_type, end) && pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_scrambling_id, end) && diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 9bbc61d4b6..edd997a30f 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -285,7 +285,7 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, start_meas(&gNB->dlsch_resource_mapping_stats); for (int ap=0; apnrOfLayers; ap++) { - // DMRS params for this ap + // DMRS params for this poir get_Wt(Wt, ap, dmrs_Type); get_Wf(Wf, ap, dmrs_Type); delta = get_delta(ap, dmrs_Type); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index d174d85eac..84ad8aa8af 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -37,6 +37,7 @@ #include "PHY/MODULATION/nr_modulation.h" #include "PHY/MODULATION/modulation_common.h" #include "common/utils/assertions.h" +#include "common/utils/nr/nr_common.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "PHY/NR_TRANSPORT/nr_sch_dmrs.h" @@ -107,7 +108,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, int8_t Wf[2], Wt[2]; int l_prime[2], delta; uint8_t nb_dmrs_re_per_rb; - int ap, i; + int i; int sample_offsetF, N_RE_prime; NR_DL_FRAME_PARMS *frame_parms = &UE->frame_parms; @@ -207,99 +208,99 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////// //////////////////////////////////////////////////////////////////////// - /////////////////////////DMRS Modulation///////////////////////// - /////////// - uint32_t ***pusch_dmrs = UE->nr_gold_pusch_dmrs[slot]; - uint16_t n_dmrs = (pusch_pdu->bwp_start + start_rb + nb_rb)*((dmrs_type == pusch_dmrs_type1) ? 6:4); - int16_t mod_dmrs[n_dmrs<<1] __attribute((aligned(16))); - /////////// - //////////////////////////////////////////////////////////////////////// + /////////////////////////DMRS Modulation///////////////////////// + /////////// + uint32_t ***pusch_dmrs = UE->nr_gold_pusch_dmrs[slot]; + uint16_t n_dmrs = (pusch_pdu->bwp_start + start_rb + nb_rb)*((dmrs_type == pusch_dmrs_type1) ? 6:4); + int16_t mod_dmrs[n_dmrs<<1] __attribute((aligned(16))); + /////////// + //////////////////////////////////////////////////////////////////////// - /////////////////////////PTRS parameters' initialization///////////////////////// - /////////// + /////////////////////////PTRS parameters' initialization///////////////////////// + /////////// - int16_t mod_ptrs[nb_rb] __attribute((aligned(16))); // assume maximum number of PTRS per pusch allocation - uint8_t L_ptrs, K_ptrs = 0; - uint16_t beta_ptrs = 1; // temp value until power control is implemented + int16_t mod_ptrs[nb_rb] __attribute((aligned(16))); // assume maximum number of PTRS per pusch allocation + uint8_t L_ptrs, K_ptrs = 0; + uint16_t beta_ptrs = 1; // temp value until power control is implemented - if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { + if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { - K_ptrs = harq_process_ul_ue->pusch_pdu.pusch_ptrs.ptrs_freq_density; - L_ptrs = 1<pusch_pdu.pusch_ptrs.ptrs_time_density; + K_ptrs = harq_process_ul_ue->pusch_pdu.pusch_ptrs.ptrs_freq_density; + L_ptrs = 1<pusch_pdu.pusch_ptrs.ptrs_time_density; - beta_ptrs = 1; // temp value until power control is implemented + beta_ptrs = 1; // temp value until power control is implemented - ulsch_ue->ptrs_symbols = 0; + ulsch_ue->ptrs_symbols = 0; - set_ptrs_symb_idx(&ulsch_ue->ptrs_symbols, - number_of_symbols, - start_symbol, - L_ptrs, - ul_dmrs_symb_pos); - } - - /////////// - //////////////////////////////////////////////////////////////////////////////// - - /////////////////////////ULSCH layer mapping///////////////////////// - /////////// - - nr_ue_layer_mapping(UE->ulsch[thread_id][gNB_id], - Nl, - available_bits/mod_order, - tx_layers); - - /////////// - //////////////////////////////////////////////////////////////////////// - - - //////////////////////// ULSCH transform precoding //////////////////////// - /////////// - - l_prime[0] = 0; // single symbol ap 0 - - uint16_t index; - uint8_t u = 0, v = 0; - int16_t *dmrs_seq = NULL; - - // if transform precoding is enbaled (value 0) - if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { - - uint32_t nb_re_pusch=nb_rb * NR_NB_SC_PER_RB; - uint32_t y_offset = 0; - uint16_t num_dmrs_res_per_symbol = nb_rb*(NR_NB_SC_PER_RB/2); - - // Calculate index to dmrs seq array based on number of DMRS Subcarriers on this symbol - index = get_index_for_dmrs_lowpapr_seq(num_dmrs_res_per_symbol); - u = pusch_pdu->dfts_ofdm.low_papr_group_number; - v = pusch_pdu->dfts_ofdm.low_papr_sequence_number; - dmrs_seq = dmrs_lowpaprtype1_ul_ref_sig[u][v][index]; - - AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); - AssertFatal(dmrs_seq != NULL, "DMRS low PAPR seq not found, check if DMRS sequences are generated"); - - LOG_D(PHY,"Transform Precoding params. u: %d, v: %d, index for dmrsseq: %d\n", u, v, index); - - for (l = start_symbol; l < start_symbol + number_of_symbols; l++) { - - if((ul_dmrs_symb_pos >> l) & 0x01) - /* In the symbol with DMRS no data would be transmitted CDM groups is 2*/ - continue; - - nr_dft(&ulsch_ue->y[y_offset], &((int32_t*)tx_layers[0])[y_offset], nb_re_pusch); - - y_offset = y_offset + nb_re_pusch; - - LOG_D(PHY,"Transform precoding being done on data- symbol: %d, nb_re_pusch: %d, y_offset: %d\n", l, nb_re_pusch, y_offset); - - #ifdef DEBUG_PUSCH_MAPPING - printf("NR_ULSCH_UE: y_offset %d\t nb_re_pusch %d \t Symbol %d \t nb_rb %d \n", - y_offset, nb_re_pusch, l, nb_rb); - #endif + set_ptrs_symb_idx(&ulsch_ue->ptrs_symbols, + number_of_symbols, + start_symbol, + L_ptrs, + ul_dmrs_symb_pos); } - #ifdef DEBUG_DFT_IDFT + /////////// + //////////////////////////////////////////////////////////////////////////////// + + /////////////////////////ULSCH layer mapping///////////////////////// + /////////// + + nr_ue_layer_mapping(UE->ulsch[thread_id][gNB_id], + Nl, + available_bits/mod_order, + tx_layers); + + /////////// + //////////////////////////////////////////////////////////////////////// + + + //////////////////////// ULSCH transform precoding //////////////////////// + /////////// + + l_prime[0] = 0; // single symbol ap 0 + + uint16_t index; + uint8_t u = 0, v = 0; + int16_t *dmrs_seq = NULL; + + // if transform precoding is enbaled (value 0) + if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { + + uint32_t nb_re_pusch=nb_rb * NR_NB_SC_PER_RB; + uint32_t y_offset = 0; + uint16_t num_dmrs_res_per_symbol = nb_rb*(NR_NB_SC_PER_RB/2); + + // Calculate index to dmrs seq array based on number of DMRS Subcarriers on this symbol + index = get_index_for_dmrs_lowpapr_seq(num_dmrs_res_per_symbol); + u = pusch_pdu->dfts_ofdm.low_papr_group_number; + v = pusch_pdu->dfts_ofdm.low_papr_sequence_number; + dmrs_seq = dmrs_lowpaprtype1_ul_ref_sig[u][v][index]; + + AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); + AssertFatal(dmrs_seq != NULL, "DMRS low PAPR seq not found, check if DMRS sequences are generated"); + + LOG_D(PHY,"Transform Precoding params. u: %d, v: %d, index for dmrsseq: %d\n", u, v, index); + + for (l = start_symbol; l < start_symbol + number_of_symbols; l++) { + + if((ul_dmrs_symb_pos >> l) & 0x01) + /* In the symbol with DMRS no data would be transmitted CDM groups is 2*/ + continue; + + nr_dft(&ulsch_ue->y[y_offset], &((int32_t*)tx_layers[0])[y_offset], nb_re_pusch); + + y_offset = y_offset + nb_re_pusch; + + LOG_D(PHY,"Transform precoding being done on data- symbol: %d, nb_re_pusch: %d, y_offset: %d\n", l, nb_re_pusch, y_offset); + + #ifdef DEBUG_PUSCH_MAPPING + printf("NR_ULSCH_UE: y_offset %d\t nb_re_pusch %d \t Symbol %d \t nb_rb %d \n", + y_offset, nb_re_pusch, l, nb_rb); + #endif + } + + #ifdef DEBUG_DFT_IDFT int32_t debug_symbols[MAX_NUM_NR_RE] __attribute__ ((aligned(16))); int offset = 0; printf("NR_ULSCH_UE: available_bits: %d, mod_order: %d", available_bits,mod_order); @@ -320,20 +321,19 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, LOG_M("postDFT_all_symbols.m","UE_postDFT", ulsch_ue->y,number_of_symbols*nb_re_pusch,1,1); LOG_M("DEBUG_IDFT_SYMBOLS.m","UE_Debug_IDFT", debug_symbols,number_of_symbols*nb_re_pusch,1,1); LOG_M("UE_DMRS_SEQ.m","UE_DMRS_SEQ", dmrs_seq,nb_re_pusch,1,1); - #endif + #endif - } + } - /////////// - //////////////////////////////////////////////////////////////////////// + /////////// + //////////////////////////////////////////////////////////////////////// - /////////////////////////ULSCH RE mapping///////////////////////// - /////////// + /////////////////////////ULSCH RE mapping///////////////////////// + /////////// - for (ap=0; ap< Nl; ap++) - { + for (int l=0; l < Nl; l++) { uint8_t k_prime = 0; uint16_t m = 0; @@ -341,24 +341,22 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, printf("NR_ULSCH_UE: Value of CELL ID %d /t, u %d \n", frame_parms->Nid_cell, u); #endif - // DMRS params for this ap - get_Wt(Wt, ap, dmrs_type); - get_Wf(Wf, ap, dmrs_type); - delta = get_delta(ap, dmrs_type); + int dmrs_port = get_dmrs_port(l,pusch_pdu->dmrs_ports); + // DMRS params for this dmrs port + get_Wt(Wt, dmrs_port, dmrs_type); + get_Wf(Wf, dmrs_port, dmrs_type); + delta = get_delta(dmrs_port, dmrs_type); - for (l=start_symbol; l> l) & 0x01) - { + if ((ul_dmrs_symb_pos >> l) & 0x01) { is_dmrs_sym = 1; - if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) - { + if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { if (dmrs_type == pusch_dmrs_type1) dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; else @@ -369,39 +367,33 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, LOG_D(PHY,"DMRS in symbol %d\n",l); nr_modulation(pusch_dmrs[l][0], n_dmrs*2, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated } - else - { + else { dmrs_idx = 0; } } - else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) - { + else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { AssertFatal(pusch_pdu->transformPrecoder == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); - if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) - { + if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) { is_ptrs_sym = 1; nr_modulation(pusch_dmrs[l][0], nb_rb, DMRS_MOD_ORDER, mod_ptrs); } } - for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) - { + for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) { uint8_t is_dmrs = 0; uint8_t is_ptrs = 0; sample_offsetF = l*frame_parms->ofdm_symbol_size + k; - if (is_dmrs_sym) - { + if (is_dmrs_sym) { if (k == ((start_sc+get_dmrs_freq_idx_ul(n, k_prime, delta, dmrs_type))%frame_parms->ofdm_symbol_size)) is_dmrs = 1; } - else if (is_ptrs_sym) - { + else if (is_ptrs_sym) { is_ptrs = is_ptrs_subcarrier(k, rnti, - ap, + dmrs_port, dmrs_type, K_ptrs, nb_rb, @@ -410,23 +402,20 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, frame_parms->ofdm_symbol_size); } - if (is_dmrs == 1) - { - if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) - { - ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; + if (is_dmrs == 1) { + if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { + ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; } - else - { - ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; + else { + ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; } #ifdef DEBUG_PUSCH_MAPPING printf("DMRS: Layer: %d\t, dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", - ap, dmrs_idx, l, k, k_prime, n, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); + l, dmrs_idx, l, k, k_prime, n, ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1], + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1]); #endif dmrs_idx++; @@ -434,47 +423,41 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, k_prime&=1; n+=(k_prime)?0:1; } - else if (is_ptrs == 1) - { - ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; + else if (is_ptrs == 1) { + ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; ptrs_idx++; } - else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) - { - if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) - { - ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[ap])[m<<1]; - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[ap])[(m<<1) + 1]; + else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) { + if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { + ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[l])[m<<1]; + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[l])[(m<<1) + 1]; } - else - { - ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; + else { + ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; } #ifdef DEBUG_PUSCH_MAPPING printf("DATA: layer %d\t m %d\t l %d \t k %d \t txdataF: %d %d\n", - ap, m, l, k, ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1], - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1]); + l, m, l, k, ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1], + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1]); #endif m++; } - else - { - ((int16_t*)tx_precoding[ap])[(sample_offsetF)<<1] = 0; - ((int16_t*)tx_precoding[ap])[((sample_offsetF)<<1) + 1] = 0; + else { + ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = 0; + ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = 0; } - if (++k >= frame_parms->ofdm_symbol_size) - { + if (++k >= frame_parms->ofdm_symbol_size) { k -= frame_parms->ofdm_symbol_size; } } //for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) }//for (l=start_symbol; lnb_antennas_tx; ap++) - { - for (int l=start_symbol; lnb_antennas_tx; ap++) { + for (int l=start_symbol; lTpmi; @@ -510,12 +490,10 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, k -= frame_parms->ofdm_symbol_size; } } - else - { + else { //get the precoding matrix weights: char *W_prec; - switch (frame_parms->nb_antennas_tx) - { + switch (frame_parms->nb_antennas_tx) { case 1://1 antenna port W_prec = nr_W_1l_2p[pmi][ap]; break; @@ -541,15 +519,13 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, break; } - for (int i=0; iofdm_symbol_size + k; int32_t precodatatx_F = nr_layer_precoder(tx_precoding, W_prec, pusch_pdu->nrOfLayers, re_offset); ((int16_t*)txdataF[ap])[(re_offset<<1)] = ((int16_t *) &precodatatx_F)[0]; ((int16_t*)txdataF[ap])[(re_offset<<1) + 1] = ((int16_t *) &precodatatx_F)[1]; - if (++k >= frame_parms->ofdm_symbol_size) - { + if (++k >= frame_parms->ofdm_symbol_size) { k -= frame_parms->ofdm_symbol_size; } } @@ -557,7 +533,6 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, } //RB loop } // symbol loop }// port loop - } //for (cwd_index = 0;cwd_index < num_of_codewords; cwd_index++) NR_UL_UE_HARQ_t *harq_process_ulsch=NULL; @@ -573,7 +548,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE, uint8_t slot, NR_DL_FRAME_PARMS *frame_parms, - uint8_t Nl) { + uint8_t n_antenna_ports) { int tx_offset, ap; int32_t **txdata; @@ -595,7 +570,7 @@ uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE, txdataF = UE->common_vars.txdataF; int symb_offset = (slot%frame_parms->slots_per_subframe)*frame_parms->symbols_per_slot; - for(ap = 0; ap < Nl; ap++) { + for(ap = 0; ap < n_antenna_ports; ap++) { for (int s=0;s (%d.%d)\n", @@ -613,7 +588,7 @@ uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE, } } - for (ap = 0; ap < Nl; ap++) { + for (ap = 0; ap < n_antenna_ports; ap++) { if (frame_parms->Ncp == 1) { // extended cyclic prefix PHY_ofdm_mod(txdataF[ap], &txdata[ap][tx_offset], diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 206a55347c..d185517fcc 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -369,10 +369,10 @@ void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH nr_ulsch_layer_demapping(gNB->pusch_vars[ULSCH_id]->llr, - pusch_pdu->nrOfLayers, - pusch_pdu->qam_mod_order, - G, - gNB->pusch_vars[ULSCH_id]->llr_layers); + pusch_pdu->nrOfLayers, + pusch_pdu->qam_mod_order, + G, + gNB->pusch_vars[ULSCH_id]->llr_layers); //---------------------------------------------------------- //------------------- ULSCH unscrambling ------------------- diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 7e5be9eeba..b7f928f709 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -33,6 +33,7 @@ #include "LAYER2/NR_MAC_gNB/mac_proto.h" #include "common/utils/nr/nr_common.h" #include +#include #define reserved 0xffff @@ -2531,6 +2532,30 @@ uint8_t get_transformPrecoding(const NR_BWP_UplinkCommon_t *initialUplinkBWP, return -1; } +uint8_t get_pusch_nb_antenna_ports(NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + dci_field_t srs_resource_indicator) { + + uint8_t n_antenna_port = 1; + if (get_softmodem_params()->phy_test == 1) { + // temporary hack to allow UL-MIMO in phy-test mode without SRS + n_antenna_port = *pusch_Config->maxRank; + } + else { + if(srs_config != NULL && srs_resource_indicator.nbits > 0) { + for(int rs = 0; rs < srs_config->srs_ResourceSetToAddModList->list.count; rs++) { + NR_SRS_ResourceSet_t *srs_resource_set = srs_config->srs_ResourceSetToAddModList->list.array[rs]; + if(srs_resource_set->usage == NR_SRS_ResourceSet__usage_codebook) { + NR_SRS_Resource_t *srs_resource = srs_config->srs_ResourceToAddModList->list.array[srs_resource_indicator.val]; + AssertFatal(srs_resource != NULL,"SRS resource indicated by DCI does not exist\n"); + n_antenna_port = 1<nrofSRS_Ports; + } + } + } + } + return n_antenna_port; +} + uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, const NR_BWP_UplinkCommon_t *initialUplinkBWP, const NR_CellGroupConfig_t *cg, @@ -2544,17 +2569,16 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, uint16_t numRBG = 0; long rbg_size_config; int num_entries = 0; - int pusch_antenna_ports = 1; // TODO hardcoded number of antenna ports for pusch const NR_BWP_DownlinkDedicated_t *bwpd = NULL; const NR_BWP_UplinkDedicated_t *ubwpd = NULL; const NR_BWP_DownlinkCommon_t *bwpc = NULL; const NR_BWP_UplinkCommon_t *ubwpc = NULL; - const NR_PDSCH_Config_t *pdsch_Config = NULL; - const NR_PUSCH_Config_t *pusch_Config = NULL; - const NR_PUCCH_Config_t *pucch_Config = NULL; - const NR_PDCCH_Config_t *pdcch_Config = NULL; - const NR_SRS_Config_t *srs_config = NULL; + NR_PDSCH_Config_t *pdsch_Config = NULL; + NR_PUSCH_Config_t *pusch_Config = NULL; + NR_PUCCH_Config_t *pucch_Config = NULL; + NR_PDCCH_Config_t *pdcch_Config = NULL; + NR_SRS_Config_t *srs_config = NULL; if(bwp_id > 0) { AssertFatal(cg!=NULL,"Cellgroup is null and bwp_id!=0"); bwpd=cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]->bwp_Dedicated; @@ -2709,7 +2733,7 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, // Precoding info and number of layers long transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, ubwpd, (uint8_t*)&format, rnti_type, 0); - pusch_antenna_ports = *pusch_Config->maxRank; + uint8_t pusch_antenna_ports = get_pusch_nb_antenna_ports(pusch_Config, srs_config, dci_pdu->srs_resource_indicator); dci_pdu->precoding_information.nbits=0; if (pusch_Config && diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index 8182e1c930..23c022bf0c 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -144,6 +144,10 @@ void get_info_from_tda_tables(int default_abc, void fill_coresetZero(NR_ControlResourceSet_t *coreset0, NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config); void fill_searchSpaceZero(NR_SearchSpace_t *ss0, NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config); +uint8_t get_pusch_nb_antenna_ports(NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + dci_field_t srs_resource_indicator); + uint16_t compute_pucch_prb_size(uint8_t format, uint8_t nr_prbs, uint16_t O_tot, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index adb2c966b0..83981c8a6f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -44,6 +44,7 @@ /* MAC */ #include "NR_MAC_COMMON/nr_mac.h" +#include "NR_MAC_COMMON/nr_mac_common.h" #include "NR_MAC_UE/mac_proto.h" #include "NR_MAC_UE/mac_extern.h" @@ -181,13 +182,16 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con NR_ServingCellConfigCommon_t *scc = mac->scc; NR_BWP_UplinkDedicated_t *ubwpd=NULL; + NR_SRS_Config_t *srs_config = NULL; if (mac->cg && mac->cg->spCellConfig && mac->cg->spCellConfig->spCellConfigDedicated && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && - mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP) + mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP) { ubwpd = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP; + srs_config = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP->srs_Config->choice.setup; + } NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0] ? mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup : @@ -212,7 +216,10 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)){ - uint8_t n_antenna_port = mac->phy_config.config_req.carrier_config.num_tx_ant; + // The UE shall transmit PUSCH using the same antenna port(s) as the SRS port(s) in the SRS resource indicated by the DCI format 0_1 + // 38.214 Section 6.1.1 + + uint8_t n_antenna_port = get_pusch_nb_antenna_ports(pusch_Config, srs_config, dci->srs_resource_indicator); if (n_antenna_port == 1); // 1 antenna port and the higher layer parameter txConfig = codebook 0 bits @@ -331,10 +338,9 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con } // todo: this function shall be reviewed completely because of the many comments left by the author -void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) { +void ul_ports_config(NR_UE_MAC_INST_t *mac, int *n_front_load_symb, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) { - /* ANTENNA_PORTS */ - uint8_t rank = 1; // We need to initialize rank FIXME!!! + uint8_t rank = pusch_config_pdu->nrOfLayers; NR_ServingCellConfigCommon_t *scc = mac->scc; NR_BWP_UplinkDedicated_t *ubwpd=NULL; @@ -373,29 +379,24 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf LOG_D(NR_MAC,"MappingType%s max_length %s, dmrs_type %s, antenna_ports %d\n", pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA?"A":"B",max_length?"len2":"len1",dmrs_type?"type2":"type1",dci->antenna_ports.val); + if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) && - (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-6 - pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC - pusch_config_pdu->dmrs_ports = dci->antenna_ports.val; //TBC + (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-6 + pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; + pusch_config_pdu->dmrs_ports = 1<antenna_ports.val; } if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) && - (dmrs_type == NULL) && (max_length != NULL)) { // tables 7.3.1.1.2-7 + (dmrs_type == NULL) && (max_length != NULL)) { // tables 7.3.1.1.2-7 pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC - pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 3)?(dci->antenna_ports.val-4):(dci->antenna_ports.val); //TBC - //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports > 3)?2:1; //FIXME + pusch_config_pdu->dmrs_ports = 1<<((dci->antenna_ports.val > 3)?(dci->antenna_ports.val-4):(dci->antenna_ports.val)); + *n_front_load_symb = (dci->antenna_ports.val > 3)?2:1; } if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && - (dmrs_type == NULL) && (max_length == NULL)) - { // tables 7.3.1.1.2-8/9/10/11 + (dmrs_type == NULL) && (max_length == NULL)) { // tables 7.3.1.1.2-8/9/10/11 - if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook)) - { - rank = pusch_config_pdu->nrOfLayers; - } - if (rank == 1) { pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; pusch_config_pdu->dmrs_ports =1<<((dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val)); @@ -408,12 +409,12 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf if (rank == 3){ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; - pusch_config_pdu->dmrs_ports = ((1<dmrs_ports = 0x7; // ports 0-2 } if (rank == 4){ pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; - pusch_config_pdu->dmrs_ports = ((1<dmrs_ports = 0xf; // ports 0-3 } } @@ -422,8 +423,8 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf if (rank == 1){ pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC - pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val > 5 ?(dci->antenna_ports.val-6):(dci->antenna_ports.val-2)):dci->antenna_ports.val; //TBC - //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 6)?2:1; //FIXME + pusch_config_pdu->dmrs_ports = 1<<((dci->antenna_ports.val > 1)?(dci->antenna_ports.val > 5 ?(dci->antenna_ports.val-6):(dci->antenna_ports.val-2)):dci->antenna_ports.val); + *n_front_load_symb = (dci->antenna_ports.val > 6)?2:1; } if (rank == 2){ @@ -431,7 +432,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf pusch_config_pdu->dmrs_ports = 0; //FIXME //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_13[dci->antenna_ports.val][1]; //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_13[dci->antenna_ports.val][2]; - //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 3)?2:1; // FIXME + //n_front_load_symb = (dci->antenna_ports.val > 3)?2:1; // FIXME } if (rank == 3){ @@ -440,7 +441,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_14[dci->antenna_ports.val][1]; //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_14[dci->antenna_ports.val][2]; //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_14[dci->antenna_ports.val][3]; - //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME + //n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME } if (rank == 4){ @@ -450,7 +451,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_15[dci->antenna_ports.val][2]; //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_15[dci->antenna_ports.val][3]; //pusch_config_pdu->dmrs_ports[3] = table_7_3_1_1_2_15[dci->antenna_ports.val][4]; - //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME + //n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME } } @@ -494,7 +495,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf if (rank == 1){ pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_20[dci->antenna_ports.val][0]; //TBC pusch_config_pdu->dmrs_ports = table_7_3_1_1_2_20[dci->antenna_ports.val][1]; //TBC - //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_20[dci->antenna_ports.val][2]; //FIXME + //n_front_load_symb = table_7_3_1_1_2_20[dci->antenna_ports.val][2]; //FIXME } if (rank == 2){ @@ -502,7 +503,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf pusch_config_pdu->dmrs_ports = 0; //FIXME //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_21[dci->antenna_ports.val][1]; //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_21[dci->antenna_ports.val][2]; - //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_21[dci->antenna_ports.val][3]; //FIXME + //n_front_load_symb = table_7_3_1_1_2_21[dci->antenna_ports.val][3]; //FIXME } if (rank == 3){ @@ -511,7 +512,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_22[dci->antenna_ports.val][1]; //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_22[dci->antenna_ports.val][2]; //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_22[dci->antenna_ports.val][3]; - //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_22[dci->antenna_ports.val][4]; //FIXME + //n_front_load_symb = table_7_3_1_1_2_22[dci->antenna_ports.val][4]; //FIXME } if (rank == 4){ @@ -521,7 +522,7 @@ void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_conf //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_23[dci->antenna_ports.val][2]; //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_23[dci->antenna_ports.val][3]; //pusch_config_pdu->dmrs_ports[3] = table_7_3_1_1_2_23[dci->antenna_ports.val][4]; - //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME + //n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME } } LOG_D(NR_MAC,"num_dmrs_cdm_grps_no_data %d, dmrs_ports %d\n",pusch_config_pdu->num_dmrs_cdm_grps_no_data,pusch_config_pdu->dmrs_ports); @@ -565,7 +566,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, else initialUplinkBWP = &mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP; pusch_dmrs_AdditionalPosition_t add_pos = pusch_dmrs_pos2; - pusch_maxLength_t dmrslength = pusch_len1; + int dmrslength = 1; if (rar_grant) { @@ -712,7 +713,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ul_layers_config(mac, pusch_config_pdu, dci); - ul_ports_config(mac, pusch_config_pdu, dci); + ul_ports_config(mac, &dmrslength, pusch_config_pdu, dci); } else { @@ -805,22 +806,18 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->absolute_delta_PUSCH = 4; } - if (NR_DMRS_ulconfig != NULL) { + if (NR_DMRS_ulconfig != NULL) add_pos = (NR_DMRS_ulconfig->dmrs_AdditionalPosition == NULL) ? 2 : *NR_DMRS_ulconfig->dmrs_AdditionalPosition; - dmrslength = NR_DMRS_ulconfig->maxLength == NULL ? pusch_len1 : pusch_len2; - } /* DMRS */ l_prime_mask = get_l_prime(pusch_config_pdu->nr_of_symbols, mappingtype, add_pos, dmrslength, pusch_config_pdu->start_symbol_index, mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); - if (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) - { - if (*dci_format != NR_UL_DCI_FORMAT_0_1) - { + if (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { + if (*dci_format != NR_UL_DCI_FORMAT_0_1) { pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; - } + } } else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index eb727494d8..08208300fd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -691,7 +691,6 @@ void config_uldci(const NR_BWP_Uplink_t *ubwp, "Non Codebook configuration non supported\n"); dci_pdu_rel15->srs_resource_indicator.val = 0; // taking resource 0 for SRS } - AssertFatal((pusch_pdu->Tpmi==0), "unsupport Tpmi\n"); dci_pdu_rel15->precoding_information.val= 0; if (pusch_pdu->nrOfLayers == 2) { From c1d157dc429153fb2284b6fb13839cce81273dca Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 17 Jan 2022 19:28:17 +0100 Subject: [PATCH 070/233] fixing compilation issues --- cmake_targets/CMakeLists.txt | 3 +++ executables/nr-ue.c | 2 ++ openair1/SIMULATION/NR_PHY/dlschsim.c | 3 +++ openair1/SIMULATION/NR_PHY/nr_dummy_functions.c | 2 ++ openair1/SIMULATION/NR_PHY/pucchsim.c | 3 ++- openair1/SIMULATION/NR_PHY/ulschsim.c | 2 ++ 6 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index bf3bb2b017..0ff76d145f 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -3171,6 +3171,7 @@ add_executable(nr_dlschsim ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR_DIR}/common/utils/utils.c ${OPENAIR_DIR}/common/utils/threadPool/thread-pool.c + ${OPENAIR_DIR}/executables/softmodem-common.c ${UTIL_SRC} ${T_SOURCE} ${SHLIB_LOADER_SOURCES} @@ -3203,6 +3204,7 @@ add_executable(nr_pucchsim ${OPENAIR_DIR}/common/utils/system.c ${OPENAIR_DIR}/common/utils/utils.c ${OPENAIR_DIR}/common/utils/threadPool/thread-pool.c + ${OPENAIR_DIR}/executables/softmodem-common.c ${UTIL_SRC} ${T_SOURCE} ${SHLIB_LOADER_SOURCES} @@ -3259,6 +3261,7 @@ add_executable(nr_ulschsim ${OPENAIR_DIR}/common/utils/utils.c ${OPENAIR_DIR}/common/utils/system.c ${OPENAIR_DIR}/common/utils/nr/nr_common.c + ${OPENAIR_DIR}/executables/softmodem-common.c ${PHY_INTERFACE_DIR}/queue_t.c ${UTIL_SRC} ${T_SOURCE} diff --git a/executables/nr-ue.c b/executables/nr-ue.c index d2553a03bf..747eb4bb45 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -19,6 +19,8 @@ * contact@openairinterface.org */ +#define _GNU_SOURCE +#include #include #include "executables/nr-uesoftmodem.h" #include "PHY/phy_extern_nr_ue.h" diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index ff07422c6b..5d34b75047 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -47,13 +47,16 @@ #include "openair1/SIMULATION/NR_PHY/nr_unitary_defs.h" #include "openair1/SIMULATION/NR_PHY/nr_dummy_functions.c" + //#define DEBUG_NR_DLSCHSIM +THREAD_STRUCT thread_struct; PHY_VARS_gNB *gNB; PHY_VARS_NR_UE *UE; RAN_CONTEXT_t RC; UE_nr_rxtx_proc_t proc; int32_t uplink_frequency_offset[MAX_NUM_CCs][4]; +uint64_t downlink_frequency[MAX_NUM_CCs][4]; double cpuf; //uint8_t nfapi_mode = 0; diff --git a/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c b/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c index 7a47410129..69ae17c3ac 100644 --- a/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c +++ b/openair1/SIMULATION/NR_PHY/nr_dummy_functions.c @@ -20,6 +20,8 @@ nfapi_mode_t nfapi_mod; nfapi_mode_t nfapi_getmode(void) { return nfapi_mod; } +void nfapi_setmode(nfapi_mode_t nfapi_mode) {} + int dummy_nr_ue_dl_indication(nr_downlink_indication_t *dl_info) { return(0); } int dummy_nr_ue_ul_indication(nr_uplink_indication_t *ul_info) { return(0); } void nr_fill_dl_indication(nr_downlink_indication_t *dl_ind, diff --git a/openair1/SIMULATION/NR_PHY/pucchsim.c b/openair1/SIMULATION/NR_PHY/pucchsim.c index 213924e5fd..9f268bb879 100644 --- a/openair1/SIMULATION/NR_PHY/pucchsim.c +++ b/openair1/SIMULATION/NR_PHY/pucchsim.c @@ -46,12 +46,13 @@ #include "openair1/SIMULATION/NR_PHY/nr_unitary_defs.h" #include "openair1/SIMULATION/NR_PHY/nr_dummy_functions.c" - +THREAD_STRUCT thread_struct; PHY_VARS_gNB *gNB; PHY_VARS_NR_UE *UE; RAN_CONTEXT_t RC; openair0_config_t openair0_cfg[MAX_CARDS]; int32_t uplink_frequency_offset[MAX_NUM_CCs][4]; +uint64_t downlink_frequency[MAX_NUM_CCs][4]; double cpuf; //uint8_t nfapi_mode = 0; diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index fb8b4295ae..e51d790d24 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -49,10 +49,12 @@ //#define DEBUG_NR_ULSCHSIM +THREAD_STRUCT thread_struct; PHY_VARS_gNB *gNB; PHY_VARS_NR_UE *UE; RAN_CONTEXT_t RC; int32_t uplink_frequency_offset[MAX_NUM_CCs][4]; +uint64_t downlink_frequency[MAX_NUM_CCs][4]; void init_downlink_harq_status(NR_DL_UE_HARQ_t *dl_harq) {} From 2b6c64c98c02bfdda2a4a9c3af4f27b2755d34f6 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Mon, 17 Jan 2022 20:39:27 +0000 Subject: [PATCH 071/233] Backup improvements in servingcellconfigdedicated --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 409 ++++++++++++------ ...gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 46 +- 2 files changed, 268 insertions(+), 187 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index d26818fc4a..b0d1d1459e 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1000,22 +1000,11 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, AssertFatal(1==0,"SSB bitmap size value %d undefined (allowed values 1,2,3) \n", scc->ssb_PositionsInBurst->present); } - if (servingcellconfigdedicated) { - bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; - } else { - if(servingcellconfigdedicated->downlinkBWP_ToAddModList == NULL){ - servingcellconfigdedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*servingcellconfigdedicated->downlinkBWP_ToAddModList)); - } - bwp = calloc(1,sizeof(*bwp)); - bwp->bwp_Id = bwp_loop + 1; - bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); - // copy common BWP size from initial BWP except for bandwdith - memcpy((void*)&bwp->bwp_Common->genericParameters, - &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters, - sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - bwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - } + bwp->bwp_Id = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); + memcpy((void*)&bwp->bwp_Common->genericParameters,&scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters,sizeof(bwp->bwp_Common->genericParameters)); + bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; + bwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; @@ -1023,21 +1012,15 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet)); - int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); + int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); coreset->controlResourceSetId=2; // frequency domain resources depends on BWP size // options are 24, 48 or 96 coreset->frequencyDomainResources.buf = calloc(1,6); - if (curr_bwp < 48) - coreset->frequencyDomainResources.buf[0] = 0xf0; - else - coreset->frequencyDomainResources.buf[0] = 0xff; - if (curr_bwp < 96) - coreset->frequencyDomainResources.buf[1] = 0; - else - coreset->frequencyDomainResources.buf[1] = 0xff; + coreset->frequencyDomainResources.buf[0] = 0xf0; + coreset->frequencyDomainResources.buf[1] = 0; coreset->frequencyDomainResources.buf[2] = 0; coreset->frequencyDomainResources.buf[3] = 0; coreset->frequencyDomainResources.buf[4] = 0; @@ -1083,7 +1066,7 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ss->monitoringSymbolsWithinSlot->bits_unused = 2; ss->nrofCandidates = calloc(1,sizeof(*ss->nrofCandidates)); ss->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; - ss->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; + ss->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n2; ss->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; ss->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; ss->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; @@ -1121,10 +1104,7 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); } - if (!servingcellconfigdedicated) { - bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); - } - + bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); @@ -1196,15 +1176,13 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss2); - if (!servingcellconfigdedicated) { - bwp->bwp_Dedicated->pdsch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); - bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); - } + bwp->bwp_Dedicated->pdsch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); + bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; + bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL; @@ -1237,24 +1215,12 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, rrc_gNB_carrier_data_t *carrier, int uid) { - if (servingcellconfigdedicated) { - ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; - } else { - if (servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList == NULL) { - servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList)); - } - ubwp = calloc(1,sizeof(*ubwp)); - ubwp->bwp_Id = bwp_loop + 1; - ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); - - // copy bwp_Common from Initial UL BWP except for bandwidth - memcpy((void*)&ubwp->bwp_Common->genericParameters, - (void*)&scc->uplinkConfigCommon->initialUplinkBWP->genericParameters, - sizeof(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters)); - ubwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - ubwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - } + ubwp->bwp_Id = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); + ubwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; + ubwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; + ubwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; int curr_bwp = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); @@ -1271,9 +1237,9 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); pucch_Config->resourceSetToReleaseList = NULL; NR_PUCCH_ResourceSet_t *pucchresset0=calloc(1,sizeof(*pucchresset0)); - pucchresset0->pucch_ResourceSetId = 0; + pucchresset0->pucch_ResourceSetId = 1; NR_PUCCH_ResourceId_t *pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); - *pucchresset0id0=0; + *pucchresset0id0=1; ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); pucchresset0->maxPayloadSize=NULL; ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); @@ -1283,7 +1249,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, // configure one single PUCCH0 opportunity for initial connection procedure // one symbol (13) NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); - pucchres0->pucch_ResourceId=0; + pucchres0->pucch_ResourceId=1; pucchres0->startingPRB=(8+uid) % curr_bwp; pucchres0->intraSlotFrequencyHopping=NULL; pucchres0->secondHopPRB=NULL; @@ -1298,8 +1264,8 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, // 40 slot period pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); - schedulingRequestResourceConfig->schedulingRequestResourceId = 1; - schedulingRequestResourceConfig->schedulingRequestID= 0; + schedulingRequestResourceConfig->schedulingRequestResourceId = 2; + schedulingRequestResourceConfig->schedulingRequestID= 1; schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; // note: make sure that there is no issue here. Later choose the RNTI accordingly. @@ -1327,28 +1293,19 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, initialUplinkBWP->pusch_Config = calloc(1,sizeof(*initialUplinkBWP->pusch_Config)); initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - NR_PUSCH_Config_t *pusch_Config = NULL; - if (servingcellconfigdedicated) { - pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Dedicated->pusch_Config->choice.setup; - } else { - pusch_Config = calloc(1,sizeof(*pusch_Config)); - } + NR_PUSCH_Config_t *pusch_Config = calloc(1,sizeof(*pusch_Config)); initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA = NULL; - if (!servingcellconfigdedicated) { - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); - } + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; NR_DMRS_UplinkConfig->dmrs_Type = NULL; NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = calloc(1,sizeof(*NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)); *NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos0; - if (!servingcellconfigdedicated) { - NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; - } + NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; NR_DMRS_UplinkConfig->maxLength=NULL; NR_DMRS_UplinkConfig->transformPrecodingDisabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingDisabled)); NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0 = NULL; @@ -1371,7 +1328,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList)); NR_PUSCH_PathlossReferenceRS_t *plrefRS = calloc(1,sizeof(*plrefRS)); plrefRS->pusch_PathlossReferenceRS_Id=0; - plrefRS->referenceSignal.present = NR_PathlossReferenceRS_Config_PR_ssb_Index; + plrefRS->referenceSignal.present = NR_PUSCH_PathlossReferenceRS__referenceSignal_PR_ssb_Index; plrefRS->referenceSignal.choice.ssb_Index = 0; ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList->list,plrefRS); pusch_Config->pusch_PowerControl->pathlossReferenceRSToReleaseList = NULL; @@ -1487,10 +1444,8 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, ubwp->bwp_Common->pusch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; ubwp->bwp_Common->pucch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; - if (!servingcellconfigdedicated) { - ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); - } + ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; pucch_Config = calloc(1,sizeof(*pucch_Config)); @@ -1602,12 +1557,11 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, ASN_SEQUENCE_ADD(&pucch_Config->pucch_PowerControl->p0_Set->list,p00); pucch_Config->pucch_PowerControl->pathlossReferenceRSs = NULL; - if (!servingcellconfigdedicated) { - // copy pusch_Config from dedicated initialBWP - ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); - ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; - } + // copy pusch_Config from dedicated initialBWP + ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); + ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; + ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; + ubwp->bwp_Dedicated->configuredGrantConfig = NULL; ubwp->bwp_Dedicated->srs_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->srs_Config)); ubwp->bwp_Dedicated->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; @@ -1622,21 +1576,212 @@ void fill_initial_SpCellConfig(rnti_t rnti, NR_ServingCellConfigCommon_t *scc, NR_ServingCellConfig_t *servingcellconfigdedicated, rrc_gNB_carrier_data_t *carrier) { - + int curr_bwp = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); SpCellConfig->servCellIndex = NULL; SpCellConfig->reconfigurationWithSync = NULL; SpCellConfig->rlmInSyncOutOfSyncThreshold = NULL; SpCellConfig->rlf_TimersAndConstants = NULL; - if (servingcellconfigdedicated) { - SpCellConfig->spCellConfigDedicated = servingcellconfigdedicated; - } else { - SpCellConfig->spCellConfigDedicated = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated)); - } + SpCellConfig->spCellConfigDedicated = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated)); + SpCellConfig->spCellConfigDedicated->uplinkConfig = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig)); - if (!SpCellConfig->spCellConfigDedicated->uplinkConfig) { - SpCellConfig->spCellConfigDedicated->uplinkConfig = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig)); + NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); + SpCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; + initialUplinkBWP->pucch_Config = calloc(1,sizeof(*initialUplinkBWP->pucch_Config)); + initialUplinkBWP->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; + NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); + initialUplinkBWP->pucch_Config->choice.setup=pucch_Config; + pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); + pucch_Config->resourceSetToReleaseList = NULL; + NR_PUCCH_ResourceSet_t *pucchresset0=calloc(1,sizeof(*pucchresset0)); + pucchresset0->pucch_ResourceSetId = 0; + NR_PUCCH_ResourceId_t *pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); + *pucchresset0id0=0; + ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); + pucchresset0->maxPayloadSize=NULL; + ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); + + pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); + pucch_Config->resourceToReleaseList = NULL; + // configure one single PUCCH0 opportunity for initial connection procedure + // one symbol (13) + NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); + pucchres0->pucch_ResourceId=0; + //pucchres0->startingPRB=0; + pucchres0->startingPRB=(8+rnti) % curr_bwp; + LOG_D(NR_RRC, "pucchres0->startPRB %ld rnti %d curr_bwp %d\n", pucchres0->startingPRB, rnti, curr_bwp); + pucchres0->intraSlotFrequencyHopping=NULL; + pucchres0->secondHopPRB=NULL; + pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; + pucchres0->format.choice.format0=calloc(1,sizeof(*pucchres0->format.choice.format0)); + pucchres0->format.choice.format0->initialCyclicShift=0; + pucchres0->format.choice.format0->nrofSymbols=1; + pucchres0->format.choice.format0->startingSymbolIndex=13; + ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); + initialUplinkBWP->pusch_Config = calloc(1,sizeof(*initialUplinkBWP->pusch_Config)); + initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; + NR_PUSCH_Config_t *pusch_Config = calloc(1,sizeof(*pusch_Config)); + initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; + pusch_Config->dataScramblingIdentityPUSCH = NULL; + pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); + *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA = NULL; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); + NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + NR_DMRS_UplinkConfig->dmrs_Type = NULL; + NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NULL; /*calloc(1,sizeof(*NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)); + *NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos0;*/ + NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; + NR_DMRS_UplinkConfig->maxLength=NULL; + NR_DMRS_UplinkConfig->transformPrecodingDisabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingDisabled)); + NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0 = NULL; + NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID1 = NULL; + NR_DMRS_UplinkConfig->transformPrecodingEnabled = NULL; + pusch_Config->pusch_PowerControl = calloc(1,sizeof(*pusch_Config->pusch_PowerControl)); + pusch_Config->pusch_PowerControl->tpc_Accumulation = NULL; + pusch_Config->pusch_PowerControl->msg3_Alpha = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->msg3_Alpha)); + *pusch_Config->pusch_PowerControl->msg3_Alpha = NR_Alpha_alpha1; + pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant)); + *pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = -76; + pusch_Config->pusch_PowerControl->p0_AlphaSets = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->p0_AlphaSets)); + NR_P0_PUSCH_AlphaSet_t *aset = calloc(1,sizeof(*aset)); + aset->p0_PUSCH_AlphaSetId=0; + aset->p0=calloc(1,sizeof(*aset->p0)); + *aset->p0 = 0; + aset->alpha=calloc(1,sizeof(*aset->alpha)); + *aset->alpha=NR_Alpha_alpha1; + ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->p0_AlphaSets->list,aset); + pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList)); + NR_PUSCH_PathlossReferenceRS_t *plrefRS = calloc(1,sizeof(*plrefRS)); + plrefRS->pusch_PathlossReferenceRS_Id=0; + plrefRS->referenceSignal.present = NR_PathlossReferenceRS_Config_PR_ssb_Index; + plrefRS->referenceSignal.choice.ssb_Index = 0; + ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList->list,plrefRS); + pusch_Config->pusch_PowerControl->pathlossReferenceRSToReleaseList = NULL; + pusch_Config->pusch_PowerControl->twoPUSCH_PC_AdjustmentStates = NULL; + pusch_Config->pusch_PowerControl->deltaMCS = NULL; + pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToAddModList = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToAddModList)); + NR_SRI_PUSCH_PowerControl_t *sriPUSCHPC=calloc(1,sizeof(*sriPUSCHPC)); + sriPUSCHPC->sri_PUSCH_PowerControlId=0; + sriPUSCHPC->sri_PUSCH_PathlossReferenceRS_Id=0; + sriPUSCHPC->sri_P0_PUSCH_AlphaSetId=0; + sriPUSCHPC->sri_PUSCH_ClosedLoopIndex=NR_SRI_PUSCH_PowerControl__sri_PUSCH_ClosedLoopIndex_i0; + ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToAddModList->list,sriPUSCHPC); + pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToReleaseList = NULL; + pusch_Config->frequencyHopping=NULL; + pusch_Config->frequencyHoppingOffsetLists=NULL; + pusch_Config->resourceAllocation = NR_PUSCH_Config__resourceAllocation_resourceAllocationType1; + pusch_Config->pusch_TimeDomainAllocationList = NULL; + pusch_Config->pusch_AggregationFactor=NULL; + pusch_Config->mcs_Table=NULL; + pusch_Config->mcs_TableTransformPrecoder=NULL; + pusch_Config->transformPrecoder= NULL; + if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) { + pusch_Config->transformPrecoder=calloc(1,sizeof(*pusch_Config->transformPrecoder)); + *pusch_Config->transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; } + pusch_Config->codebookSubset=calloc(1,sizeof(*pusch_Config->codebookSubset)); + *pusch_Config->codebookSubset = NR_PUSCH_Config__codebookSubset_nonCoherent; + pusch_Config->maxRank=calloc(1,sizeof(*pusch_Config->maxRank)); + *pusch_Config->maxRank= 1; + pusch_Config->rbg_Size=NULL; + pusch_Config->uci_OnPUSCH=NULL; + pusch_Config->tp_pi2BPSK=NULL; + + initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); + initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; + NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); + initialUplinkBWP->srs_Config->choice.setup=srs_Config; + srs_Config->srs_ResourceSetToReleaseList=NULL; + srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); + NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); + srs_resset0->srs_ResourceSetId = 0; + srs_resset0->srs_ResourceIdList=calloc(1,sizeof(*srs_resset0->srs_ResourceIdList)); + NR_SRS_ResourceId_t *srs_resset0_id=calloc(1,sizeof(*srs_resset0_id)); + *srs_resset0_id=0; + ASN_SEQUENCE_ADD(&srs_resset0->srs_ResourceIdList->list,srs_resset0_id); + srs_Config->srs_ResourceToReleaseList=NULL; + srs_resset0->resourceType.present = NR_SRS_ResourceSet__resourceType_PR_aperiodic; + srs_resset0->resourceType.choice.aperiodic = calloc(1,sizeof(*srs_resset0->resourceType.choice.aperiodic)); + srs_resset0->resourceType.choice.aperiodic->aperiodicSRS_ResourceTrigger=1; + srs_resset0->resourceType.choice.aperiodic->csi_RS=NULL; + srs_resset0->resourceType.choice.aperiodic->slotOffset= calloc(1,sizeof(*srs_resset0->resourceType.choice.aperiodic->slotOffset)); + *srs_resset0->resourceType.choice.aperiodic->slotOffset=2; + srs_resset0->resourceType.choice.aperiodic->ext1=NULL; + srs_resset0->usage=NR_SRS_ResourceSet__usage_codebook; + srs_resset0->alpha = calloc(1,sizeof(*srs_resset0->alpha)); + *srs_resset0->alpha = NR_Alpha_alpha1; + srs_resset0->p0=calloc(1,sizeof(*srs_resset0->p0)); + *srs_resset0->p0=-80; + srs_resset0->pathlossReferenceRS=NULL; + srs_resset0->srs_PowerControlAdjustmentStates=NULL; + ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceSetToAddModList->list,srs_resset0); + srs_Config->srs_ResourceToReleaseList=NULL; + srs_Config->srs_ResourceToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceToAddModList)); + NR_SRS_Resource_t *srs_res0=calloc(1,sizeof(*srs_res0)); + srs_res0->srs_ResourceId=0; + srs_res0->nrofSRS_Ports=NR_SRS_Resource__nrofSRS_Ports_port1; + srs_res0->ptrs_PortIndex=NULL; + srs_res0->transmissionComb.present=NR_SRS_Resource__transmissionComb_PR_n2; + srs_res0->transmissionComb.choice.n2=calloc(1,sizeof(*srs_res0->transmissionComb.choice.n2)); + srs_res0->transmissionComb.choice.n2->combOffset_n2=0; + srs_res0->transmissionComb.choice.n2->cyclicShift_n2=0; + srs_res0->resourceMapping.startPosition=2; + srs_res0->resourceMapping.nrofSymbols=NR_SRS_Resource__resourceMapping__nrofSymbols_n1; + srs_res0->resourceMapping.repetitionFactor=NR_SRS_Resource__resourceMapping__repetitionFactor_n1; + srs_res0->freqDomainPosition=0; + srs_res0->freqDomainShift=0; + srs_res0->freqHopping.c_SRS = 0; + srs_res0->freqHopping.b_SRS=0; + srs_res0->freqHopping.b_hop=0; + srs_res0->groupOrSequenceHopping=NR_SRS_Resource__groupOrSequenceHopping_neither; + srs_res0->resourceType.present= NR_SRS_Resource__resourceType_PR_aperiodic; + srs_res0->resourceType.choice.aperiodic=calloc(1,sizeof(*srs_res0->resourceType.choice.aperiodic)); + srs_res0->sequenceId=40; + srs_res0->spatialRelationInfo=calloc(1,sizeof(*srs_res0->spatialRelationInfo)); + srs_res0->spatialRelationInfo->servingCellId=NULL; + srs_res0->spatialRelationInfo->referenceSignal.present=NR_SRS_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; + srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; + ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); + + // configure Scheduling request + // 40 slot period + pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); + NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); + schedulingRequestResourceConfig->schedulingRequestResourceId = 1; + schedulingRequestResourceConfig->schedulingRequestID= 0; + schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); + schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; + // note: make sure that there is no issue here. Later choose the RNTI accordingly. + // Here we would be limited to 3 UEs on this resource (1 1/2 Frames 30 kHz SCS, 5 ms TDD periodicity => slots 7,8,9). + // This should be a temporary resource until the first RRCReconfiguration gives new pucch resources. + // Check for above configuration and exit for now if it is not the case + AssertFatal(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing==NR_SubcarrierSpacing_kHz30, + "SCS != 30kHz\n"); + AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity==NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms5, + "TDD period != 5ms : %ld\n",scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); + + schedulingRequestResourceConfig->periodicityAndOffset->choice.sl40 = 8; + schedulingRequestResourceConfig->resource = calloc(1,sizeof(*schedulingRequestResourceConfig->resource)); + *schedulingRequestResourceConfig->resource = 0; + ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); + + pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); + long *delay[8]; + for (int i=0;i<8;i++) { + delay[i] = calloc(1,sizeof(*delay[i])); + AssertFatal(carrier->minRXTXTIME >=2 && carrier->minRXTXTIME <7, + "check minRXTXTIME %d\n",carrier->minRXTXTIME); + *delay[i] = (i+carrier->minRXTXTIME); + ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); + } + + + + + SpCellConfig->spCellConfigDedicated->initialDownlinkBWP = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->initialDownlinkBWP)); NR_BWP_DownlinkDedicated_t *bwp_Dedicated = SpCellConfig->spCellConfigDedicated->initialDownlinkBWP; @@ -1654,18 +1799,18 @@ void fill_initial_SpCellConfig(rnti_t rnti, // options are 24, 48 or 96 coreset->frequencyDomainResources.buf = calloc(1,6); if (0) { - int curr_bwp = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); - if (curr_bwp < 48) - coreset->frequencyDomainResources.buf[0] = 0xf0; - else - coreset->frequencyDomainResources.buf[0] = 0xff; - if (curr_bwp < 96) - coreset->frequencyDomainResources.buf[1] = 0; - else - coreset->frequencyDomainResources.buf[1] = 0xff; + int curr_bwp = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + if (curr_bwp < 48) + coreset->frequencyDomainResources.buf[0] = 0xf0; + else + coreset->frequencyDomainResources.buf[0] = 0xff; + if (curr_bwp < 96) + coreset->frequencyDomainResources.buf[1] = 0; + else + coreset->frequencyDomainResources.buf[1] = 0xff; } else { - coreset->frequencyDomainResources.buf[0] = 0xf0; - coreset->frequencyDomainResources.buf[1] = 0; + coreset->frequencyDomainResources.buf[0] = 0xf0; + coreset->frequencyDomainResources.buf[1] = 0; } coreset->frequencyDomainResources.buf[2] = 0; coreset->frequencyDomainResources.buf[3] = 0; @@ -1762,48 +1907,26 @@ void fill_initial_SpCellConfig(rnti_t rnti, pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1,sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers)); *pdsch_servingcellconfig->ext1->maxMIMO_Layers = 2; - if(SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList == NULL){ - SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); - } - if(SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList == NULL){ - SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); - } + // ==================== - int n_dl_bwp = 1; - if (servingcellconfigdedicated) { - n_dl_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; - } + /// Downlink BWPs + SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); + NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); + fill_default_downlinkBWP(bwp, 0, servingcellconfigdedicated, scc, carrier); + ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); + SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; + SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id=1; - for (int bwp_loop = 0; bwp_loop < n_dl_bwp; bwp_loop++) { - NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); - fill_default_downlinkBWP(bwp, bwp_loop, servingcellconfigdedicated, scc, carrier); - if (!servingcellconfigdedicated) { - ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); - } - } + /// Uplink BWPs + SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); + NR_BWP_Uplink_t *ubwp = calloc(1, sizeof(*ubwp)); + fill_default_uplinkBWP(ubwp, 0, servingcellconfigdedicated, scc, carrier, uid); + ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list, ubwp); + SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=1; - if (SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id==NULL) { - SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; - } - - int n_ul_bwp = 1; - if (servingcellconfigdedicated) { - n_ul_bwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; - } - - for (int bwp_loop = 0; bwp_loop < n_ul_bwp; bwp_loop++) { - NR_BWP_Uplink_t *ubwp = calloc(1, sizeof(*ubwp)); - fill_default_uplinkBWP(ubwp, bwp_loop, servingcellconfigdedicated, scc, carrier, uid); - if (!servingcellconfigdedicated) { - ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list, ubwp); - } - } - - if (SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id==NULL) { - SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=1; - } xer_fprint(stdout, &asn_DEF_NR_SpCellConfig, (void *)SpCellConfig); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index 99cb1c1712..0f4f4e480b 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -53,10 +53,10 @@ gNBs = # downlinkConfigCommon #frequencyInfoDL # this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68 - absoluteFrequencySSB = 621312; # 641280 + absoluteFrequencySSB = 641280; # 641280 dl_frequencyBand = 78; # this is 3300.60 MHz - dl_absoluteFrequencyPointA = 620040; # 640008 + dl_absoluteFrequencyPointA = 640008; # 640008 #scs-SpecificCarrierList dl_offstToCarrier = 0; # subcarrierSpacing @@ -182,27 +182,6 @@ servingCellConfigDedicated = ({ # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp1_subcarrierSpacing = 1; - # BWP 2 Configuration - dl_bwp-Id_2 = 2; - dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp2_subcarrierSpacing = 1; - - # BWP 3 Configuration - dl_bwp-Id_3 = 3; - dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp3_subcarrierSpacing = 1; - - # BWP 4 Configuration - dl_bwp-Id_4 = 4; - dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp4_subcarrierSpacing = 1; - firstActiveDownlinkBWP-Id = 1; #BWP-Id defaultDownlinkBWP-Id = 1; #BWP-Id @@ -220,27 +199,6 @@ servingCellConfigDedicated = ({ # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp1_subcarrierSpacing = 1; - # BWP 2 Configuration - ul_bwp-Id_2 = 2; - ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp2_subcarrierSpacing = 1; - - # BWP 3 Configuration - ul_bwp-Id_3 = 3; - ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp3_subcarrierSpacing = 1; - - # BWP 4 Configuration - ul_bwp-Id_4 = 4; - ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp4_subcarrierSpacing = 1; - firstActiveUplinkBWP-Id = 1; #BWP-Id } ); From 1bd68ef261595fb7b1dc90a50d54b11023f7e083 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 18 Jan 2022 00:09:45 +0000 Subject: [PATCH 072/233] Fixes when handling with multiple Coresets --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 7 +++++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 16 ++++++++-------- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 4 ++-- openair2/RRC/NR/MESSAGES/asn1_msg.c | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 6098cc9d20..a8c51f71d8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -307,8 +307,11 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, } memset(RC.nrmac[module_idP]->cce_list[0][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 - for(int i_bwp = 0; i_bwp < MAX_NUM_BWP; i_bwp++) - memset(RC.nrmac[module_idP]->cce_list[i_bwp][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid i (0 to coreset1 on initialBWP) + for(int i_bwp = 0; i_bwp < MAX_NUM_BWP; i_bwp++) { + for (int i_coreset = 1; i_coreset < MAX_NUM_CORESET; i_coreset++) { + memset(RC.nrmac[module_idP]->cce_list[i_bwp][i_coreset],0,MAX_NUM_CCE*sizeof(int)); // coresetid i (0 to coreset1 on initialBWP) + } + } NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info; for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 6083b197d8..7a21b526b4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1117,7 +1117,7 @@ void prepare_dci(const NR_CellGroupConfig_t *CellGroup, if (pdsch_Config->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList != NULL) AssertFatal(1==0,"Aperiodic ZP CSI-RS currently not supported\n"); // transmission configuration indication - if (pdcch_Config->controlResourceSetToAddModList->list.array[controlResourceSetId-1]->tci_PresentInDCI != NULL) + if (pdcch_Config->controlResourceSetToAddModList->list.array[0]->tci_PresentInDCI != NULL) AssertFatal(1==0,"TCI in DCI currently not supported\n"); //srs resource set if (CellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->carrierSwitching!=NULL) { @@ -1852,18 +1852,18 @@ int find_nr_UE_id(module_id_t mod_idP, rnti_t rntiP) return -1; } -void set_Y(int Y[3][160], rnti_t rnti) { +void set_Y(int Y[MAX_NUM_CORESET][160], rnti_t rnti) { const int A[3] = {39827, 39829, 39839}; const int D = 65537; - Y[0][0] = (A[0] * rnti) % D; - Y[1][0] = (A[1] * rnti) % D; - Y[2][0] = (A[2] * rnti) % D; + for (int i = 0; i < MAX_NUM_CORESET; i++) { + Y[i][0] = (A[i%3] * rnti) % D; + } for (int s = 1; s < 160; s++) { - Y[0][s] = (A[0] * Y[0][s - 1]) % D; - Y[1][s] = (A[1] * Y[1][s - 1]) % D; - Y[2][s] = (A[2] * Y[2][s - 1]) % D; + for (int i = 0; i < MAX_NUM_CORESET; i++) { + Y[i][s] = (A[i%3] * Y[i][s - 1]) % D; + } } } diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 37efbf4355..c2d34eff76 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -73,7 +73,7 @@ /* Defs */ #define MAX_NUM_BWP 5 -#define MAX_NUM_CORESET 2 +#define MAX_NUM_CORESET 12 #define MAX_NUM_CCE 90 #define MAX_HARQ_ROUNDS 4 /*!\brief Maximum number of random access process */ @@ -654,7 +654,7 @@ typedef struct { rnti_t rnti[MAX_MOBILES_PER_GNB]; NR_CellGroupConfig_t *CellGroup[MAX_MOBILES_PER_GNB]; /// CCE indexing - int Y[MAX_MOBILES_PER_GNB][3][160]; + int Y[MAX_MOBILES_PER_GNB][MAX_NUM_CORESET][160]; int m[MAX_MOBILES_PER_GNB]; int num_pdcch_cand[MAX_MOBILES_PER_GNB][MAX_NUM_CORESET]; // UE selected beam index diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index b0d1d1459e..92130f7483 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1608,7 +1608,7 @@ void fill_initial_SpCellConfig(rnti_t rnti, NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); pucchres0->pucch_ResourceId=0; //pucchres0->startingPRB=0; - pucchres0->startingPRB=(8+rnti) % curr_bwp; + pucchres0->startingPRB=(8+uid) % curr_bwp; LOG_D(NR_RRC, "pucchres0->startPRB %ld rnti %d curr_bwp %d\n", pucchres0->startingPRB, rnti, curr_bwp); pucchres0->intraSlotFrequencyHopping=NULL; pucchres0->secondHopPRB=NULL; From f05c1957512f299f454c3be6603a0f5378b95dfb Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 18 Jan 2022 09:15:05 +0100 Subject: [PATCH 073/233] bugfix for nl index --- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 84ad8aa8af..7fb1a15fac 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -103,7 +103,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, LOG_D(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); uint32_t available_bits; - uint8_t cwd_index, l; + uint8_t cwd_index; uint32_t scrambled_output[NR_MAX_NB_CODEWORDS][NR_MAX_PDSCH_ENCODED_LENGTH>>5]; int8_t Wf[2], Wt[2]; int l_prime[2], delta; @@ -282,7 +282,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, LOG_D(PHY,"Transform Precoding params. u: %d, v: %d, index for dmrsseq: %d\n", u, v, index); - for (l = start_symbol; l < start_symbol + number_of_symbols; l++) { + for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) { if((ul_dmrs_symb_pos >> l) & 0x01) /* In the symbol with DMRS no data would be transmitted CDM groups is 2*/ @@ -333,7 +333,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH RE mapping///////////////////////// /////////// - for (int l=0; l < Nl; l++) { + for (int nl=0; nl < Nl; nl++) { uint8_t k_prime = 0; uint16_t m = 0; @@ -341,13 +341,13 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, printf("NR_ULSCH_UE: Value of CELL ID %d /t, u %d \n", frame_parms->Nid_cell, u); #endif - int dmrs_port = get_dmrs_port(l,pusch_pdu->dmrs_ports); + int dmrs_port = get_dmrs_port(nl,pusch_pdu->dmrs_ports); // DMRS params for this dmrs port get_Wt(Wt, dmrs_port, dmrs_type); get_Wf(Wf, dmrs_port, dmrs_type); delta = get_delta(dmrs_port, dmrs_type); - for (l=start_symbol; ltransformPrecoder == transformPrecoder_enabled) { - ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; + ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; } else { - ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; + ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[dmrs_idx<<1]) >> 15; + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15; } #ifdef DEBUG_PUSCH_MAPPING printf("DMRS: Layer: %d\t, dmrs_idx %d\t l %d \t k %d \t k_prime %d \t n %d \t dmrs: %d %d\n", - l, dmrs_idx, l, k, k_prime, n, ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1], - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1]); + l, dmrs_idx, l, k, k_prime, n, ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1], + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1]); #endif dmrs_idx++; @@ -424,32 +424,32 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, n+=(k_prime)?0:1; } else if (is_ptrs == 1) { - ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; + ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = (beta_ptrs*AMP*mod_ptrs[ptrs_idx<<1]) >> 15; + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; ptrs_idx++; } else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) { if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { - ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[l])[m<<1]; - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[l])[(m<<1) + 1]; + ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[nl])[m<<1]; + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[nl])[(m<<1) + 1]; } else { - ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; + ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = ((int16_t *) ulsch_ue->y)[m<<1]; + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = ((int16_t *) ulsch_ue->y)[(m<<1) + 1]; } #ifdef DEBUG_PUSCH_MAPPING printf("DATA: layer %d\t m %d\t l %d \t k %d \t txdataF: %d %d\n", - l, m, l, k, ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1], - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1]); + nl, m, l, k, ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1], + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1]); #endif m++; } else { - ((int16_t*)tx_precoding[l])[(sample_offsetF)<<1] = 0; - ((int16_t*)tx_precoding[l])[((sample_offsetF)<<1) + 1] = 0; + ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = 0; + ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = 0; } if (++k >= frame_parms->ofdm_symbol_size) { @@ -457,7 +457,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, } } //for (i=0; i< nb_rb*NR_NB_SC_PER_RB; i++) }//for (l=start_symbol; l Date: Tue, 18 Jan 2022 11:53:57 +0100 Subject: [PATCH 074/233] Shutdown nr-softmodem cleanly --- common/utils/ocp_itti/intertask_interface.cpp | 1 + executables/nr-softmodem.c | 1 - openair1/PHY/INIT/nr_init_ru.c | 12 +++++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/common/utils/ocp_itti/intertask_interface.cpp b/common/utils/ocp_itti/intertask_interface.cpp index 44fae9f788..111a423dcb 100644 --- a/common/utils/ocp_itti/intertask_interface.cpp +++ b/common/utils/ocp_itti/intertask_interface.cpp @@ -450,6 +450,7 @@ extern "C" { void itti_wait_tasks_end(void) { shutting_down = false; signal(SIGTERM, catch_sigterm); + signal(SIGINT, catch_sigterm); while (! shutting_down) { sleep(24 * 3600); diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 2b8335167a..9dbc94637c 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -876,7 +876,6 @@ int main( int argc, char **argv ) { nr_phy_free_RU(RC.ru[inst]); } - free_lte_top(); pthread_cond_destroy(&sync_cond); pthread_mutex_destroy(&sync_mutex); pthread_cond_destroy(&nfapi_sync_cond); diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 4024ed1617..73666b48ac 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -193,7 +193,7 @@ void nr_phy_free_RU(RU_t *ru) free_and_zero(ru->common.rxdata_7_5kHz); // free beamforming input buffers (TX) - for (i = 0; i < 15; i++) free_and_zero(ru->common.txdataF[i]); + for (i = 0; i < ru->nb_tx; i++) free_and_zero(ru->common.txdataF[i]); free_and_zero(ru->common.txdataF); // free IFFT input buffers (TX) @@ -209,10 +209,12 @@ void nr_phy_free_RU(RU_t *ru) free_and_zero(ru->prach_rxsigF[j][i]); } } - for (i = 0; i < ru->num_gNB; i++) { - for (p = 0; p < 15; p++) { - for (j=0; jnb_tx; j++) free_and_zero(ru->beam_weights[i][p][j]); - free_and_zero(ru->beam_weights[i][p]); + if (ru->do_precoding == 1) { + for (i = 0; i < ru->num_gNB; i++) { + for (p = 0; p < ru->nb_log_antennas; p++) { + for (j=0; jnb_tx; j++) free_and_zero(ru->beam_weights[i][p][j]); + free_and_zero(ru->beam_weights[i][p]); + } } } } From b608e326efa813de70096553e38220fd85506c04 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 18 Jan 2022 13:00:58 +0000 Subject: [PATCH 075/233] Derive each Coreset or SearchSpace IDs from the BWPId to uniquely identify each one --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 397 +++++++++++----------------- 1 file changed, 158 insertions(+), 239 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 92130f7483..b2af4b47e3 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -983,6 +983,50 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, NR_ServingCellConfigCommon_t *scc, rrc_gNB_carrier_data_t *carrier) { + bwp->bwp_Id = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + + /// BWP common configuration + bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); + bwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; + bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; + bwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); + bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet)); + + int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + + NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); + coreset->controlResourceSetId=(bwp->bwp_Id<<1); // To uniquely identify each Coreset lets derive it from the BWPId + // frequency domain resources depends on BWP size + // options are 24, 48 or 96 + coreset->frequencyDomainResources.buf = calloc(1,6); + if (0) { + if (curr_bwp < 48) + coreset->frequencyDomainResources.buf[0] = 0xf0; + else + coreset->frequencyDomainResources.buf[0] = 0xff; + if (curr_bwp < 96) + coreset->frequencyDomainResources.buf[1] = 0; + else + coreset->frequencyDomainResources.buf[1] = 0xff; + } else { + coreset->frequencyDomainResources.buf[0] = 0xf0; + coreset->frequencyDomainResources.buf[1] = 0; + } + coreset->frequencyDomainResources.buf[2] = 0; + coreset->frequencyDomainResources.buf[3] = 0; + coreset->frequencyDomainResources.buf[4] = 0; + coreset->frequencyDomainResources.buf[5] = 0; + coreset->frequencyDomainResources.size = 6; + coreset->frequencyDomainResources.bits_unused = 3; + coreset->duration=1; + coreset->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; + coreset->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; + + coreset->tci_StatesPDCCH_ToAddList=calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); uint64_t bitmap=0; switch (scc->ssb_PositionsInBurst->present) { case 1 : @@ -999,39 +1043,6 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, default: AssertFatal(1==0,"SSB bitmap size value %d undefined (allowed values 1,2,3) \n", scc->ssb_PositionsInBurst->present); } - - bwp->bwp_Id = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; - bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); - memcpy((void*)&bwp->bwp_Common->genericParameters,&scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters,sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; - bwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; - - bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); - bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero=NULL; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet)); - - int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); - - NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); - coreset->controlResourceSetId=2; - // frequency domain resources depends on BWP size - // options are 24, 48 or 96 - coreset->frequencyDomainResources.buf = calloc(1,6); - coreset->frequencyDomainResources.buf[0] = 0xf0; - coreset->frequencyDomainResources.buf[1] = 0; - coreset->frequencyDomainResources.buf[2] = 0; - coreset->frequencyDomainResources.buf[3] = 0; - coreset->frequencyDomainResources.buf[4] = 0; - coreset->frequencyDomainResources.buf[5] = 0; - coreset->frequencyDomainResources.size = 6; - coreset->frequencyDomainResources.bits_unused = 3; - coreset->duration=1; - coreset->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; - coreset->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; - - coreset->tci_StatesPDCCH_ToAddList=calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); NR_TCI_StateId_t *tci[64]; for (int i=0;i<64;i++) { if ((bitmap>>(63-i))&0x01){ @@ -1045,15 +1056,14 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, coreset->pdcch_DMRS_ScramblingID = NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); - ss->searchSpaceId = 3; + ss->searchSpaceId = (bwp->bwp_Id<<1)-1 + 7; // To uniquely identify each SearchSpace lets derive it from the BWPId ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); - *ss->controlResourceSetId=2; + *ss->controlResourceSetId=coreset->controlResourceSetId; ss->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*ss->monitoringSlotPeriodicityAndOffset)); ss->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; ss->duration=NULL; @@ -1061,7 +1071,7 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ss->monitoringSymbolsWithinSlot->buf = calloc(1,2); // should be '1100 0000 0000 00'B (LSB first!), first two symols in slot, adjust if needed ss->monitoringSymbolsWithinSlot->buf[1] = 0; - ss->monitoringSymbolsWithinSlot->buf[0] = (1<<7) | (1<<6); + ss->monitoringSymbolsWithinSlot->buf[0] = (1<<7); ss->monitoringSymbolsWithinSlot->size = 2; ss->monitoringSymbolsWithinSlot->bits_unused = 2; ss->nrofCandidates = calloc(1,sizeof(*ss->nrofCandidates)); @@ -1085,13 +1095,12 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->pagingSearchSpace=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; - bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); - // copy PDSCH TimeDomainResourceAllocation from InitialBWP + // Copy PDSCH TimeDomainResourceAllocation from InitialBWP NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; for (int i=0;idownlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) { pdschi= calloc(1,sizeof(*pdschi)); @@ -1104,6 +1113,7 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); } + /// BWP dedicated configuration bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; @@ -1111,48 +1121,44 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); - coreset = calloc(1,sizeof(*coreset)); - coreset->controlResourceSetId=3; + NR_ControlResourceSet_t *coreset2 = calloc(1,sizeof(*coreset2)); + coreset2->controlResourceSetId=(bwp->bwp_Id<<1)+1; // To uniquely identify each Coreset lets derive it from the BWPId // frequency domain resources depends on BWP size // options are 24, 48 or 96 - coreset->frequencyDomainResources.buf = calloc(1,6); + coreset2->frequencyDomainResources.buf = calloc(1,6); if (0) { - int curr_bwp = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); if (curr_bwp < 48) - coreset->frequencyDomainResources.buf[0] = 0xf0; + coreset2->frequencyDomainResources.buf[0] = 0xf0; else - coreset->frequencyDomainResources.buf[0] = 0xff; + coreset2->frequencyDomainResources.buf[0] = 0xff; if (curr_bwp < 96) - coreset->frequencyDomainResources.buf[1] = 0; + coreset2->frequencyDomainResources.buf[1] = 0; else - coreset->frequencyDomainResources.buf[1] = 0xff; + coreset2->frequencyDomainResources.buf[1] = 0xff; } else { - coreset->frequencyDomainResources.buf[0] = 0xf0; - coreset->frequencyDomainResources.buf[1] = 0; + coreset2->frequencyDomainResources.buf[0] = 0xf0; + coreset2->frequencyDomainResources.buf[1] = 0; } - coreset->frequencyDomainResources.buf[2] = 0; - coreset->frequencyDomainResources.buf[3] = 0; - coreset->frequencyDomainResources.buf[4] = 0; - coreset->frequencyDomainResources.buf[5] = 0; - coreset->frequencyDomainResources.size = 6; - coreset->frequencyDomainResources.bits_unused = 3; - coreset->duration=1; - coreset->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; - coreset->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; - coreset->tci_StatesPDCCH_ToAddList=NULL; - coreset->tci_StatesPDCCH_ToReleaseList = NULL; - coreset->tci_PresentInDCI = NULL; - coreset->pdcch_DMRS_ScramblingID = NULL; - - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset); + coreset2->frequencyDomainResources.buf[2] = 0; + coreset2->frequencyDomainResources.buf[3] = 0; + coreset2->frequencyDomainResources.buf[4] = 0; + coreset2->frequencyDomainResources.buf[5] = 0; + coreset2->frequencyDomainResources.size = 6; + coreset2->frequencyDomainResources.bits_unused = 3; + coreset2->duration=1; + coreset2->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; + coreset2->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; + coreset2->tci_StatesPDCCH_ToAddList=NULL; + coreset2->tci_StatesPDCCH_ToReleaseList = NULL; + coreset2->tci_PresentInDCI = NULL; + coreset2->pdcch_DMRS_ScramblingID = NULL; + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset2); bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); - NR_SearchSpace_t *ss2 = calloc(1,sizeof(*ss2)); - - ss2->searchSpaceId=4; + ss2->searchSpaceId=(bwp->bwp_Id<<1) + 7; // To uniquely identify each SearchSpace lets derive it from the BWPId ss2->controlResourceSetId=calloc(1,sizeof(*ss2->controlResourceSetId)); - *ss2->controlResourceSetId=3; + *ss2->controlResourceSetId=coreset2->controlResourceSetId; ss2->monitoringSlotPeriodicityAndOffset=calloc(1,sizeof(*ss2->monitoringSlotPeriodicityAndOffset)); ss2->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; ss2->monitoringSlotPeriodicityAndOffset->choice.sl1=(NULL_t)0; @@ -1173,7 +1179,6 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ss2->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_ue_Specific; ss2->searchSpaceType->choice.ue_Specific = calloc(1,sizeof(*ss2->searchSpaceType->choice.ue_Specific)); ss2->searchSpaceType->choice.ue_Specific->dci_Formats=NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_1_And_1_1; - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss2); bwp->bwp_Dedicated->pdsch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); @@ -1183,7 +1188,6 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL; bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition)); @@ -1194,9 +1198,7 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList)); - NR_TCI_State_t *tcic = calloc(1,sizeof(*tcic)); tcic->tci_StateId=0; tcic->qcl_Type1.cell=NULL; @@ -1204,7 +1206,6 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, tcic->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; tcic->qcl_Type1.referenceSignal.choice.ssb = 0; tcic->qcl_Type1.qcl_Type=NR_QCL_Info__qcl_Type_typeD; - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList->list,tcic); } @@ -1215,41 +1216,39 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, rrc_gNB_carrier_data_t *carrier, int uid) { - ubwp->bwp_Id = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + + /// BWP common configuration ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); ubwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; ubwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; ubwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + ubwp->bwp_Common->rach_ConfigCommon = NULL; + ubwp->bwp_Common->pusch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; + ubwp->bwp_Common->pucch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; - int curr_bwp = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); + /// BWP dedicated configuration + ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); - if (!servingcellconfigdedicated) { - servingcellconfigdedicated->uplinkConfig=calloc(1,sizeof(*servingcellconfigdedicated->uplinkConfig)); - } - - NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); - servingcellconfigdedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; - initialUplinkBWP->pucch_Config = calloc(1,sizeof(*initialUplinkBWP->pucch_Config)); - initialUplinkBWP->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; + // PUCCH config + ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); + ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); - initialUplinkBWP->pucch_Config->choice.setup=pucch_Config; + ubwp->bwp_Dedicated->pucch_Config->choice.setup=pucch_Config; pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); pucch_Config->resourceSetToReleaseList = NULL; NR_PUCCH_ResourceSet_t *pucchresset0=calloc(1,sizeof(*pucchresset0)); - pucchresset0->pucch_ResourceSetId = 1; - NR_PUCCH_ResourceId_t *pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); - *pucchresset0id0=1; - ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); + pucchresset0->pucch_ResourceSetId = 0; + NR_PUCCH_ResourceId_t *pucchres0id0=calloc(1,sizeof(*pucchres0id0)); + *pucchres0id0=ubwp->bwp_Id; // To uniquely identify each pucchresource lets derive it from the BWPId + ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchres0id0); pucchresset0->maxPayloadSize=NULL; ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); - pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); pucch_Config->resourceToReleaseList = NULL; - // configure one single PUCCH0 opportunity for initial connection procedure - // one symbol (13) + int curr_bwp = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); - pucchres0->pucch_ResourceId=1; + pucchres0->pucch_ResourceId=*pucchres0id0; pucchres0->startingPRB=(8+uid) % curr_bwp; pucchres0->intraSlotFrequencyHopping=NULL; pucchres0->secondHopPRB=NULL; @@ -1260,14 +1259,28 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pucchres0->format.choice.format0->startingSymbolIndex=13; ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); - // configure Scheduling request - // 40 slot period +/* + pucch_Config->format2=calloc(1,sizeof(*pucch_Config->format2)); + pucch_Config->format2->present=NR_SetupRelease_PUCCH_FormatConfig_PR_setup; + NR_PUCCH_FormatConfig_t *pucchfmt2 = calloc(1,sizeof(*pucchfmt2)); + pucch_Config->format2->choice.setup = pucchfmt2; + pucchfmt2->interslotFrequencyHopping=NULL; + pucchfmt2->additionalDMRS=NULL; + pucchfmt2->maxCodeRate=calloc(1,sizeof(*pucchfmt2->maxCodeRate)); + *pucchfmt2->maxCodeRate=NR_PUCCH_MaxCodeRate_zeroDot35; + pucchfmt2->nrofSlots=NULL; + pucchfmt2->pi2BPSK=NULL; + pucchfmt2->simultaneousHARQ_ACK_CSI=calloc(1,sizeof(*pucchfmt2->simultaneousHARQ_ACK_CSI)); + *pucchfmt2->simultaneousHARQ_ACK_CSI=NR_PUCCH_FormatConfig__simultaneousHARQ_ACK_CSI_true; +*/ + + // PUCCH config - Scheduling request configuration pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); - schedulingRequestResourceConfig->schedulingRequestResourceId = 2; - schedulingRequestResourceConfig->schedulingRequestID= 1; + schedulingRequestResourceConfig->schedulingRequestResourceId = 1; + schedulingRequestResourceConfig->schedulingRequestID = 0; schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); - schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; + schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; // 40 slot period // note: make sure that there is no issue here. Later choose the RNTI accordingly. // Here we would be limited to 3 UEs on this resource (1 1/2 Frames 30 kHz SCS, 5 ms TDD periodicity => slots 7,8,9). // This should be a temporary resource until the first RRCReconfiguration gives new pucch resources. @@ -1276,12 +1289,13 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, "SCS != 30kHz\n"); AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity==NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms5, "TDD period != 5ms : %ld\n",scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); - schedulingRequestResourceConfig->periodicityAndOffset->choice.sl40 = 8; schedulingRequestResourceConfig->resource = calloc(1,sizeof(*schedulingRequestResourceConfig->resource)); - *schedulingRequestResourceConfig->resource = 0; + *schedulingRequestResourceConfig->resource = *pucchres0id0; ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); + pucch_Config->schedulingRequestResourceToReleaseList=NULL; + pucch_Config->multi_CSI_PUCCH_ResourceList=NULL; pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); long *delay[8]; for (int i=0;i<8;i++) { @@ -1290,11 +1304,46 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, *delay[i] = i+carrier->minRXTXTIME; ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); } + pucch_Config->spatialRelationInfoToAddModList = calloc(1,sizeof(*pucch_Config->spatialRelationInfoToAddModList)); + NR_PUCCH_SpatialRelationInfo_t *pucchspatial = calloc(1,sizeof(*pucchspatial)); + pucchspatial->pucch_SpatialRelationInfoId = 1; + pucchspatial->servingCellId = NULL; + if(carrier->do_CSIRS) { + pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; + pucchspatial->referenceSignal.choice.csi_RS_Index = 0; + } + else { + pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_ssb_Index; + pucchspatial->referenceSignal.choice.ssb_Index = 0; + } + pucchspatial->pucch_PathlossReferenceRS_Id = 0; + pucchspatial->p0_PUCCH_Id = 1; + pucchspatial->closedLoopIndex = NR_PUCCH_SpatialRelationInfo__closedLoopIndex_i0; + ASN_SEQUENCE_ADD(&pucch_Config->spatialRelationInfoToAddModList->list,pucchspatial); + pucch_Config->spatialRelationInfoToReleaseList=NULL; + pucch_Config->pucch_PowerControl=calloc(1,sizeof(*pucch_Config->pucch_PowerControl)); + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = 0; + pucch_Config->pucch_PowerControl->p0_Set = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->p0_Set)); + NR_P0_PUCCH_t *p00 = calloc(1,sizeof(*p00)); + p00->p0_PUCCH_Id=1; + p00->p0_PUCCH_Value = 0; + ASN_SEQUENCE_ADD(&pucch_Config->pucch_PowerControl->p0_Set->list,p00); + pucch_Config->pucch_PowerControl->pathlossReferenceRSs = NULL; - initialUplinkBWP->pusch_Config = calloc(1,sizeof(*initialUplinkBWP->pusch_Config)); - initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; + // PUSCH config + ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); + ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; NR_PUSCH_Config_t *pusch_Config = calloc(1,sizeof(*pusch_Config)); - initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; + ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA = NULL; @@ -1361,9 +1410,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, pusch_Config->rbg_Size=NULL; pusch_Config->uci_OnPUSCH=NULL; pusch_Config->tp_pi2BPSK=NULL; - uint8_t transform_precoding = NR_PUSCH_Config__transformPrecoder_disabled; - if (pusch_Config->transformPrecoder == NULL) { if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder != NULL) transform_precoding = NR_PUSCH_Config__transformPrecoder_enabled; @@ -1371,9 +1418,8 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, else { transform_precoding = *pusch_Config->transformPrecoder; } - if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled) { - /* Enable DMRS uplink config for transform precoding enabled */ + // Enable DMRS uplink config for transform precoding enabled NR_DMRS_UplinkConfig->transformPrecodingEnabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingEnabled)); NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity = NULL; NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceGroupHopping = NULL; @@ -1382,10 +1428,12 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, LOG_I(NR_RRC,"Transform precoding enabled\n"); } - initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); - initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; + // SRS config + ubwp->bwp_Dedicated->configuredGrantConfig = NULL; + ubwp->bwp_Dedicated->srs_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->srs_Config)); + ubwp->bwp_Dedicated->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); - initialUplinkBWP->srs_Config->choice.setup=srs_Config; + ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; srs_Config->srs_ResourceSetToReleaseList=NULL; srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); @@ -1425,7 +1473,7 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, srs_res0->resourceMapping.repetitionFactor=NR_SRS_Resource__resourceMapping__repetitionFactor_n1; srs_res0->freqDomainPosition=0; srs_res0->freqDomainShift=0; - srs_res0->freqHopping.c_SRS = 0; + srs_res0->freqHopping.c_SRS=0; srs_res0->freqHopping.b_SRS=0; srs_res0->freqHopping.b_hop=0; srs_res0->groupOrSequenceHopping=NR_SRS_Resource__groupOrSequenceHopping_neither; @@ -1438,135 +1486,6 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); - servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; - - ubwp->bwp_Common->rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon; - ubwp->bwp_Common->pusch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; - ubwp->bwp_Common->pucch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; - - - ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); - ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); - ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; - pucch_Config = calloc(1,sizeof(*pucch_Config)); - ubwp->bwp_Dedicated->pucch_Config->choice.setup=pucch_Config; - pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); - pucch_Config->resourceSetToReleaseList = NULL; - pucchresset0=calloc(1,sizeof(*pucchresset0)); - pucchresset0->pucch_ResourceSetId = 0; - pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); - *pucchresset0id0=0; - ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); - pucchresset0->maxPayloadSize=NULL; - ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); - - pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); - pucch_Config->resourceToReleaseList = NULL; - // configure one single PUCCH0 opportunity for initial connection procedure - // one symbol (13) - pucchres0=calloc(1,sizeof(*pucchres0)); - pucchres0->pucch_ResourceId=0; - pucchres0->startingPRB=(8+uid) % curr_bwp; - pucchres0->intraSlotFrequencyHopping=NULL; - pucchres0->secondHopPRB=NULL; - pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; - pucchres0->format.choice.format0=calloc(1,sizeof(*pucchres0->format.choice.format0)); - pucchres0->format.choice.format0->initialCyclicShift=0; - pucchres0->format.choice.format0->nrofSymbols=1; - pucchres0->format.choice.format0->startingSymbolIndex=13; - ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); - -/* - pucch_Config->format2=calloc(1,sizeof(*pucch_Config->format2)); - pucch_Config->format2->present=NR_SetupRelease_PUCCH_FormatConfig_PR_setup; - NR_PUCCH_FormatConfig_t *pucchfmt2 = calloc(1,sizeof(*pucchfmt2)); - pucch_Config->format2->choice.setup = pucchfmt2; - pucchfmt2->interslotFrequencyHopping=NULL; - pucchfmt2->additionalDMRS=NULL; - pucchfmt2->maxCodeRate=calloc(1,sizeof(*pucchfmt2->maxCodeRate)); - *pucchfmt2->maxCodeRate=NR_PUCCH_MaxCodeRate_zeroDot35; - pucchfmt2->nrofSlots=NULL; - pucchfmt2->pi2BPSK=NULL; - pucchfmt2->simultaneousHARQ_ACK_CSI=calloc(1,sizeof(*pucchfmt2->simultaneousHARQ_ACK_CSI)); - *pucchfmt2->simultaneousHARQ_ACK_CSI=NR_PUCCH_FormatConfig__simultaneousHARQ_ACK_CSI_true; -*/ - - // configure Scheduling request - // 40 slot period - pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); - schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); - schedulingRequestResourceConfig->schedulingRequestResourceId = 1; - schedulingRequestResourceConfig->schedulingRequestID = 0; - schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); - schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; - // note: make sure that there is no issue here. Later choose the RNTI accordingly. - // Here we would be limited to 3 UEs on this resource (1 1/2 Frames 30 kHz SCS, 5 ms TDD periodicity => slots 7,8,9). - // This should be a temporary resource until the first RRCReconfiguration gives new pucch resources. - // Check for above configuration and exit for now if it is not the case - AssertFatal(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing==NR_SubcarrierSpacing_kHz30, - "SCS != 30kHz\n"); - AssertFatal(scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity==NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms5, - "TDD period != 5ms : %ld\n",scc->tdd_UL_DL_ConfigurationCommon->pattern1.dl_UL_TransmissionPeriodicity); - - schedulingRequestResourceConfig->periodicityAndOffset->choice.sl40 = 8; - schedulingRequestResourceConfig->resource = calloc(1,sizeof(*schedulingRequestResourceConfig->resource)); - *schedulingRequestResourceConfig->resource = 0; - ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); - - pucch_Config->schedulingRequestResourceToReleaseList=NULL; - pucch_Config->multi_CSI_PUCCH_ResourceList=NULL; - pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); - for (int i=0;i<8;i++) { - delay[i] = calloc(1,sizeof(*delay[i])); - AssertFatal(carrier->minRXTXTIME >=2 && carrier->minRXTXTIME <7, "check minRXTXTIME %d\n",carrier->minRXTXTIME); - *delay[i] = i+carrier->minRXTXTIME; - ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); - } - pucch_Config->spatialRelationInfoToAddModList = calloc(1,sizeof(*pucch_Config->spatialRelationInfoToAddModList)); - NR_PUCCH_SpatialRelationInfo_t *pucchspatial = calloc(1,sizeof(*pucchspatial)); - pucchspatial->pucch_SpatialRelationInfoId = 1; - pucchspatial->servingCellId = NULL; - if(carrier->do_CSIRS) { - pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; - pucchspatial->referenceSignal.choice.csi_RS_Index = 0; - } - else { - pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_ssb_Index; - pucchspatial->referenceSignal.choice.ssb_Index = 0; - } - pucchspatial->pucch_PathlossReferenceRS_Id = 0; - pucchspatial->p0_PUCCH_Id = 1; - pucchspatial->closedLoopIndex = NR_PUCCH_SpatialRelationInfo__closedLoopIndex_i0; - ASN_SEQUENCE_ADD(&pucch_Config->spatialRelationInfoToAddModList->list,pucchspatial); - pucch_Config->spatialRelationInfoToReleaseList=NULL; - pucch_Config->pucch_PowerControl=calloc(1,sizeof(*pucch_Config->pucch_PowerControl)); - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = 0; - pucch_Config->pucch_PowerControl->p0_Set = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->p0_Set)); - NR_P0_PUCCH_t *p00 = calloc(1,sizeof(*p00)); - p00->p0_PUCCH_Id=1; - p00->p0_PUCCH_Value = 0; - ASN_SEQUENCE_ADD(&pucch_Config->pucch_PowerControl->p0_Set->list,p00); - pucch_Config->pucch_PowerControl->pathlossReferenceRSs = NULL; - - // copy pusch_Config from dedicated initialBWP - ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); - ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; - - ubwp->bwp_Dedicated->configuredGrantConfig = NULL; - ubwp->bwp_Dedicated->srs_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->srs_Config)); - ubwp->bwp_Dedicated->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; - ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; - ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; } From a2244ab32ddd25b97bdaadded7ad2bca4874ad1b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 18 Jan 2022 14:21:58 +0100 Subject: [PATCH 076/233] Stop RU threads at end of nr-softmodem --- executables/nr-softmodem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 9dbc94637c..60efba8464 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -873,6 +873,7 @@ int main( int argc, char **argv ) { } for (int inst = 0; inst < NB_RU; inst++) { + kill_NR_RU_proc(inst); nr_phy_free_RU(RC.ru[inst]); } From 18fd38fa082d878bcee2b2aa6afee021583c5cda Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 18 Jan 2022 14:23:54 +0100 Subject: [PATCH 077/233] Don't call trx_end_func() at nr-softmodem, is done in ru_thread() --- executables/nr-softmodem.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 60efba8464..e01c3078c5 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -886,9 +886,6 @@ int main( int argc, char **argv ) { // *** Handle per CC_id openair0 for(ru_id=0; ru_idrfdevice.trx_end_func) - RC.ru[ru_id]->rfdevice.trx_end_func(&RC.ru[ru_id]->rfdevice); - if (RC.ru[ru_id]->ifdevice.trx_end_func) RC.ru[ru_id]->ifdevice.trx_end_func(&RC.ru[ru_id]->ifdevice); } From 2a610cfef423194cd239d3a1314ede4cd167ba32 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 18 Jan 2022 13:49:22 +0000 Subject: [PATCH 078/233] Handle config file without servingcellconfigdedicated --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 5 +- openair2/RRC/NR/MESSAGES/asn1_msg.c | 58 +++++++++++-------- ...gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 50 ++++++++++++++-- 3 files changed, 81 insertions(+), 32 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index a8c51f71d8..236530b184 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -306,10 +306,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing); } - memset(RC.nrmac[module_idP]->cce_list[0][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0 for(int i_bwp = 0; i_bwp < MAX_NUM_BWP; i_bwp++) { - for (int i_coreset = 1; i_coreset < MAX_NUM_CORESET; i_coreset++) { - memset(RC.nrmac[module_idP]->cce_list[i_bwp][i_coreset],0,MAX_NUM_CCE*sizeof(int)); // coresetid i (0 to coreset1 on initialBWP) + for (int i_coreset = 0; i_coreset < MAX_NUM_CORESET; i_coreset++) { + memset(RC.nrmac[module_idP]->cce_list[i_bwp][i_coreset],0,MAX_NUM_CCE*sizeof(int)); } } diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index b2af4b47e3..ee224dcb70 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1575,7 +1575,7 @@ void fill_initial_SpCellConfig(rnti_t rnti, pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList)); NR_PUSCH_PathlossReferenceRS_t *plrefRS = calloc(1,sizeof(*plrefRS)); plrefRS->pusch_PathlossReferenceRS_Id=0; - plrefRS->referenceSignal.present = NR_PathlossReferenceRS_Config_PR_ssb_Index; + plrefRS->referenceSignal.present = NR_PUSCH_PathlossReferenceRS__referenceSignal_PR_ssb_Index; plrefRS->referenceSignal.choice.ssb_Index = 0; ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList->list,plrefRS); pusch_Config->pusch_PowerControl->pathlossReferenceRSToReleaseList = NULL; @@ -1697,11 +1697,6 @@ void fill_initial_SpCellConfig(rnti_t rnti, ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); } - - - - - SpCellConfig->spCellConfigDedicated->initialDownlinkBWP = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->initialDownlinkBWP)); NR_BWP_DownlinkDedicated_t *bwp_Dedicated = SpCellConfig->spCellConfigDedicated->initialDownlinkBWP; bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp_Dedicated->pdcch_Config)); @@ -1826,26 +1821,39 @@ void fill_initial_SpCellConfig(rnti_t rnti, pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1,sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers)); *pdsch_servingcellconfig->ext1->maxMIMO_Layers = 2; - // ==================== - - /// Downlink BWPs - SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); - NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); - fill_default_downlinkBWP(bwp, 0, servingcellconfigdedicated, scc, carrier); - ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); - SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; - SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id=1; - - /// Uplink BWPs - SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); - NR_BWP_Uplink_t *ubwp = calloc(1, sizeof(*ubwp)); - fill_default_uplinkBWP(ubwp, 0, servingcellconfigdedicated, scc, carrier, uid); - ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list, ubwp); - SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id=1; + // Downlink BWPs + int n_dl_bwp = 0; + if (servingcellconfigdedicated && servingcellconfigdedicated->downlinkBWP_ToAddModList) { + n_dl_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; + } + if(n_dl_bwp>0){ + SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); + for (int bwp_loop = 0; bwp_loop < n_dl_bwp; bwp_loop++) { + NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); + fill_default_downlinkBWP(bwp, bwp_loop, servingcellconfigdedicated, scc, carrier); + ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); + SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = servingcellconfigdedicated->firstActiveDownlinkBWP_Id ? *servingcellconfigdedicated->firstActiveDownlinkBWP_Id : 1; + SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = servingcellconfigdedicated->defaultDownlinkBWP_Id ? *servingcellconfigdedicated->defaultDownlinkBWP_Id : 1; + } + } + // Uplink BWPs + int n_ul_bwp = 0; + if (servingcellconfigdedicated && servingcellconfigdedicated->uplinkConfig && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList) { + n_ul_bwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; + } + if(n_ul_bwp>0) { + SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); + for (int bwp_loop = 0; bwp_loop < n_ul_bwp; bwp_loop++) { + NR_BWP_Uplink_t *ubwp = calloc(1, sizeof(*ubwp)); + fill_default_uplinkBWP(ubwp, bwp_loop, servingcellconfigdedicated, scc, carrier, uid); + ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list, ubwp); + SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id ? *servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id : 1; + } + } xer_fprint(stdout, &asn_DEF_NR_SpCellConfig, (void *)SpCellConfig); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index 0f4f4e480b..1a52f2add4 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -39,7 +39,7 @@ gNBs = pdcch_ConfigSIB1 = ( { - controlResourceSetZero = 11; + controlResourceSetZero = 12; searchSpaceZero = 0; } ); @@ -53,10 +53,10 @@ gNBs = # downlinkConfigCommon #frequencyInfoDL # this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68 - absoluteFrequencySSB = 641280; # 641280 + absoluteFrequencySSB = 621312; # 641280 dl_frequencyBand = 78; # this is 3300.60 MHz - dl_absoluteFrequencyPointA = 640008; # 640008 + dl_absoluteFrequencyPointA = 620040; # 640008 #scs-SpecificCarrierList dl_offstToCarrier = 0; # subcarrierSpacing @@ -71,7 +71,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialDLBWPsubcarrierSpacing = 1; #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPcontrolResourceSetZero = 12; initialDLBWPsearchSpaceZero = 0; #uplinkConfigCommon @@ -182,6 +182,27 @@ servingCellConfigDedicated = ({ # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp1_subcarrierSpacing = 1; + # BWP 2 Configuration + dl_bwp-Id_2 = 2; + dl_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + dl_bwp-Id_3 = 3; + dl_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + dl_bwp-Id_4 = 4; + dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp4_subcarrierSpacing = 1; + firstActiveDownlinkBWP-Id = 1; #BWP-Id defaultDownlinkBWP-Id = 1; #BWP-Id @@ -199,6 +220,27 @@ servingCellConfigDedicated = ({ # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp1_subcarrierSpacing = 1; + # BWP 2 Configuration + ul_bwp-Id_2 = 2; + ul_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + ul_bwp-Id_3 = 3; + ul_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp3_subcarrierSpacing = 1; + + # BWP 4 Configuration + ul_bwp-Id_4 = 4; + ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp4_subcarrierSpacing = 1; + firstActiveUplinkBWP-Id = 1; #BWP-Id } ); From 10ef3751d7ee24a0eb9ffbc1ab7ac7f87dcbba71 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 18 Jan 2022 15:23:01 +0000 Subject: [PATCH 079/233] Fix rbStart in pf_ul --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index b61c68c813..3e9b99f6e1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1230,11 +1230,11 @@ void pf_ul(module_id_t module_id, NR_CellGroupConfig_t *cg = UE_info->CellGroup[UE_id]; NR_BWP_UplinkDedicated_t *ubwpd= cg ? cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP:NULL; NR_BWP_t *genericParameters = sched_ctrl->active_ubwp ? &sched_ctrl->active_ubwp->bwp_Common->genericParameters : &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; - int rbStart = sched_ctrl->active_ubwp ? NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE) : 0; const uint16_t bwpSize = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; NR_pusch_semi_static_t *ps = &sched_ctrl->pusch_semi_static; + int rbStart = 0; while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++; sched_pusch->rbStart = rbStart; uint16_t max_rbSize = 1; From e7d11fab4b0566230bf2772479fa2065c08c9738 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 2 Nov 2021 17:24:10 +0000 Subject: [PATCH 080/233] Fix segmentation fault when exporting packets to wireshark with high MCS --- openair2/UTIL/OPT/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/UTIL/OPT/probe.c b/openair2/UTIL/OPT/probe.c index e64f7724c6..8731eb294a 100644 --- a/openair2/UTIL/OPT/probe.c +++ b/openair2/UTIL/OPT/probe.c @@ -408,7 +408,7 @@ static void SendFrameNR(guint8 radioType, guint8 direction, guint8 rntiType, guint8 isPredefinedData, guint8 retx, guint8 crcStatus, guint8 oob_event, guint8 oob_event_value, uint8_t *pdu_buffer, unsigned int pdu_buffer_size) { - unsigned char frameBuffer[9000]; + unsigned char frameBuffer[12000]; unsigned int frameOffset; ssize_t bytesSent; frameOffset = 0; From 754a39def5fdda90be784d0fb52637e4360136a4 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 18 Jan 2022 15:54:32 +0000 Subject: [PATCH 081/233] Dedicated BWPs working with COTS UE in SA mode --- .../gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index 1a52f2add4..543533a46e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -196,13 +196,6 @@ servingCellConfigDedicated = ({ # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp3_subcarrierSpacing = 1; - # BWP 4 Configuration - dl_bwp-Id_4 = 4; - dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp4_subcarrierSpacing = 1; - firstActiveDownlinkBWP-Id = 1; #BWP-Id defaultDownlinkBWP-Id = 1; #BWP-Id @@ -234,13 +227,6 @@ servingCellConfigDedicated = ({ # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp3_subcarrierSpacing = 1; - # BWP 4 Configuration - ul_bwp-Id_4 = 4; - ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp4_subcarrierSpacing = 1; - firstActiveUplinkBWP-Id = 1; #BWP-Id } ); From 6f7b434c4965f37aadc01c5ff990b7f22a0bfa03 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 18 Jan 2022 17:49:24 +0000 Subject: [PATCH 082/233] Update config files and fix bwp selection --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 18 +++++++-------- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 18 ++++++++++----- openair2/RRC/NR/MESSAGES/asn1_msg.c | 3 +-- .../gnb.sa.band41.fr1.106PRB.usrpb210.conf | 22 ++++--------------- .../gnb.sa.band78.fr1.106PRB.usrpb210.conf | 22 ++++--------------- 5 files changed, 30 insertions(+), 53 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 49aff8d58b..2235bcf224 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -534,19 +534,19 @@ void configure_ss_coreset(NR_UE_MAC_INST_t *mac, NR_BWP_Id_t dl_bwp_id) { - NR_BWP_DownlinkCommon_t *bwp_Common; - if (dl_bwp_id>0) - bwp_Common = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Common; - else { - if (mac->scc_SIB) - bwp_Common = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; - else - bwp_Common = mac->scc->downlinkConfigCommon->initialDownlinkBWP; + NR_BWP_DownlinkCommon_t *bwp_Common = NULL; + if(dl_bwp_id > 0 && scd->downlinkBWP_ToAddModList) { + bwp_Common = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Common; + } else if(mac->scc) { + bwp_Common = mac->scc->downlinkConfigCommon->initialDownlinkBWP; + } else if(mac->scc_SIB) { + bwp_Common = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; } + AssertFatal(bwp_Common != NULL, "bwp_Common is null\n"); + NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; AssertFatal(pdcch_ConfigCommon != NULL, "pdcch_ConfigCommon is null\n"); - AssertFatal(pdcch_ConfigCommon->choice.setup->ra_SearchSpace != NULL, "ra_SearchSpace must be available in DL BWP\n"); // configuring eventual common coreset NR_ControlResourceSet_t *coreset = pdcch_ConfigCommon->choice.setup->commonControlResourceSet; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 2c4fad5820..95f8a091f9 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -196,8 +196,8 @@ void get_bwp_info(NR_UE_MAC_INST_t *mac, (int)dl_bwp_id-1); *bwpd = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated; if (mac->DLbwp[dl_bwp_id-1]->bwp_Common) *bwpc = mac->DLbwp[dl_bwp_id-1]->bwp_Common; - else if (mac->scc_SIB) *bwpc = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; else if (mac->scc) *bwpc = mac->scc->downlinkConfigCommon->initialDownlinkBWP; + else if (mac->scc_SIB) *bwpc = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; AssertFatal(*bwpc!=NULL,"bwpc shouldn't be null\n"); } else { if (mac->cg && @@ -205,8 +205,8 @@ void get_bwp_info(NR_UE_MAC_INST_t *mac, mac->cg->spCellConfig->spCellConfigDedicated && mac->cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP) *bwpd = mac->cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP; - if (mac->scc_SIB) *bwpc = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; - else if (mac->scc) *bwpc = mac->scc->downlinkConfigCommon->initialDownlinkBWP; + if (mac->scc) *bwpc = mac->scc->downlinkConfigCommon->initialDownlinkBWP; + else if (mac->scc_SIB) *bwpc = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; AssertFatal(*bwpc!=NULL,"bwpc shouldn't be null\n"); } @@ -215,8 +215,8 @@ void get_bwp_info(NR_UE_MAC_INST_t *mac, ul_bwp_id-1); *ubwpd = mac->ULbwp[ul_bwp_id-1]->bwp_Dedicated; if (mac->ULbwp[ul_bwp_id-1]->bwp_Common) *ubwpc = mac->ULbwp[ul_bwp_id-1]->bwp_Common; - else if (mac->scc_SIB) *ubwpc = &mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP; else if (mac->scc) *ubwpc = mac->scc->uplinkConfigCommon->initialUplinkBWP; + else if (mac->scc_SIB) *ubwpc = &mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP; AssertFatal(*bwpc!=NULL,"bwpc shouldn't be null\n"); } @@ -227,8 +227,8 @@ void get_bwp_info(NR_UE_MAC_INST_t *mac, mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig && mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP) *ubwpd = mac->cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP; - if (mac->scc_SIB) *ubwpc = &mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP; - else if (mac->scc) *ubwpc = mac->scc->uplinkConfigCommon->initialUplinkBWP; + if (mac->scc) *ubwpc = mac->scc->uplinkConfigCommon->initialUplinkBWP; + else if (mac->scc_SIB) *ubwpc = &mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP; AssertFatal(*ubwpc!=NULL,"ubwpc shouldn't be null\n"); } } @@ -630,6 +630,7 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr uint16_t n_RB_DLBWP; if (dl_bwp_id>0 && mac->DLbwp[dl_bwp_id-1]) n_RB_DLBWP = NRRIV2BW(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + else if (mac->scc) n_RB_DLBWP = NRRIV2BW(mac->scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth,MAX_BWP_SIZE); else if (mac->scc_SIB) n_RB_DLBWP = NRRIV2BW(mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters.locationAndBandwidth,MAX_BWP_SIZE); else n_RB_DLBWP = mac->type0_PDCCH_CSS_config.num_rbs; @@ -834,6 +835,11 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->SubcarrierSpacing = mac->DLbwp[dl_bwp_id-1]->bwp_Common->genericParameters.subcarrierSpacing; pdsch_config = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated->pdsch_Config->choice.setup; + } else if (mac->scc) { + dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + dlsch_config_pdu_1_0->SubcarrierSpacing = mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing; + pdsch_config = NULL; } else if (mac->scc_SIB) { dlsch_config_pdu_1_0->BWPSize = NRRIV2BW(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); dlsch_config_pdu_1_0->BWPStart = NRRIV2PRBOFFSET(mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters.locationAndBandwidth, MAX_BWP_SIZE); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index ee224dcb70..5e3f6c19f2 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1526,7 +1526,6 @@ void fill_initial_SpCellConfig(rnti_t rnti, // one symbol (13) NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); pucchres0->pucch_ResourceId=0; - //pucchres0->startingPRB=0; pucchres0->startingPRB=(8+uid) % curr_bwp; LOG_D(NR_RRC, "pucchres0->startPRB %ld rnti %d curr_bwp %d\n", pucchres0->startingPRB, rnti, curr_bwp); pucchres0->intraSlotFrequencyHopping=NULL; @@ -1670,7 +1669,7 @@ void fill_initial_SpCellConfig(rnti_t rnti, pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); schedulingRequestResourceConfig->schedulingRequestResourceId = 1; - schedulingRequestResourceConfig->schedulingRequestID= 0; + schedulingRequestResourceConfig->schedulingRequestID = 0; schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl40; // note: make sure that there is no issue here. Later choose the RNTI accordingly. diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf index afecad980c..6ae7ecd6ec 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf @@ -185,25 +185,18 @@ servingCellConfigDedicated = ({ # BWP 2 Configuration dl_bwp-Id_2 = 2; - dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + dl_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp2_subcarrierSpacing = 1; # BWP 3 Configuration dl_bwp-Id_3 = 3; - dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + dl_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp3_subcarrierSpacing = 1; - # BWP 4 Configuration - dl_bwp-Id_4 = 4; - dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp4_subcarrierSpacing = 1; - firstActiveDownlinkBWP-Id = 1; #BWP-Id defaultDownlinkBWP-Id = 1; #BWP-Id @@ -223,25 +216,18 @@ servingCellConfigDedicated = ({ # BWP 2 Configuration ul_bwp-Id_2 = 2; - ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + ul_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp2_subcarrierSpacing = 1; # BWP 3 Configuration ul_bwp-Id_3 = 3; - ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + ul_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp3_subcarrierSpacing = 1; - # BWP 4 Configuration - ul_bwp-Id_4 = 4; - ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp4_subcarrierSpacing = 1; - firstActiveUplinkBWP-Id = 1; #BWP-Id } ); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index 8ab3096a8b..db4841c65a 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -185,25 +185,18 @@ servingCellConfigDedicated = ({ # BWP 2 Configuration dl_bwp-Id_2 = 2; - dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + dl_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp2_subcarrierSpacing = 1; # BWP 3 Configuration dl_bwp-Id_3 = 3; - dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + dl_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp3_subcarrierSpacing = 1; - # BWP 4 Configuration - dl_bwp-Id_4 = 4; - dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp4_subcarrierSpacing = 1; - firstActiveDownlinkBWP-Id = 1; #BWP-Id defaultDownlinkBWP-Id = 1; #BWP-Id @@ -223,25 +216,18 @@ servingCellConfigDedicated = ({ # BWP 2 Configuration ul_bwp-Id_2 = 2; - ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + ul_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp2_subcarrierSpacing = 1; # BWP 3 Configuration ul_bwp-Id_3 = 3; - ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + ul_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp3_subcarrierSpacing = 1; - # BWP 4 Configuration - ul_bwp-Id_4 = 4; - ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp4_subcarrierSpacing = 1; - firstActiveUplinkBWP-Id = 1; #BWP-Id } ); From a6bf3f4cb29518a1a1a2c668a290975e5b4d5fad Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 19 Jan 2022 00:29:37 +0000 Subject: [PATCH 083/233] Fix search space loop in ue_dci_configuration --- openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 3cca55b7c7..8839a71131 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -279,7 +279,12 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl // loop over all available SS for bwp_id if (bwpd) { - for (ss_id = 1; ss_id <= FAPI_NR_MAX_SS && mac->SSpace[bwp_id-1][ss_id-1] != NULL; ss_id++){ + for (ss_id = 1; ss_id <= FAPI_NR_MAX_SS; ss_id++){ + + if(mac->SSpace[bwp_id-1][ss_id-1]==NULL) { + continue; + } + LOG_D(NR_MAC, "[DCI_CONFIG] ss_id %d\n",ss_id); NR_SearchSpace_t *ss = mac->SSpace[bwp_id-1][ss_id-1]; AssertFatal(ss_id == ss->searchSpaceId,"SS IDs don't correspond\n"); From 3377f91b627e3467d5fbec6c6e4c3daad9375650 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 19 Jan 2022 11:48:51 +0100 Subject: [PATCH 084/233] fix free pusch_vars->rho --- openair1/PHY/INIT/nr_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index d3f1b02826..4b3a896893 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -363,6 +363,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) for (int i = 0; i < fp->nb_antennas_rx; i++) { free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext[i]); free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext2[i]); + free_and_zero(pusch_vars[ULSCH_id]->rho[i]); } for (int i = 0; i < 4*fp->nb_antennas_rx; i++) { free_and_zero(pusch_vars[ULSCH_id]->ul_ch_estimates[i]); @@ -376,7 +377,6 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_and_zero(pusch_vars[ULSCH_id]->ul_ch_magb0[i]); free_and_zero(pusch_vars[ULSCH_id]->ul_ch_mag[i]); free_and_zero(pusch_vars[ULSCH_id]->ul_ch_magb[i]); - free_and_zero(pusch_vars[ULSCH_id]->rho[i]); } free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext); free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext2); From 9daebabe60adf3006eec5dfad0bfe274a172d315 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 19 Jan 2022 11:56:41 +0000 Subject: [PATCH 085/233] Handling SIB1 decode at UE when the searchSpaceSIB1 in BWP is NULL --- .../NR_MAC_UE/nr_ue_dci_configuration.c | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 8839a71131..d836f6d363 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -425,34 +425,18 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl AssertFatal(1==0,"Handle DCI searching when CellGroup without dedicated BWP\n"); } // Search space 0, CORESET ID 0 - - NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; - - if (pdcch_ConfigCommon && - pdcch_ConfigCommon->choice.setup->searchSpaceSIB1 && - !get_softmodem_params()->nsa) { - - NR_SearchSpace_t *ss0 = mac->search_space_zero; - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; - - if (ss0->searchSpaceId == *pdcch_ConfigCommon->choice.setup->searchSpaceSIB1){ - if( (frame%2 == mac->type0_PDCCH_CSS_config.sfn_c) && (slot == mac->type0_PDCCH_CSS_config.n_0) ){ - rel15->num_dci_options = 1; - rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; - config_dci_pdu(mac, rel15, dl_config, NR_RNTI_SI, -1); - fill_dci_search_candidates(ss0, rel15); - } - } - } - else if (!get_softmodem_params()->nsa) { // use coreset0/ss0 + if (!get_softmodem_params()->nsa) { NR_SearchSpace_t *ss0 = mac->search_space_zero; if(ss0) { - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[0].dci_config_pdu.dci_config_rel15; + fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; rel15->num_dci_options = 1; rel15->dci_format_options[0] = NR_DL_DCI_FORMAT_1_0; - config_dci_pdu(mac, rel15, dl_config, NR_RNTI_C , -1); + config_dci_pdu(mac, + rel15, + dl_config, + ((frame%2==mac->type0_PDCCH_CSS_config.sfn_c)&&(slot==mac->type0_PDCCH_CSS_config.n_0)) ? NR_RNTI_SI : NR_RNTI_C, + -1); fill_dci_search_candidates(ss0, rel15); - dl_config->number_pdus = 1; } } } From 4bdc6ac59f1838da60846fc42adf030cf2de99d8 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 19 Jan 2022 18:11:43 +0100 Subject: [PATCH 086/233] minor changes --- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 18 ++++-------- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 29 +++++++------------ openair1/SIMULATION/NR_PHY/ulsim.c | 1 - 3 files changed, 15 insertions(+), 33 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 5286fe8780..3f6540a94a 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -298,16 +298,7 @@ void nr_processULSegment(void* arg) { stop_meas(&phy_vars_gNB->ulsch_deinterleaving_stats); - /*LOG_D(PHY,"HARQ_PID %d Rate Matching Segment %d (coded bits %d,unpunctured/repeated bits %d, TBS %d, mod_order %d, nb_rb %d, Nl %d, rv %d, round %d)...\n", - harq_pid,r, G, - Kr*3, - ulsch_harq->TBS, - Qm, - nb_rb, - n_layers, - pusch_pdu->pusch_data.rv_index, - ulsch_harq->round);*/ - ////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// @@ -387,13 +378,13 @@ void nr_processULSegment(void* arg) { if (check_crc((uint8_t*)llrProcBuf,length_dec,ulsch_harq->F,crc_type)) { #ifdef PRINT_CRC_CHECK - LOG_I(PHY, "Segment %d CRC OK, iterations %d/%d\n",r,no_iteration_ldpc,max_ldpc_iterations); + LOG_I(PHY,"Segment %d CRC OK, iterations %d/%d\n",r,no_iteration_ldpc,max_ldpc_iterations); #endif rdata->decodeIterations = no_iteration_ldpc; if (rdata->decodeIterations > p_decoderParms->numMaxIter) rdata->decodeIterations--; } else { #ifdef PRINT_CRC_CHECK - LOG_I(PHY, "CRC NOK\n"); + LOG_I(PHY,"CRC NOK\n"); #endif rdata->decodeIterations = max_ldpc_iterations + 1; } @@ -480,7 +471,8 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, A = (harq_process->TBS)<<3; - LOG_D(PHY,"ULSCH Decoding, harq_pid %d TBS %d G %d mcs %d Nl %d nb_rb %d, Qm %d, n_layers %d, Coderate %d\n",harq_pid,A,G, mcs, n_layers, nb_rb, Qm, n_layers, R); + LOG_D(PHY,"ULSCH Decoding, harq_pid %d TBS %d G %d mcs %d Nl %d nb_rb %d, Qm %d, Coderate %d RV %d round %d\n", + harq_pid, A, G, mcs, n_layers, nb_rb, Qm, R, pusch_pdu->pusch_data.rv_index, harq_process->round); if (R<1024) Coderate = (float) R /(float) 1024; diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index dad8d70465..26a7c953c9 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -347,14 +347,12 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, if (is_dmrs_symbol == 0) { // //rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); - if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) - { + if (start_re + nb_re_pusch < frame_parms->ofdm_symbol_size) { memcpy1((void*)rxF_ext, (void*)&rxF[start_re*2], nb_re_pusch*sizeof(int32_t)); } - else - { + else { int neg_length = frame_parms->ofdm_symbol_size-start_re; int pos_length = nb_re_pusch-neg_length; memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); @@ -373,8 +371,7 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); } } - else - { + else { #ifdef SUPPORT_PMI_MATRIC for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) #else @@ -390,26 +387,22 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, ul_ch0_ext_index = 0; ul_ch0_index = 0; - for (re = 0; re < nb_re_pusch; re++) - { + for (re = 0; re < nb_re_pusch; re++) { uint16_t k= start_re+re; is_data_re = allowed_xlsch_re_in_dmrs_symbol(k, start_re, frame_parms->ofdm_symbol_size, pusch_pdu->num_dmrs_cdm_grps_no_data, pusch_pdu->dmrs_config_type); - if (++k >= frame_parms->ofdm_symbol_size) - { + if (++k >= frame_parms->ofdm_symbol_size) { k -= frame_parms->ofdm_symbol_size; } #ifdef DEBUG_RB_EXT - printf("re = %d, kprime %d, n %d, is_dmrs_symbol = %d, symbol = %d\n", re, k_prime, n, is_dmrs_symbol, symbol); + printf("re = %d, is_dmrs_symbol = %d, symbol = %d\n", re, is_dmrs_symbol, symbol); #endif /* save only data and respective channel estimates */ - if (is_data_re == 1) - { - if (aatx == aarx) - { + if (is_data_re == 1) { + if (aatx == aarx) { rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); rxF_ext_index +=2; @@ -1206,8 +1199,7 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, if (n_rx > 1) { #if defined(__x86_64__) || defined(__i386__) - for (int aatx=0; aatxnb_antennas_rx][(symbol*(nb_re + off))]; @@ -2002,8 +1994,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] = 0; gNB->pusch_vars[ulsch_id]->ulsch_noise_power[aarx] = 0; } - for (aatx = 0; aatx < rel15_ul->nrOfLayers; aatx++) - { + for (aatx = 0; aatx < rel15_ul->nrOfLayers; aatx++) { gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] += signal_energy_nodc( &gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aatx*gNB->frame_parms.nb_antennas_rx+aarx][symbol * frame_parms->ofdm_symbol_size], rel15_ul->rb_size * 12); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 32cb2d6d48..8bf43ef0b1 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -243,7 +243,6 @@ nrUE_params_t *get_nrUE_params(void) { // needed for some functions uint16_t n_rnti = 0x1234; openair0_config_t openair0_cfg[MAX_CARDS]; -//const uint8_t nr_rv_round_map[4] = {0, 2, 1, 3}; channel_desc_t *UE2gNB[NUMBER_OF_UE_MAX][NUMBER_OF_gNB_MAX]; From 06d07e3a4c92354cb124babbd731b59b036c8815 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 9 Feb 2022 20:09:51 +0000 Subject: [PATCH 087/233] Bug fix in min num of PRBs to schedule to a UE when there is multiple UEs --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 7c8d1163c7..ca0c09739c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -740,7 +740,7 @@ void pf_dl(module_id_t module_id, } /* Loop UE_sched to find max coeff and allocate transmission */ - while (max_num_ue > 0 && n_rb_sched > 0 && UE_sched.head >= 0) { + while (max_num_ue > 0 && n_rb_sched >= MIN_NUM_PRBS_TO_SCHEDULE && UE_sched.head >= 0) { /* Find max coeff from UE_sched*/ int *max = &UE_sched.head; /* assume head is max */ diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 6073366779..31ab96d31f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -403,7 +403,7 @@ bool nr_find_nb_rb(uint16_t Qm, return true; /* is the minimum enough? */ - *nb_rb = 5; + *nb_rb = MIN_NUM_PRBS_TO_SCHEDULE; *tbs = nr_compute_tbs(Qm, R, *nb_rb, nb_symb_sch, nb_dmrs_prb, 0, 0, 1) >> 3; if (bytes <= *tbs) return true; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 4131941c4a..4af1f850bc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -80,6 +80,8 @@ #define NR_NB_RA_PROC_MAX 4 #define MAX_NUM_OF_SSB 64 +#define MIN_NUM_PRBS_TO_SCHEDULE 5 + /*! \brief NR_list_t is a "list" (of users, HARQ processes, slices, ...). * Especially useful in the scheduler and to keep "classes" of users. */ typedef struct { From c1931a60e71cd800474ad33b31c77986a52ae3e2 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 9 Feb 2022 20:09:51 +0000 Subject: [PATCH 088/233] Bug fix in min num of PRBs to schedule to a UE when there is multiple UEs --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index cb76c22292..3afc7d4cde 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -740,7 +740,7 @@ void pf_dl(module_id_t module_id, } /* Loop UE_sched to find max coeff and allocate transmission */ - while (max_num_ue > 0 && n_rb_sched > 0 && UE_sched.head >= 0) { + while (max_num_ue > 0 && n_rb_sched >= MIN_NUM_PRBS_TO_SCHEDULE && UE_sched.head >= 0) { /* Find max coeff from UE_sched*/ int *max = &UE_sched.head; /* assume head is max */ diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 928d14c1aa..cc81a181ef 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -403,7 +403,7 @@ bool nr_find_nb_rb(uint16_t Qm, return true; /* is the minimum enough? */ - *nb_rb = 5; + *nb_rb = MIN_NUM_PRBS_TO_SCHEDULE; *tbs = nr_compute_tbs(Qm, R, *nb_rb, nb_symb_sch, nb_dmrs_prb, 0, 0, 1) >> 3; if (bytes <= *tbs) return true; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index f7735a28b1..80293279f2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -80,6 +80,8 @@ #define NR_NB_RA_PROC_MAX 4 #define MAX_NUM_OF_SSB 64 +#define MIN_NUM_PRBS_TO_SCHEDULE 5 + /*! \brief NR_list_t is a "list" (of users, HARQ processes, slices, ...). * Especially useful in the scheduler and to keep "classes" of users. */ typedef struct { From 571677b94896a1e7b2dd1ba438fd8560a2d9f711 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 10 Feb 2022 11:50:21 +0100 Subject: [PATCH 089/233] Make nr_find_nb_rb() take minimum RBsize --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 5 ++++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 5 +++-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 5 ++++- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 1 + openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 -- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 3afc7d4cde..0a21391162 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -598,6 +598,7 @@ bool allocate_dl_retransmission(module_id_t module_id, temp_ps.nrOfSymbols, temp_ps.N_PRB_DMRS * temp_ps.N_DMRS_SLOT, retInfo->tb_size, + 1, /* minimum of 1RB: need to find exact TBS, don't preclude any number */ rbSize, &new_tbs, &new_rbSize); @@ -739,8 +740,9 @@ void pf_dl(module_id_t module_id, } } + const int min_rbSize = 5; /* Loop UE_sched to find max coeff and allocate transmission */ - while (max_num_ue > 0 && n_rb_sched >= MIN_NUM_PRBS_TO_SCHEDULE && UE_sched.head >= 0) { + while (max_num_ue > 0 && n_rb_sched >= min_rbSize && UE_sched.head >= 0) { /* Find max coeff from UE_sched*/ int *max = &UE_sched.head; /* assume head is max */ @@ -859,6 +861,7 @@ void pf_dl(module_id_t module_id, ps->nrOfSymbols, ps->N_PRB_DMRS * ps->N_DMRS_SLOT, sched_ctrl->num_total_bytes + oh, + min_rbSize, max_rbSize, &TBS, &rbSize); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index cc81a181ef..bb13a009e1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -388,6 +388,7 @@ bool nr_find_nb_rb(uint16_t Qm, uint16_t nb_symb_sch, uint16_t nb_dmrs_prb, uint32_t bytes, + uint16_t nb_rb_min, uint16_t nb_rb_max, uint32_t *tbs, uint16_t *nb_rb) @@ -403,7 +404,7 @@ bool nr_find_nb_rb(uint16_t Qm, return true; /* is the minimum enough? */ - *nb_rb = MIN_NUM_PRBS_TO_SCHEDULE; + *nb_rb = nb_rb_min; *tbs = nr_compute_tbs(Qm, R, *nb_rb, nb_symb_sch, nb_dmrs_prb, 0, 0, 1) >> 3; if (bytes <= *tbs) return true; @@ -411,7 +412,7 @@ bool nr_find_nb_rb(uint16_t Qm, /* perform binary search to allocate all bytes within a TBS up to nb_rb_max * RBs */ int hi = nb_rb_max; - int lo = 1; + int lo = nb_rb_min; for (int p = (hi + lo) / 2; lo + 1 < hi; p = (hi + lo) / 2) { const uint32_t TBS = nr_compute_tbs(Qm, R, p, nb_symb_sch, nb_dmrs_prb, 0, 0, 1) >> 3; if (bytes == TBS) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 6d7dbf5506..24fa6fb4e0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1004,6 +1004,7 @@ bool allocate_ul_retransmission(module_id_t module_id, temp_ps.nrOfSymbols, temp_ps.N_PRB_DMRS * temp_ps.num_dmrs_symb, retInfo->tb_size, + 1, /* minimum of 1RB: need to find exact TBS, don't preclude any number */ rbSize, &new_tbs, &new_rbSize); @@ -1256,8 +1257,9 @@ void pf_ul(module_id_t module_id, } + const int min_rbSize = 5; /* Loop UE_sched to find max coeff and allocate transmission */ - while (UE_sched.head >= 0 && max_num_ue> 0 && n_rb_sched > 0) { + while (UE_sched.head >= 0 && max_num_ue> 0 && n_rb_sched >= min_rbSize) { /* Find max coeff */ int *max = &UE_sched.head; /* Find max coeff: assume head is max */ int *p = &UE_sched.next[*max]; @@ -1349,6 +1351,7 @@ void pf_ul(module_id_t module_id, ps->nrOfSymbols, ps->N_PRB_DMRS * ps->num_dmrs_symb, B, + min_rbSize, max_rbSize, &TBS, &rbSize); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 2dbb924d7b..90fd5ca6ce 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -461,6 +461,7 @@ bool nr_find_nb_rb(uint16_t Qm, uint16_t nb_symb_sch, uint16_t nb_dmrs_prb, uint32_t bytes, + uint16_t nb_rb_min, uint16_t nb_rb_max, uint32_t *tbs, uint16_t *nb_rb); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 80293279f2..f7735a28b1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -80,8 +80,6 @@ #define NR_NB_RA_PROC_MAX 4 #define MAX_NUM_OF_SSB 64 -#define MIN_NUM_PRBS_TO_SCHEDULE 5 - /*! \brief NR_list_t is a "list" (of users, HARQ processes, slices, ...). * Especially useful in the scheduler and to keep "classes" of users. */ typedef struct { From 0a6322103cfb815761dad6af867630462a96accd Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 10 Feb 2022 12:04:07 +0100 Subject: [PATCH 090/233] Reduce max_num_ue after successful PUCCH alloc --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 10 ++++++---- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 3 +-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 0a21391162..bf29fd0e01 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -802,9 +802,6 @@ void pf_dl(module_id_t module_id, LOG_D(NR_MAC, "%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n", frame, slot, UE_id, rnti); continue; } - /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */ - max_num_ue--; - if (max_num_ue < 0) return; /* Find PUCCH occasion: if it fails, undo CCE allocation (undoing PUCCH * allocation after CCE alloc fail would be more complex) */ @@ -818,9 +815,14 @@ void pf_dl(module_id_t module_id, frame, slot); mac->pdcch_cand[cid]--; - return; + continue; } + /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE + * and PUCCH */ + max_num_ue--; + AssertFatal(max_num_ue >= 0, "Illegal max_num_ue %d\n", max_num_ue); + sched_ctrl->cce_index = CCEIndex; fill_pdcch_vrb_map(mac, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 24fa6fb4e0..e084d5f9ff 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1305,8 +1305,7 @@ void pf_ul(module_id_t module_id, /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */ max_num_ue--; - if (max_num_ue < 0) - return; + AssertFatal(max_num_ue >= 0, "Illegal max_num_ue %d\n", max_num_ue); NR_CellGroupConfig_t *cg = UE_info->CellGroup[UE_id]; NR_BWP_UplinkDedicated_t *ubwpd= cg ? cg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP:NULL; From acdbb5bb8d4a3426fb1fc3dc60c1fb30477f1211 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Mon, 14 Feb 2022 14:03:05 +0000 Subject: [PATCH 091/233] Fix segmentation fault in phy-test --- openair2/LAYER2/NR_MAC_gNB/config.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 642cfe5a53..91a63a0a2e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -531,12 +531,13 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, } } - if (mib) RC.nrmac[Mod_idP]->common_channels[0].mib = mib; - + if (mib) RC.nrmac[Mod_idP]->common_channels[0].mib = mib; + if (CellGroup) { - // Preferred DL TDA for InitialBWP - calculate_preferred_dl_tda(Mod_idP, NULL); + if (!get_softmodem_params()->phy_test) { + calculate_preferred_dl_tda(Mod_idP, NULL); + } const NR_ServingCellConfig_t *servingCellConfig = CellGroup->spCellConfig->spCellConfigDedicated; const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig->downlinkBWP_ToAddModList; @@ -613,7 +614,6 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, UE_info->CellGroup[UE_id] = CellGroup; LOG_I(NR_MAC,"Modified UE_id %d/%x with CellGroup\n",UE_id,rnti); process_CellGroup(CellGroup,&UE_info->UE_sched_ctrl[UE_id]); - const NR_ServingCellConfig_t *servingCellConfig = CellGroup ? CellGroup->spCellConfig->spCellConfigDedicated : NULL; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; const NR_PDSCH_ServingCellConfig_t *pdsch = servingCellConfig ? servingCellConfig->pdsch_ServingCellConfig->choice.setup : NULL; if (sched_ctrl->available_dl_harq.len == 0) { From 1cd71d1742b9e9ac5b1fdc67e830bd70c8f91a09 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 16 Feb 2022 11:51:20 +0000 Subject: [PATCH 092/233] Change LOG level --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 5e482a8816..87a4d20cff 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1905,7 +1905,9 @@ void fill_initial_SpCellConfig(rnti_t rnti, } } - xer_fprint(stdout, &asn_DEF_NR_SpCellConfig, (void *)SpCellConfig); + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_SpCellConfig, (void *)SpCellConfig); + } } From 405edc8bd366326519bb33e7fd5d55d90ebdff96 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 16 Feb 2022 12:43:04 +0000 Subject: [PATCH 093/233] Fix RRC state and sub-state --- openair2/RRC/NR_UE/rrc_UE.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index f910ff68d4..a75df842e4 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1480,8 +1480,8 @@ int8_t nr_rrc_ue_decode_ccch( const protocol_ctxt_t *const ctxt_pP, const NR_SRB nr_rrc_ue_process_RadioBearerConfig(ctxt_pP, gNB_index, &dl_ccch_msg->message.choice.c1->choice.rrcSetup->criticalExtensions.choice.rrcSetup->radioBearerConfig); - nr_rrc_set_state (ctxt_pP->module_id, RRC_STATE_CONNECTED); - nr_rrc_set_sub_state (ctxt_pP->module_id, RRC_SUB_STATE_CONNECTED); + nr_rrc_set_state (ctxt_pP->module_id, RRC_STATE_CONNECTED_NR); + nr_rrc_set_sub_state (ctxt_pP->module_id, RRC_SUB_STATE_CONNECTED_NR); NR_UE_rrc_inst[ctxt_pP->module_id].Info[gNB_index].rnti = ctxt_pP->rnti; rrc_ue_generate_RRCSetupComplete( ctxt_pP, From 6b69e67f71b75ab72b0c3368c8f33a1604603d96 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 16 Feb 2022 16:43:49 +0000 Subject: [PATCH 094/233] Fix number of search spaces --- nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h | 3 ++- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h index f107b5b973..42b41ee11b 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h @@ -10,7 +10,8 @@ #define FAPI_NR_MAX_RA_OCCASION_PER_CSIRS 64 // Constants Defined in 38.213 #define FAPI_NR_MAX_CORESET_PER_BWP 3 -#define FAPI_NR_MAX_SS 10 +#define FAPI_NR_MAX_SS_PER_BWP 10 +#define FAPI_NR_MAX_SS FAPI_NR_MAX_SS_PER_BWP*NR_MAX_NUM_BWP /// RX_IND diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 2235bcf224..b72f6427c3 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -575,7 +575,7 @@ void configure_ss_coreset(NR_UE_MAC_INST_t *mac, struct NR_PDCCH_Config__searchSpacesToAddModList *searchSpacesToAddModList = pdcch_Config->choice.setup->searchSpacesToAddModList; AssertFatal(searchSpacesToAddModList != NULL, "searchSpacesToAddModList is null\n"); AssertFatal(searchSpacesToAddModList->list.count > 0, "list of UE specifically configured Search Spaces is empty\n"); - AssertFatal(searchSpacesToAddModList->list.count < FAPI_NR_MAX_SS, "too many searchpaces per coreset %d\n", searchSpacesToAddModList->list.count); + AssertFatal(searchSpacesToAddModList->list.count < FAPI_NR_MAX_SS_PER_BWP, "too many searchpaces per coreset %d\n", searchSpacesToAddModList->list.count); // check available Search Spaces in the searchSpacesToAddModList and pass to MAC // note: the network configures at most 10 Search Spaces per BWP per cell (including UE-specific and common Search Spaces). From bddcfc512faec03875bb7097895c81591787b43c Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 16 Feb 2022 19:23:53 +0000 Subject: [PATCH 095/233] Implementation of functions required for the BWP switching based on RRC method --- openair2/LAYER2/NR_MAC_gNB/config.c | 2 + openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 3 + .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 84 +++++++++++++++++-- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 5 ++ .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 19 ++++- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 4 + openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 21 ++++- openair2/RRC/NR/L2_nr_interface.c | 13 +++ openair2/RRC/NR/MESSAGES/asn1_msg.c | 10 ++- openair2/RRC/NR/nr_rrc_proto.h | 10 +++ openair2/RRC/NR/rrc_gNB.c | 38 +++++++++ 11 files changed, 193 insertions(+), 16 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 91a63a0a2e..3812ec4a7f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -615,6 +615,8 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, LOG_I(NR_MAC,"Modified UE_id %d/%x with CellGroup\n",UE_id,rnti); process_CellGroup(CellGroup,&UE_info->UE_sched_ctrl[UE_id]); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + sched_ctrl->update_pdsch_ps = true; + sched_ctrl->update_pusch_ps = true; const NR_PDSCH_ServingCellConfig_t *pdsch = servingCellConfig ? servingCellConfig->pdsch_ServingCellConfig->choice.setup : NULL; if (sched_ctrl->available_dl_harq.len == 0) { // add all available DL HARQ processes for this UE in SA diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index ba854608c5..f2d3474f3f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -346,6 +346,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, LOG_I(NR_MAC,"Frame.Slot %d.%d\n%s\n",frame,slot,stats_output); } + // This schedules the BWP switching + schedule_nr_bwp_switch(module_idP, frame, slot); + // This schedules MIB schedule_nr_mib(module_idP, frame, slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index c7cd6a435d..ff1c2587cf 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -500,14 +500,14 @@ void nr_store_dlsch_buffer(module_id_t module_id, 0); stop_meas(&RC.nrmac[module_id]->rlc_status_ind); } - if(sched_ctrl->rlc_status[DL_SCH_LCID_DCCH].bytes_in_buffer > 0){ + if(sched_ctrl->rlc_status[DL_SCH_LCID_DCCH].bytes_in_buffer > 0) { lcid = DL_SCH_LCID_DCCH; - } - else if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH1].bytes_in_buffer > 0) - { + } else if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH1].bytes_in_buffer > 0) { lcid = DL_SCH_LCID_DCCH1; - }else{ - lcid = DL_SCH_LCID_DTCH; + } else if (sched_ctrl->bwp_switch_info.bwp_switch_state != BWP_SWITCH_RUNNING) { + lcid = DL_SCH_LCID_DTCH; + } else { + continue; } sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; @@ -577,8 +577,10 @@ bool allocate_dl_retransmission(module_id_t module_id, } /* check whether we need to switch the TDA allocation since the last * (re-)transmission */ - if (ps->time_domain_allocation != tda) + if (ps->time_domain_allocation != tda || sched_ctrl->update_pdsch_ps) { nr_set_pdsch_semi_static(scc, cg, sched_ctrl->active_bwp, bwpd, tda, f, ps); + sched_ctrl->update_pdsch_ps = false; + } } else { /* the retransmission will use a different time domain allocation, check * that we have enough resources */ @@ -838,8 +840,10 @@ void pf_dl(module_id_t module_id, NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; NR_pdsch_semi_static_t *ps = &sched_ctrl->pdsch_semi_static; const long f = (sched_ctrl->active_bwp || bwpd) ? sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats : 0; - if (ps->time_domain_allocation != tda) + if (ps->time_domain_allocation != tda || sched_ctrl->update_pdsch_ps) { nr_set_pdsch_semi_static(scc, UE_info->CellGroup[UE_id], sched_ctrl->active_bwp, bwpd, tda, f, ps); + sched_ctrl->update_pdsch_ps = false; + } const uint16_t slbitmap = SL_to_bitmap(ps->startSymbolIndex, ps->nrOfSymbols); // Freq-demain allocation @@ -964,6 +968,70 @@ nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(module_id_t module_id, int CC_id) return nr_fr1_dlsch_preprocessor; } +void nr_bwp_switch(module_id_t module_id, + frame_t frame, + sub_frame_t slot, + int UE_id, + int bwp_id) { + + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + + switch(sched_ctrl->bwp_switch_info.bwp_switch_state) { + + case BWP_SWITCH_TO_START: + LOG_W(NR_MAC,"(%d.%d) [UE_id %d] Schedule BWP switch from bwp_id %ld to %d\n", + frame, slot, UE_id, UE_info->UE_sched_ctrl[UE_id].active_bwp->bwp_Id, bwp_id); + sched_ctrl->bwp_switch_info.bwp_switch_timer = 0; + sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_RUNNING; + nr_mac_rrc_bwp_switch_req(module_id, frame, slot, UE_info->rnti[UE_id], bwp_id); + break; + + case BWP_SWITCH_RUNNING: + sched_ctrl->bwp_switch_info.bwp_switch_timer++; + if(sched_ctrl->bwp_switch_info.bwp_switch_timer == sched_ctrl->bwp_switch_info.bwp_switch_delay) { + const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; + const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; + const int bwp_id = servingCellConfig && servingCellConfig->firstActiveDownlinkBWP_Id ? + *servingCellConfig->firstActiveDownlinkBWP_Id : 0; + sched_ctrl->active_bwp = bwpList && bwp_id > 0 ? bwpList->list.array[bwp_id - 1] : NULL; + const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; + const int ubwp_id = servingCellConfig && servingCellConfig->uplinkConfig && servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id ? + *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; + sched_ctrl->active_ubwp = ubwpList && ubwp_id > 0 ? ubwpList->list.array[ubwp_id - 1] : NULL; + sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_INACTIVE; + } + break; + + case BWP_SWITCH_INACTIVE: + break; + + default: + AssertFatal(1==0,"Invalid bwp switch state\n"); + break; + } +} + +void schedule_nr_bwp_switch(module_id_t module_id, + frame_t frame, + sub_frame_t slot) { + + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + const NR_list_t *UE_list = &UE_info->list; + + // TODO: Implementation of a algorithm to perform: + // - the BWP switch trigger: sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_TO_START + // - the BWP selection: sched_ctrl->bwp_switch_info.next_bwp = bwp_id + + for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if(sched_ctrl->bwp_switch_info.bwp_switch_state > BWP_SWITCH_INACTIVE) { + nr_bwp_switch(module_id, frame, slot, UE_id, sched_ctrl->bwp_switch_info.next_bwp); + } + } + +} + void nr_schedule_ue_spec(module_id_t module_id, frame_t frame, sub_frame_t slot) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 407c325cff..072757aaed 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2076,6 +2076,11 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG scc, genericParameters, NULL); + sched_ctrl->bwp_switch_info.current_bwp = bwp_id; + sched_ctrl->bwp_switch_info.next_bwp = -1; + sched_ctrl->bwp_switch_info.bwp_switch_timer = 0; + sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_INACTIVE; + sched_ctrl->bwp_switch_info.bwp_switch_delay = 40; const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; if (ubwpList) AssertFatal(ubwpList->list.count <= NR_MAX_NUM_BWP, "uplinkBWP_ToAddModList has %d BWP!\n", diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index af77767ea2..6286d99f29 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -983,8 +983,11 @@ bool allocate_ul_retransmission(module_id_t module_id, if (ps->time_domain_allocation != tda || ps->dci_format != dci_format - || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) + || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data + || sched_ctrl->update_pusch_ps) { nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); + sched_ctrl->update_pusch_ps = false; + } LOG_D(NR_MAC, "%s(): retransmission keeping TDA %d and TBS %d\n", __func__, tda, retInfo->tb_size); } else { /* the retransmission will use a different time domain allocation, check @@ -1163,6 +1166,10 @@ void pf_ul(module_id_t module_id, if (B == 0 && !do_sched) continue; + if(sched_ctrl->bwp_switch_info.bwp_switch_state == BWP_SWITCH_RUNNING) { + continue; + } + /* Schedule UE on SR or UL inactivity and no data (otherwise, will be scheduled * based on data to transmit) */ if (B == 0 && do_sched) { @@ -1220,8 +1227,11 @@ void pf_ul(module_id_t module_id, const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format - || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) + || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data + || sched_ctrl->update_pusch_ps) { nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); + sched_ctrl->update_pusch_ps = false; + } NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch; sched_pusch->mcs = 9; update_ul_ue_R_Qm(sched_pusch, ps); @@ -1337,8 +1347,11 @@ void pf_ul(module_id_t module_id, const int tda = sched_ctrl->active_ubwp ? nrmac->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; if (ps->time_domain_allocation != tda || ps->dci_format != dci_format - || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data) + || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data + || sched_ctrl->update_pusch_ps) { nr_set_pusch_semi_static(scc, sched_ctrl->active_ubwp, ubwpd, dci_format, tda, num_dmrs_cdm_grps_no_data, ps); + sched_ctrl->update_pusch_ps = false; + } update_ul_ue_R_Qm(sched_pusch, ps); /* Calculate the current scheduling bytes and the necessary RBs */ diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index c8c8b40fc9..a33d494c58 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -64,6 +64,10 @@ void clear_nr_nfapi_information(gNB_MAC_INST * gNB, void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame_rxP, sub_frame_t slot_rxP); +void schedule_nr_bwp_switch(module_id_t module_id, + frame_t frame, + sub_frame_t slot); + /* \brief main DL scheduler function. Calls a preprocessor to decide on * resource allocation, then "post-processes" resource allocation (nFAPI * messages, statistics, HARQ handling, CEs, ... */ diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 3db5326d39..9a00c381be 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -551,6 +551,20 @@ typedef struct NR_UE_ul_harq { NR_sched_pusch_t sched_pusch; } NR_UE_ul_harq_t; +typedef enum { + BWP_SWITCH_INACTIVE = 0, + BWP_SWITCH_TO_START, + BWP_SWITCH_RUNNING +} NR_BWP_switch_states_t; + +typedef struct NR_BWP_switch_info { + NR_BWP_Id_t current_bwp; + NR_BWP_Id_t next_bwp; + int bwp_switch_timer; + uint8_t bwp_switch_delay; + NR_BWP_switch_states_t bwp_switch_state; +} NR_BWP_switch_info_t; + /*! \brief scheduling control information set through an API */ #define MAX_CSI_REPORTS 48 typedef struct { @@ -558,6 +572,10 @@ typedef struct { NR_BWP_Downlink_t *active_bwp; /// the currently active BWP in UL NR_BWP_Uplink_t *active_ubwp; + + /// the state of bwp switch procedure + NR_BWP_switch_info_t bwp_switch_info; + /// CCE index and aggregation, should be coherent with cce_list NR_SearchSpace_t *search_space; NR_ControlResourceSet_t *coreset; @@ -624,7 +642,8 @@ typedef struct { int ul_failure; struct CSI_Report CSI_report[MAX_CSI_REPORTS]; bool SR; - + bool update_pdsch_ps; + bool update_pusch_ps; /// information about every HARQ process NR_UE_harq_t harq_processes[NR_MAX_NB_HARQ_PROCESSES]; /// HARQ processes that are free diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index cb29ecf347..3fa47389b2 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -304,6 +304,19 @@ uint16_t mac_rrc_nr_data_req(const module_id_t Mod_idP, } +int8_t nr_mac_rrc_bwp_switch_req(const module_id_t module_idP, + const frame_t frameP, + const sub_frame_t sub_frameP, + const rnti_t rntiP, + const int bwp_id) { + + struct rrc_gNB_ue_context_s *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[module_idP], rntiP); + + protocol_ctxt_t ctxt; + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, GNB_FLAG_YES, rntiP, frameP, sub_frameP, 0); + nr_rrc_reconfiguration_req(ue_context_p, &ctxt, bwp_id); +} + int8_t nr_mac_rrc_data_ind(const module_id_t module_idP, const int CC_id, const frame_t frameP, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 87a4d20cff..0c0849e362 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -2493,7 +2493,9 @@ int16_t do_RRCReconfiguration( enc_rval.failed_type->name, enc_rval.encoded); return -1; } - xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)cellGroupConfig); + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); + } ie->nonCriticalExtension->masterCellGroup = calloc(1,sizeof(OCTET_STRING_t)); ie->nonCriticalExtension->masterCellGroup->buf = masterCellGroup_buf; @@ -2502,9 +2504,9 @@ int16_t do_RRCReconfiguration( dl_dcch_msg.message.choice.c1->choice.rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration = ie; - //if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { - xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg); - //} + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg); + } enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index a4e6d13a7d..fd54afe484 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -178,6 +178,12 @@ int nr_rrc_mac_remove_ue(module_id_t mod_idP, rnti_t rntiP); +int8_t nr_mac_rrc_bwp_switch_req(const module_id_t module_idP, + const frame_t frameP, + const sub_frame_t sub_frameP, + const rnti_t rntiP, + const int bwp_id); + int8_t nr_mac_rrc_data_ind( const module_id_t module_idP, const int CC_id, @@ -191,6 +197,10 @@ int8_t nr_mac_rrc_data_ind( const boolean_t brOption ); +int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP, + protocol_ctxt_t *const ctxt_pP, + const int bwp_id); + int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, const uint8_t *buffer, int buffer_length, diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index d7e6c4f9d4..dbc37cfc45 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1775,6 +1775,44 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( } //----------------------------------------------------------------------------- +int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP, + protocol_ctxt_t *const ctxt_pP, + const int bwp_id) { + + uint8_t buffer[RRC_BUF_SIZE]; + memset(buffer, 0, sizeof(buffer)); + uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); + + NR_CellGroupConfig_t *masterCellGroup = ue_context_pP->ue_context.masterCellGroup; + *masterCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = bwp_id; + *masterCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = bwp_id; + *masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = bwp_id; + + uint16_t size = do_RRCReconfiguration(ctxt_pP, + buffer, + sizeof(buffer), + xid, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + masterCellGroup); + + nr_rrc_data_req(ctxt_pP, + DCCH, + rrc_gNB_mui++, + SDU_CONFIRM_NO, + size, + buffer, + PDCP_TRANSMISSION_MODE_CONTROL); + + return 0; +} + /*------------------------------------------------------------------------------*/ int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, const uint8_t *buffer, From 77153fbf5f053b8d487b97a76b9ca09116c27cb4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 17 Feb 2022 10:16:22 +0100 Subject: [PATCH 096/233] TBSLBRM for DL computed ad MAC (added fapi parameter) --- .../nfapi/public_inc/fapi_nr_ue_interface.h | 2 +- .../nfapi/public_inc/nfapi_nr_interface_scf.h | 5 ++ openair1/PHY/CODING/coding_defs.h | 4 -- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 7 +-- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 6 +-- .../PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c | 7 +-- .../PHY/NR_UE_TRANSPORT/nr_transport_ue.h | 1 + .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 8 +-- openair1/PHY/defs_gNB.h | 1 - openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 1 + .../NR_MAC_COMMON/nr_compute_tbs_common.c | 51 +++++++++---------- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 22 ++++++++ openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 7 +++ openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 15 ++++++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 10 ++++ .../LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 4 ++ .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 14 ++++- 17 files changed, 106 insertions(+), 59 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index 3b429a5e84..9e051e0a48 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -464,7 +464,7 @@ typedef struct { uint8_t nEpreRatioOfPDSCHToPTRS; /// MCS table for this DLSCH uint8_t mcs_table; - + uint32_t tbslbrm; uint16_t pduBitmap; } fapi_nr_dl_config_dlsch_pdu_rel15_t; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index c3a173758c..ee680f7fc7 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -776,6 +776,10 @@ typedef struct { nfapi_nr_dl_dci_pdu_t dci_pdu[MAX_DCI_CORESET]; } nfapi_nr_dl_tti_pdcch_pdu_rel15_t; +typedef struct { + uint32_t tbSizeLbrmBytes; +}nfapi_v3_pdsch_maintenance_parameters_t; + typedef struct { uint16_t pduBitmap; uint16_t rnti; @@ -854,6 +858,7 @@ typedef struct { uint8_t nEpreRatioOfPDSCHToPTRS; // Beamforming nfapi_nr_tx_precoding_and_beamforming_t precodingAndBeamforming; + nfapi_v3_pdsch_maintenance_parameters_t maintenance_parms_v3; }nfapi_nr_dl_tti_pdsch_pdu_rel15_t; diff --git a/openair1/PHY/CODING/coding_defs.h b/openair1/PHY/CODING/coding_defs.h index 8fe31f208e..f6aba66830 100644 --- a/openair1/PHY/CODING/coding_defs.h +++ b/openair1/PHY/CODING/coding_defs.h @@ -481,10 +481,6 @@ uint32_t nr_compute_tbs(uint16_t Qm, uint8_t tb_scaling, uint8_t Nl); -uint32_t nr_compute_tbslbrm(uint16_t table, - uint16_t nb_rb, - uint8_t Nl); - void nr_interleaving_ldpc(uint32_t E, uint8_t Qm, uint8_t *e,uint8_t *f); void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e,int16_t *f); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index 6d16fa531c..14de9ed767 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -229,13 +229,8 @@ void ldpc8blocks( void *p) { impp->F, Kr-impp->F-2*(*impp->Zc), mod_order,nb_rb); - // for tbslbrm calculation according to 5.4.2.1 of 38.212 - uint8_t Nl = 4; - if (rel15->nrOfLayers < Nl) - Nl = rel15->nrOfLayers; - - uint32_t Tbslbrm = nr_compute_tbslbrm(rel15->mcsTable[0],nb_rb,Nl); + uint32_t Tbslbrm = rel15->maintenance_parms_v3.tbSizeLbrmBytes; uint8_t Ilbrm = 1; uint8_t e[E]; diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 31d44736d4..250a243af1 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -259,7 +259,6 @@ void nr_processULSegment(void* arg) { int rv_index = rdata->rv_index; int r_offset = rdata->r_offset; uint8_t kc = rdata->Kc; - uint32_t Tbslbrm = rdata->Tbslbrm; short* ulsch_llr = rdata->ulsch_llr; int max_ldpc_iterations = p_decoderParms->numMaxIter; int8_t llrProcBuf[OAI_UL_LDPC_MAX_NUM_LLR] __attribute__ ((aligned(32))); @@ -319,7 +318,7 @@ void nr_processULSegment(void* arg) { //start_meas(&phy_vars_gNB->ulsch_rate_unmatching_stats); if (nr_rate_matching_ldpc_rx(Ilbrm, - Tbslbrm, + 0, p_decoderParms->BG, p_decoderParms->Z, ulsch_harq->d[r], @@ -420,7 +419,6 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, uint32_t r_offset; uint32_t offset; int kc; - int Tbslbrm; int E; #ifdef PRINT_CRC_CHECK @@ -559,7 +557,6 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, if (!frame%100) printf("K %d C %d Z %d \n", harq_process->K, harq_process->C, harq_process->Z); #endif - Tbslbrm = nr_compute_tbslbrm(0,nb_rb,n_layers); p_decParams->Z = harq_process->Z; @@ -610,7 +607,6 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, rdata->r_offset = r_offset; rdata->Kr_bytes = Kr_bytes; rdata->rv_index = pusch_pdu->pusch_data.rv_index; - rdata->Tbslbrm = Tbslbrm; rdata->offset = offset; rdata->ulsch = ulsch; rdata->ulsch_id = ULSCH_id; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c index 8064093eed..32848951c2 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c @@ -472,7 +472,6 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, phy_vars_ue->dl_stats[harq_process->round]++; LOG_D(PHY,"Round %d RV idx %d\n",harq_process->round,harq_process->rvidx); uint8_t kc; - uint32_t Tbslbrm;// = 950984; uint16_t nb_rb;// = 30; double Coderate;// = 0.0; uint8_t dmrs_Type = harq_process->dmrsConfigType; @@ -583,10 +582,6 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, if (LOG_DEBUGFLAG(DEBUG_DLSCH_DECOD) && (!frame%100)) LOG_I(PHY,"K %d C %d Z %d nl %d \n", harq_process->K, harq_process->C, p_decParams->Z, harq_process->Nl); } - if ((harq_process->Nl)<4) - Tbslbrm = nr_compute_tbslbrm(harq_process->mcs_table,nb_rb,harq_process->Nl); - else - Tbslbrm = nr_compute_tbslbrm(harq_process->mcs_table,nb_rb,4); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_SEGMENTATION, VCD_FUNCTION_OUT); p_decParams->Z = harq_process->Z; @@ -637,7 +632,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, rdata->r_offset = r_offset; rdata->Kr_bytes = Kr_bytes; rdata->rv_index = harq_process->rvidx; - rdata->Tbslbrm = Tbslbrm; + rdata->Tbslbrm = harq_process->tbslbrm; rdata->offset = offset; rdata->dlsch = dlsch; rdata->dlsch_id = 0; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h index 9b9f1add6e..7048c97189 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h @@ -303,6 +303,7 @@ typedef struct { uint16_t ptrs_symbols; // PTRS symbol index, to be updated every PTRS symbol within a slot. uint8_t ptrs_symbol_index; + uint32_t tbslbrm; /// PDU BITMAP uint16_t pduBitmap; } NR_DL_UE_HARQ_t; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index 553d4fa4f7..91e14fdfd4 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -233,8 +233,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, uint16_t Kr,r; uint32_t r_offset; uint32_t E,Kb; - uint8_t Ilbrm; - uint32_t Tbslbrm; + uint8_t Ilbrm; uint16_t R; float Coderate; @@ -256,7 +255,6 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, r_offset=0; F=0; Ilbrm = 0; - Tbslbrm = 950984; //max tbs Coderate = 0.0; /////////// ///////////////////////////////////////////////////////////////////////////////////////// @@ -440,12 +438,10 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, E = nr_get_E(G, harq_process->C, mod_order, harq_process->pusch_pdu.nrOfLayers, r); - Tbslbrm = nr_compute_tbslbrm(0,nb_rb,harq_process->pusch_pdu.nrOfLayers); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RATE_MATCHING_LDPC, VCD_FUNCTION_IN); start_meas(&ue->ulsch_rate_matching_stats); if (nr_rate_matching_ldpc(Ilbrm, - Tbslbrm, + 0, harq_process->BG, *pz, harq_process->d[r], diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 7c7d30a6e2..5e3a541d20 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -928,7 +928,6 @@ typedef struct LDPCDecode_s { int segment_r; int r_offset; int offset; - int Tbslbrm; int decodeIterations; } ldpcDecode_t; diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index 4f4757ec5a..c8386b881e 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -286,6 +286,7 @@ int8_t nr_ue_scheduled_response(nr_scheduled_response_t *scheduled_response){ dlsch0_harq->n_dmrs_cdm_groups = dlsch_config_pdu->n_dmrs_cdm_groups; dlsch0_harq->mcs = dlsch_config_pdu->mcs; dlsch0_harq->rvidx = dlsch_config_pdu->rv; + dlsch0_harq->tbslbrm = dlsch_config_pdu->tbslbrm; dlsch0->g_pucch = dlsch_config_pdu->accumulated_delta_PUCCH; //get nrOfLayers from DCI info uint8_t Nl = 0; diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c index 4fcbd322db..b07659a0ea 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c @@ -92,12 +92,10 @@ uint32_t nr_compute_tbs(uint16_t Qm, } - //tbslbrm calculation according to 5.4.2.1 of 38.212 uint32_t nr_compute_tbslbrm(uint16_t table, uint16_t nb_rb, - uint8_t Nl) -{ + uint8_t Nl) { uint16_t R, nb_re; uint16_t nb_rb_lbrm=0; @@ -122,34 +120,31 @@ uint32_t nr_compute_tbslbrm(uint16_t table, Ninfo = (nb_re * R * Qm * Nl)>>10; if (Ninfo <=3824) { - n = max(3, floor(log2(Ninfo)) - 6); - Np_info = max(24, (Ninfo>>n)<= Np_info){ - nr_tbs = Tbstable_nr[i]; - break; - } - } + n = max(3, floor(log2(Ninfo)) - 6); + Np_info = max(24, (Ninfo>>n)<= Np_info){ + nr_tbs = Tbstable_nr[i]; + break; + } + } } else { - n = log2(Ninfo-24)-5; - Np_info = max(3840, (ROUNDIDIV((Ninfo-24),(1< 8424){ - C = CEILIDIV((Np_info+24),8424); - nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24; - } - else { - nr_tbs = ((CEILIDIV((Np_info+24),8))<<3) - 24; - } - - } + n = log2(Ninfo-24)-5; + Np_info = max(3840, (ROUNDIDIV((Ninfo-24),(1< 8424){ + C = CEILIDIV((Np_info+24),8424); + nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24; + } + else + nr_tbs = ((CEILIDIV((Np_info+24),8))<<3) - 24; + } } return nr_tbs; } diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 942e3afad1..69d245e7c1 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -4076,6 +4076,28 @@ uint16_t compute_pucch_prb_size(uint8_t format, } } +int get_bw_tbslbrm(NR_ServingCellConfigCommon_t *scc, + NR_CellGroupConfig_t *cg) { + + int bw = 0; + NR_BWP_t genericParameters; + if (cg && cg->spCellConfig && cg->spCellConfig->spCellConfigDedicated && + cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { + struct NR_ServingCellConfig__downlinkBWP_ToAddModList *BWP_list = cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList; + for (int i=0; ilist.count; i++) { + genericParameters = BWP_list->list.array[i]->bwp_Common->genericParameters; + int curr_bw = NRRIV2BW(genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + if (curr_bw > bw) + bw = curr_bw; + } + } + else { + genericParameters = scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + bw = NRRIV2BW(genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + } + return bw; +} + /* extract UL PTRS values from RRC and validate it based upon 38.214 6.2.3 */ bool set_ul_ptrs_values(NR_PTRS_UplinkConfig_t *ul_ptrs_config, uint16_t rbSize,uint8_t mcsIndex, uint8_t mcsTable, diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index 91450fc796..bccc40b2c6 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -121,6 +121,13 @@ uint8_t get_K_ptrs(uint16_t nrb0, uint16_t nrb1, uint16_t N_RB); uint16_t get_nr_srs_offset(NR_SRS_PeriodicityAndOffset_t periodicityAndOffset); +int get_bw_tbslbrm(NR_ServingCellConfigCommon_t *scc, + NR_CellGroupConfig_t *cg); + +uint32_t nr_compute_tbslbrm(uint16_t table, + uint16_t nb_rb, + uint8_t Nl); + void get_type0_PDCCH_CSS_config_parameters(NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config, frame_t frameP, NR_MIB_t *mib, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 85bb10290a..f852da7281 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -890,6 +890,12 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr LOG_W(MAC, "[%d.%d] MCS value %d out of bounds! Possibly due to false DCI. Ignoring DCI!\n", frame, slot, dlsch_config_pdu_1_0->mcs); return -1; } + + int bw_tbslbrm = get_bw_tbslbrm(mac->scc, mac->cg); + dlsch_config_pdu_1_0->tbslbrm = nr_compute_tbslbrm(dlsch_config_pdu_1_0->mcs_table, + bw_tbslbrm, + 1); + /* NDI (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/ dlsch_config_pdu_1_0->ndi = dci->ndi; /* RV (only if CRC scrambled by C-RNTI or CS-RNTI or new-RNTI or TC-RNTI)*/ @@ -1254,6 +1260,15 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr dl_config->number_pdus = dl_config->number_pdus + 1; /* TODO same calculation for MCS table as done in UL */ dlsch_config_pdu_1_1->mcs_table = (pdsch_Config->mcs_Table) ? (*pdsch_Config->mcs_Table + 1) : 0; + + // TBS_LBRM according to section 5.4.2.1 of 38.212 + long *maxMIMO_Layers = mac->cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers; + AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n"); + int nl_tbslbrm = *maxMIMO_Layers < 4 ? *maxMIMO_Layers : 4; + int bw_tbslbrm = get_bw_tbslbrm(mac->scc, mac->cg); + dlsch_config_pdu_1_1->tbslbrm = nr_compute_tbslbrm(dlsch_config_pdu_1_1->mcs_table, + bw_tbslbrm, + nl_tbslbrm); /*PTRS configuration */ dlsch_config_pdu_1_1->pduBitmap = 0; if(pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 4261020f76..26479b4b88 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1338,6 +1338,11 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra uint8_t tb_scaling = 0; nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, tb_scaling); + int bw_tbslbrm = get_bw_tbslbrm(scc, ra->CellGroup); + pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(mcsTableIdx, + bw_tbslbrm, + 1); + // Fill PDCCH DL DCI PDU nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci]; pdcch_pdu_rel15->numDlDci++; @@ -1715,6 +1720,11 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra int x_Overhead = 0; nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, tb_scaling); + int bw_tbslbrm = get_bw_tbslbrm(scc, ra->CellGroup); + pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(mcsTableIdx, + bw_tbslbrm, + 1); + pdsch_pdu_rel15->precodingAndBeamforming.num_prgs=1; pdsch_pdu_rel15->precodingAndBeamforming.prg_size=275; pdsch_pdu_rel15->precodingAndBeamforming.dig_bf_interfaces=1; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index 0af340725a..634d2b7d89 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -502,6 +502,10 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP, pdsch_pdu_rel15->dlDmrsSymbPos = dlDmrsSymbPos; LOG_D(MAC,"dlDmrsSymbPos = 0x%x\n", pdsch_pdu_rel15->dlDmrsSymbPos); + pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(0, + pdsch_pdu_rel15->BWPSize, + 1); + /* Fill PDCCH DL DCI PDU */ nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci]; pdcch_pdu_rel15->numDlDci++; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index cb76c22292..5b39f44ee2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1140,6 +1140,16 @@ void nr_schedule_ue_spec(module_id_t module_id, pdsch_pdu->StartSymbolIndex = ps->startSymbolIndex; pdsch_pdu->NrOfSymbols = ps->nrOfSymbols; + // TBS_LBRM according to section 5.4.2.1 of 38.212 + long *maxMIMO_Layers = cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers; + AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n"); + int nl_tbslbrm = *maxMIMO_Layers < 4 ? *maxMIMO_Layers : 4; + // Maximum number of PRBs across all configured DL BWPs + int bw_tbslbrm = get_bw_tbslbrm(scc, cg); + pdsch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(ps->mcsTableIdx, + bw_tbslbrm, + nl_tbslbrm); + NR_PDSCH_Config_t *pdsch_Config=NULL; if (bwp && bwp->bwp_Dedicated && @@ -1189,7 +1199,7 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_pdu_rel15_t dci_payload; memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t)); // bwp indicator - const int n_dl_bwp = bwp ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count : 0; + const int n_dl_bwp = bwp ? cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count : 0; AssertFatal(n_dl_bwp <= 1, "downlinkBWP_ToAddModList has %d BWP!\n", n_dl_bwp); // as per table 7.3.1.1.2-1 in 38.212 @@ -1242,7 +1252,7 @@ void nr_schedule_ue_spec(module_id_t module_id, const int rnti_type = NR_RNTI_C; fill_dci_pdu_rel15(scc, - UE_info->CellGroup[UE_id], + cg, dci_pdu, &dci_payload, dci_format, From 95a02717797ee678d671ac63ec75518468c5fd24 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Thu, 17 Feb 2022 10:22:28 +0000 Subject: [PATCH 097/233] Fix warning --- openair2/RRC/NR/L2_nr_interface.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index 3fa47389b2..b32d02dae5 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -315,6 +315,8 @@ int8_t nr_mac_rrc_bwp_switch_req(const module_id_t module_idP, protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, GNB_FLAG_YES, rntiP, frameP, sub_frameP, 0); nr_rrc_reconfiguration_req(ue_context_p, &ctxt, bwp_id); + + return 0; } int8_t nr_mac_rrc_data_ind(const module_id_t module_idP, From d1e7dead00e6b4bb85975f0f295c07d39fd494b4 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Thu, 17 Feb 2022 14:14:50 +0000 Subject: [PATCH 098/233] Fix the removal of unused BWPs --- openair2/GNB_APP/gnb_config.c | 44 +++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 149b945a7f..28244ce49b 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -458,6 +458,25 @@ for (int j = 0; j < NR_MAX_NUM_BWP; j++) { /* This function checks dedicated serving cell configuration and performs fixes as needed */ void fix_scd(NR_ServingCellConfig_t *scd) { + // Remove unused BWPs + int b = 0; + while (bdownlinkBWP_ToAddModList->list.count) { + if (scd->downlinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == 0) { + asn_sequence_del(&scd->downlinkBWP_ToAddModList->list,b,1); + } else { + b++; + } + } + + b = 0; + while (buplinkConfig->uplinkBWP_ToAddModList->list.count) { + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == 0) { + asn_sequence_del(&scd->uplinkConfig->uplinkBWP_ToAddModList->list,b,1); + } else { + b++; + } + } + // Check for DL PTRS parameters validity for (int bwp_i = 0 ; bwp_idownlinkBWP_ToAddModList->list.count; bwp_i++) { @@ -1086,31 +1105,6 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { (int)scd->downlinkBWP_ToAddModList->list.array[0]->bwp_Common->genericParameters.locationAndBandwidth ); } - - for(int bb = 0; bbuplinkConfig->uplinkBWP_ToAddModList->list.count; bb++) { - printf("scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bb]->bwp_Common->genericParameters = %li\n", - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bb]->bwp_Common->genericParameters.locationAndBandwidth); - } - - // Remove unused BWPs - int b = 0; - while (bdownlinkBWP_ToAddModList->list.count) { - if (scd->downlinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == 0) { - asn_sequence_del(&scd->downlinkBWP_ToAddModList->list,b,1); - } else { - b++; - } - } - - b = 0; - while (buplinkConfig->uplinkBWP_ToAddModList->list.count) { - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == 0) { - asn_sequence_del(&scd->uplinkConfig->uplinkBWP_ToAddModList->list,b,1); - } else { - b++; - } - } - fix_scd(scd); printf("NRRRC %d: Southbound Transport %s\n",i,*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr)); From 7b4b7b96196784aee9d86aa6b686cc2dfb86bfa7 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 17 Feb 2022 18:13:57 +0100 Subject: [PATCH 099/233] fix for dlschsim --- openair1/SIMULATION/NR_PHY/dlschsim.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index f566b93d9c..a0dd1b433d 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -118,6 +118,7 @@ int main(int argc, char **argv) double DS_TDL = .03; cpuf = get_cpu_freq_GHz(); char gNBthreads[128]="n"; + int Tbslbrm = 950984; if (load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY) == 0) { exit_fun("[NR_DLSCHSIM] Error, configuration module init failed\n"); @@ -472,6 +473,7 @@ int main(int argc, char **argv) rel15->dlDmrsSymbPos = 4; rel15->mcsIndex[0] = Imcs; rel15->numDmrsCdmGrpsNoData = 1; + rel15->maintenance_parms_v3.tbSizeLbrmBytes = Tbslbrm; double *modulated_input = malloc16(sizeof(double) * 16 * 68 * 384); // [hna] 16 segments, 68*Zc short *channel_output_fixed = malloc16(sizeof(short) * 16 * 68 * 384); short *channel_output_uncoded = malloc16(sizeof(unsigned short) * 16 * 68 * 384); @@ -494,6 +496,7 @@ int main(int argc, char **argv) harq_process->dmrsConfigType = NFAPI_NR_DMRS_TYPE1; harq_process->dlDmrsSymbPos = 4; harq_process->n_dmrs_cdm_groups = 1; + harq_process->tbslbrm = Tbslbrm; printf("harq process ue mcs = %d Qm = %d, symb %d\n", harq_process->mcs, harq_process->Qm, nb_symb_sch); unsigned char *test_input; test_input = (unsigned char *) malloc16(sizeof(unsigned char) * TBS / 8); From 0789759b853448107d69d41bd270e1b5d7502a28 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Thu, 17 Feb 2022 20:09:39 +0000 Subject: [PATCH 100/233] Fix dedicated BWPs configuration for NSA --- openair2/RRC/NR/rrc_gNB_reconfig.c | 1386 +++++++++++++++------------- 1 file changed, 756 insertions(+), 630 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 2a5f031f06..7b9302bda2 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -49,6 +49,572 @@ #define false 0 #define true 1 +void fix_servingcellconfigdedicated(NR_ServingCellConfig_t *scd) { + + int b = 0; + while (b < scd->downlinkBWP_ToAddModList->list.count) { + if (scd->downlinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == 0) { + asn_sequence_del(&scd->downlinkBWP_ToAddModList->list, b, 1); + } else { + b++; + } + } + + if(scd->downlinkBWP_ToAddModList->list.count == 0) { + free(scd->downlinkBWP_ToAddModList); + scd->downlinkBWP_ToAddModList = NULL; + } + + b = 0; + while (b < scd->uplinkConfig->uplinkBWP_ToAddModList->list.count) { + if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[b]->bwp_Common->genericParameters.locationAndBandwidth == + 0) { + asn_sequence_del(&scd->uplinkConfig->uplinkBWP_ToAddModList->list, b, 1); + } else { + b++; + } + } + + if(scd->uplinkConfig->uplinkBWP_ToAddModList->list.count == 0) { + free(scd->uplinkConfig->uplinkBWP_ToAddModList); + scd->uplinkConfig->uplinkBWP_ToAddModList = NULL; + } + +} + +void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, + long bwp_loop, + NR_CellGroupConfig_t *secondaryCellGroup, + NR_ServingCellConfig_t *servingcellconfigdedicated, + NR_ServingCellConfigCommon_t *servingcellconfigcommon, + int dl_antenna_ports, + uint64_t bitmap) { + + /// BWP common configuration + + bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); + + if(secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && + bwp_loop < secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count) { + bwp->bwp_Id = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + bwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; + bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; + bwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + } else { + bwp->bwp_Id=bwp_loop+1; + bwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing; + bwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters.cyclicPrefix; + } + + bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); + bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet)); + + int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); + + NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); + coreset->controlResourceSetId=1; + // frequency domain resources depends on BWP size + // options are 24, 48 or 96 + coreset->frequencyDomainResources.buf = calloc(1,6); + if (curr_bwp < 48) + coreset->frequencyDomainResources.buf[0] = 0xf0; + else + coreset->frequencyDomainResources.buf[0] = 0xff; + if (curr_bwp < 96) + coreset->frequencyDomainResources.buf[1] = 0; + else + coreset->frequencyDomainResources.buf[1] = 0xff; + coreset->frequencyDomainResources.buf[2] = 0; + coreset->frequencyDomainResources.buf[3] = 0; + coreset->frequencyDomainResources.buf[4] = 0; + coreset->frequencyDomainResources.buf[5] = 0; + coreset->frequencyDomainResources.size = 6; + coreset->frequencyDomainResources.bits_unused = 3; + coreset->duration=1; + coreset->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; + coreset->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; + coreset->tci_StatesPDCCH_ToAddList=calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); + NR_TCI_StateId_t *tci[64]; + for (int i=0;i<64;i++) { + if ((bitmap>>(63-i))&0x01){ + tci[i]=calloc(1,sizeof(*tci[i])); + *tci[i] = i; + ASN_SEQUENCE_ADD(&coreset->tci_StatesPDCCH_ToAddList->list,tci[i]); + } + } + coreset->tci_StatesPDCCH_ToReleaseList = NULL; + coreset->tci_PresentInDCI = NULL; + coreset->pdcch_DMRS_ScramblingID = NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; + + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); + + NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); + ss->searchSpaceId = 1; + ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); + *ss->controlResourceSetId=1; + ss->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*ss->monitoringSlotPeriodicityAndOffset)); + ss->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; + ss->duration=NULL; + ss->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss->monitoringSymbolsWithinSlot)); + ss->monitoringSymbolsWithinSlot->buf = calloc(1,2); + // should be '1100 0000 0000 00'B (LSB first!), first two symols in slot, adjust if needed + ss->monitoringSymbolsWithinSlot->buf[1] = 0; + ss->monitoringSymbolsWithinSlot->buf[0] = (1<<7) | (1<<6); + ss->monitoringSymbolsWithinSlot->size = 2; + ss->monitoringSymbolsWithinSlot->bits_unused = 2; + ss->nrofCandidates = calloc(1,sizeof(*ss->nrofCandidates)); + ss->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; + ss->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; + ss->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; + ss->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; + ss->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; + ss->searchSpaceType = calloc(1,sizeof(*ss->searchSpaceType)); + ss->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_common; + ss->searchSpaceType->choice.common=calloc(1,sizeof(*ss->searchSpaceType->choice.common)); + ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0 = calloc(1,sizeof(*ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0)); + ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list,ss); + + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->pagingSearchSpace=NULL; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); + *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; + bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; + + bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); + bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; + bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); + bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); + + // copy PDSCH TimeDomainResourceAllocation from InitialBWP + NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; + for (int i=0;idownlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) { + pdschi= calloc(1,sizeof(*pdschi)); + if(servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0){ + pdschi->k0 = calloc(1,sizeof(*pdschi->k0)); + *pdschi->k0 = *servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0; + } + pdschi->mappingType = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->mappingType; + pdschi->startSymbolAndLength = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; + ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); + } + + /// BWP dedicated configuration + + if (!bwp->bwp_Dedicated) { + bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); + } + bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); + bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; + bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); + bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); + + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset); + + bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); + + NR_SearchSpace_t *ss2 = calloc(1,sizeof(*ss2)); + ss2->searchSpaceId=2; + ss2->controlResourceSetId=calloc(1,sizeof(*ss2->controlResourceSetId)); + *ss2->controlResourceSetId=1; + ss2->monitoringSlotPeriodicityAndOffset=calloc(1,sizeof(*ss2->monitoringSlotPeriodicityAndOffset)); + ss2->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; + ss2->monitoringSlotPeriodicityAndOffset->choice.sl1=(NULL_t)0; + ss2->duration=NULL; + ss2->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss2->monitoringSymbolsWithinSlot)); + ss2->monitoringSymbolsWithinSlot->buf = calloc(1,2); + ss2->monitoringSymbolsWithinSlot->size = 2; + ss2->monitoringSymbolsWithinSlot->bits_unused = 2; + ss2->monitoringSymbolsWithinSlot->buf[0]=0xc0; + ss2->monitoringSymbolsWithinSlot->buf[1]=0x0; + ss2->nrofCandidates=calloc(1,sizeof(*ss2->nrofCandidates)); + ss2->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; + ss2->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; + if (curr_bwp < 48) + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; + else if (curr_bwp < 96) + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n2; + else + ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n4; + ss2->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; + ss2->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; + ss2->searchSpaceType=calloc(1,sizeof(*ss2->searchSpaceType)); + ss2->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_ue_Specific; + ss2->searchSpaceType->choice.ue_Specific = calloc(1,sizeof(*ss2->searchSpaceType->choice.ue_Specific)); + ss2->searchSpaceType->choice.ue_Specific->dci_Formats=NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_1_And_1_1; + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss2); + + bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToReleaseList = NULL; + + if (!bwp->bwp_Dedicated->pdsch_Config) { + bwp->bwp_Dedicated->pdsch_Config = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); + bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; + bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); + } + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; + + if (dl_antenna_ports > 1) // for MIMO, we use DMRS Config Type 2 + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type)); + else + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID0=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID1=NULL; + if (!bwp->bwp_Dedicated->pdsch_Config) { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS=NULL; + } + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0; + + bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList)); + + int n_ssb = 0; + NR_TCI_State_t *tcid[64]; + for (int i=0;i<64;i++) { + if ((bitmap>>(63-i))&0x01){ + tcid[i]=calloc(1,sizeof(*tcid[i])); + tcid[i]->tci_StateId=n_ssb; + tcid[i]->qcl_Type1.cell=NULL; + tcid[i]->qcl_Type1.bwp_Id=calloc(1,sizeof(*tcid[i]->qcl_Type1.bwp_Id)); + *tcid[i]->qcl_Type1.bwp_Id=1; + tcid[i]->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; + tcid[i]->qcl_Type1.referenceSignal.choice.ssb = i; + tcid[i]->qcl_Type1.qcl_Type=NR_QCL_Info__qcl_Type_typeC; + ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList->list,tcid[i]); + n_ssb++; + } + } + + bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; + if (!get_softmodem_params()->use_256qam_table) { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; + } else { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = NR_PDSCH_Config__mcs_Table_qam256; + } + bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = + calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; + if (!get_softmodem_params()->use_256qam_table) { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; + } else { + bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = NR_PDSCH_Config__mcs_Table_qam256; + } + bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = + calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); + *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; + bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; + bwp->bwp_Dedicated->sps_Config = NULL; + bwp->bwp_Dedicated->radioLinkMonitoringConfig = NULL; + +#if 0 + bwp->bwp_Dedicated->radioLinkMonitoringConfig = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig)); + bwp->bwp_Dedicated->radioLinkMonitoringConfig->present = NR_SetupRelease_RadioLinkMonitoringConfig_PR_setup; + bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup)); + bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->failureDetectionResourcesToAddModList=NULL; + bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->failureDetectionResourcesToReleaseList=NULL; + bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureInstanceMaxCount = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureInstanceMaxCount)); + *bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureInstanceMaxCount = NR_RadioLinkMonitoringConfig__beamFailureInstanceMaxCount_n3; + bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer)); + *bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer = NR_RadioLinkMonitoringConfig__beamFailureDetectionTimer_pbfd2; +#endif + +} + +void fill_default_nsa_uplinkBWP(NR_BWP_Uplink_t *ubwp, + long bwp_loop, + NR_ServingCellConfig_t *servingcellconfigdedicated, + NR_ServingCellConfigCommon_t *servingcellconfigcommon, + int minRXTXTIME, + int do_csirs, + int do_srs, + int uid) { + + /// BWP common configuration + + ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); + + if(servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList && + bwp_loop < servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count) { + ubwp->bwp_Id = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + ubwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; + ubwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; + ubwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + } else { + ubwp->bwp_Id=bwp_loop+1; + ubwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(servingcellconfigcommon->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + ubwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; + ubwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters.cyclicPrefix; + } + + ubwp->bwp_Common->rach_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon; + ubwp->bwp_Common->pusch_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; + ubwp->bwp_Common->pucch_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; + + /// BWP dedicated configuration + + if (!ubwp->bwp_Dedicated) { + ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); + } + ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); + ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; + NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); + ubwp->bwp_Dedicated->pucch_Config->choice.setup=pucch_Config; + pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); + pucch_Config->resourceSetToReleaseList = NULL; + NR_PUCCH_ResourceSet_t *pucchresset0=calloc(1,sizeof(*pucchresset0)); + NR_PUCCH_ResourceSet_t *pucchresset1=calloc(1,sizeof(*pucchresset1)); + pucchresset0->pucch_ResourceSetId = 0; + NR_PUCCH_ResourceId_t *pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); + *pucchresset0id0=1; + ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); + pucchresset0->maxPayloadSize=NULL; + ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); + + pucchresset1->pucch_ResourceSetId = 1; + NR_PUCCH_ResourceId_t *pucchresset1id0=calloc(1,sizeof(*pucchresset1id0)); + *pucchresset1id0=2; + ASN_SEQUENCE_ADD(&pucchresset1->resourceList.list,pucchresset1id0); + pucchresset1->maxPayloadSize=NULL; + ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset1); + + pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); + pucch_Config->resourceToReleaseList = NULL; + NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); + NR_PUCCH_Resource_t *pucchres1=calloc(1,sizeof(*pucchres1)); + NR_PUCCH_Resource_t *pucchres2=calloc(1,sizeof(*pucchres2)); + NR_PUCCH_Resource_t *pucchres3=calloc(1,sizeof(*pucchres3)); + + int curr_bwp = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth,275); + + pucchres0->pucch_ResourceId=1; + pucchres0->startingPRB= (8 + uid) % curr_bwp; + pucchres0->intraSlotFrequencyHopping=NULL; + pucchres0->secondHopPRB=NULL; + pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; + pucchres0->format.choice.format0=calloc(1,sizeof(*pucchres0->format.choice.format0)); + pucchres0->format.choice.format0->initialCyclicShift=0; + pucchres0->format.choice.format0->nrofSymbols=1; + pucchres0->format.choice.format0->startingSymbolIndex=13; + ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); + + pucchres2->pucch_ResourceId=2; + pucchres2->startingPRB=0; + pucchres2->intraSlotFrequencyHopping=NULL; + pucchres2->secondHopPRB=NULL; + pucchres2->format.present= NR_PUCCH_Resource__format_PR_format2; + pucchres2->format.choice.format2=calloc(1,sizeof(*pucchres2->format.choice.format2)); + pucchres2->format.choice.format2->nrofPRBs=8; + pucchres2->format.choice.format2->nrofSymbols=1; + pucchres2->format.choice.format2->startingSymbolIndex=13; + ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres2); + + pucch_Config->format2=calloc(1,sizeof(*pucch_Config->format2)); + pucch_Config->format2->present=NR_SetupRelease_PUCCH_FormatConfig_PR_setup; + NR_PUCCH_FormatConfig_t *pucchfmt2 = calloc(1,sizeof(*pucchfmt2)); + pucch_Config->format2->choice.setup = pucchfmt2; + pucchfmt2->interslotFrequencyHopping=NULL; + pucchfmt2->additionalDMRS=NULL; + pucchfmt2->maxCodeRate=calloc(1,sizeof(*pucchfmt2->maxCodeRate)); + *pucchfmt2->maxCodeRate=NR_PUCCH_MaxCodeRate_zeroDot35; + pucchfmt2->nrofSlots=NULL; + pucchfmt2->pi2BPSK=NULL; + pucchfmt2->simultaneousHARQ_ACK_CSI=calloc(1,sizeof(*pucchfmt2->simultaneousHARQ_ACK_CSI)); + *pucchfmt2->simultaneousHARQ_ACK_CSI=NR_PUCCH_FormatConfig__simultaneousHARQ_ACK_CSI_true; + + // for scheduling requestresource + pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); + NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); + schedulingRequestResourceConfig->schedulingRequestResourceId = 1; + schedulingRequestResourceConfig->schedulingRequestID = 0; + schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); + schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl10; + schedulingRequestResourceConfig->periodicityAndOffset->choice.sl10 = 7; + schedulingRequestResourceConfig->resource = calloc(1,sizeof(*schedulingRequestResourceConfig->resource)); + *schedulingRequestResourceConfig->resource = 1; + ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); + + pucch_Config->schedulingRequestResourceToReleaseList=NULL; + pucch_Config->multi_CSI_PUCCH_ResourceList=NULL; + pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); + long *delay[8]; + for (int i=0;i<8;i++) { + delay[i] = calloc(1,sizeof(*delay[i])); + *delay[i] = i+minRXTXTIME; + ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); + } + pucch_Config->spatialRelationInfoToAddModList = calloc(1,sizeof(*pucch_Config->spatialRelationInfoToAddModList)); + NR_PUCCH_SpatialRelationInfo_t *pucchspatial = calloc(1,sizeof(*pucchspatial)); + pucchspatial->pucch_SpatialRelationInfoId = 1; + pucchspatial->servingCellId = NULL; + if(do_csirs) { + pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; + pucchspatial->referenceSignal.choice.csi_RS_Index = 0; + } + else { + pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_ssb_Index; + pucchspatial->referenceSignal.choice.ssb_Index = 0; + } + pucchspatial->pucch_PathlossReferenceRS_Id = 0; + pucchspatial->p0_PUCCH_Id = 1; + pucchspatial->closedLoopIndex = NR_PUCCH_SpatialRelationInfo__closedLoopIndex_i0; + ASN_SEQUENCE_ADD(&pucch_Config->spatialRelationInfoToAddModList->list,pucchspatial); + pucch_Config->spatialRelationInfoToReleaseList=NULL; + pucch_Config->pucch_PowerControl=calloc(1,sizeof(*pucch_Config->pucch_PowerControl)); + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = 0; + pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4)); + *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = 0; + pucch_Config->pucch_PowerControl->p0_Set = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->p0_Set)); + NR_P0_PUCCH_t *p00 = calloc(1,sizeof(*p00)); + p00->p0_PUCCH_Id=1; + p00->p0_PUCCH_Value = 0; + ASN_SEQUENCE_ADD(&pucch_Config->pucch_PowerControl->p0_Set->list,p00); + pucch_Config->pucch_PowerControl->pathlossReferenceRSs = NULL; + + if (!ubwp->bwp_Dedicated->pusch_Config) { + ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); + ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; + ubwp->bwp_Dedicated->pusch_Config->choice.setup = servingcellconfigdedicated->uplinkConfig->initialUplinkBWP->pusch_Config->choice.setup; + } + + ubwp->bwp_Dedicated->srs_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->srs_Config)); + ubwp->bwp_Dedicated->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; + NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); + ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; + srs_Config->srs_ResourceSetToReleaseList=NULL; + srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); + NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); + srs_resset0->srs_ResourceSetId = 0; + srs_resset0->srs_ResourceIdList=calloc(1,sizeof(*srs_resset0->srs_ResourceIdList)); + NR_SRS_ResourceId_t *srs_resset0_id=calloc(1,sizeof(*srs_resset0_id)); + *srs_resset0_id=0; + ASN_SEQUENCE_ADD(&srs_resset0->srs_ResourceIdList->list,srs_resset0_id); + srs_Config->srs_ResourceToReleaseList=NULL; + + if(do_srs) { + srs_resset0->resourceType.present = NR_SRS_ResourceSet__resourceType_PR_periodic; + srs_resset0->resourceType.choice.periodic = calloc(1,sizeof(*srs_resset0->resourceType.choice.periodic)); + srs_resset0->resourceType.choice.periodic->associatedCSI_RS = NULL; + } else { + srs_resset0->resourceType.present = NR_SRS_ResourceSet__resourceType_PR_aperiodic; + srs_resset0->resourceType.choice.aperiodic = calloc(1,sizeof(*srs_resset0->resourceType.choice.aperiodic)); + srs_resset0->resourceType.choice.aperiodic->aperiodicSRS_ResourceTrigger=1; + srs_resset0->resourceType.choice.aperiodic->csi_RS=NULL; + srs_resset0->resourceType.choice.aperiodic->slotOffset= calloc(1,sizeof(*srs_resset0->resourceType.choice.aperiodic->slotOffset)); + *srs_resset0->resourceType.choice.aperiodic->slotOffset=2; + srs_resset0->resourceType.choice.aperiodic->ext1=NULL; + } + + srs_resset0->usage=NR_SRS_ResourceSet__usage_codebook; + srs_resset0->alpha = calloc(1,sizeof(*srs_resset0->alpha)); + *srs_resset0->alpha = NR_Alpha_alpha1; + srs_resset0->p0=calloc(1,sizeof(*srs_resset0->p0)); + *srs_resset0->p0=-80; + srs_resset0->pathlossReferenceRS=NULL; + srs_resset0->srs_PowerControlAdjustmentStates=NULL; + ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceSetToAddModList->list,srs_resset0); + srs_Config->srs_ResourceToReleaseList=NULL; + srs_Config->srs_ResourceToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceToAddModList)); + NR_SRS_Resource_t *srs_res0=calloc(1,sizeof(*srs_res0)); + srs_res0->srs_ResourceId=0; + srs_res0->nrofSRS_Ports=NR_SRS_Resource__nrofSRS_Ports_port1; + srs_res0->ptrs_PortIndex=NULL; + srs_res0->transmissionComb.present=NR_SRS_Resource__transmissionComb_PR_n2; + srs_res0->transmissionComb.choice.n2=calloc(1,sizeof(*srs_res0->transmissionComb.choice.n2)); + srs_res0->transmissionComb.choice.n2->combOffset_n2=0; + srs_res0->transmissionComb.choice.n2->cyclicShift_n2=0; + srs_res0->resourceMapping.startPosition = 2 + uid%2; + srs_res0->resourceMapping.nrofSymbols=NR_SRS_Resource__resourceMapping__nrofSymbols_n1; + srs_res0->resourceMapping.repetitionFactor=NR_SRS_Resource__resourceMapping__repetitionFactor_n1; + srs_res0->freqDomainPosition=0; + srs_res0->freqDomainShift=0; + srs_res0->freqHopping.b_SRS=0; + srs_res0->freqHopping.b_hop=0; + srs_res0->freqHopping.c_SRS = rrc_get_max_nr_csrs( + NRRIV2BW(servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, 275), + srs_res0->freqHopping.b_SRS); + srs_res0->groupOrSequenceHopping=NR_SRS_Resource__groupOrSequenceHopping_neither; + + if(do_srs) { + srs_res0->resourceType.present= NR_SRS_Resource__resourceType_PR_periodic; + srs_res0->resourceType.choice.periodic=calloc(1,sizeof(*srs_res0->resourceType.choice.periodic)); + srs_res0->resourceType.choice.periodic->periodicityAndOffset_p.present = NR_SRS_PeriodicityAndOffset_PR_sl160; + srs_res0->resourceType.choice.periodic->periodicityAndOffset_p.choice.sl160 = 17 + (uid>1)*10; // 17/17/.../147/157 are mixed slots + } else { + srs_res0->resourceType.present= NR_SRS_Resource__resourceType_PR_aperiodic; + srs_res0->resourceType.choice.aperiodic=calloc(1,sizeof(*srs_res0->resourceType.choice.aperiodic)); + } + + srs_res0->sequenceId=40; + srs_res0->spatialRelationInfo=calloc(1,sizeof(*srs_res0->spatialRelationInfo)); + srs_res0->spatialRelationInfo->servingCellId=NULL; + srs_res0->spatialRelationInfo->referenceSignal.present=NR_SRS_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; + srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; + ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); + + ubwp->bwp_Dedicated->configuredGrantConfig = NULL; + ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; + +} + void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellconfigcommon, NR_ServingCellConfig_t *servingcellconfigdedicated, NR_CellGroupConfig_t *secondaryCellGroup, @@ -196,11 +762,15 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->rlmInSyncOutOfSyncThreshold = NULL; if (servingcellconfigdedicated) { + fix_servingcellconfigdedicated(servingcellconfigdedicated); secondaryCellGroup->spCellConfig->spCellConfigDedicated = servingcellconfigdedicated; } else { secondaryCellGroup->spCellConfig->spCellConfigDedicated = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated)); } secondaryCellGroup->spCellConfig->spCellConfigDedicated->tdd_UL_DL_ConfigurationDedicated = NULL; + + /// initialDownlinkBWP + secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdcch_Config=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config)); @@ -222,7 +792,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition)); *secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0; - secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList)); int n_ssb = 0; @@ -373,447 +942,127 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco *secondaryCellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer = NR_RadioLinkMonitoringConfig__beamFailureDetectionTimer_pbfd2; #endif - secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; - - NR_BWP_Downlink_t *bwp = NULL; + /// initialUplinkBWP - int n_bwp = 1; - if (servingcellconfigdedicated) { - n_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; + if (!secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); } - for (int bwp_loop = 0; bwp_loop < n_bwp; bwp_loop++) { + NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; + initialUplinkBWP->pucch_Config = NULL; + initialUplinkBWP->pusch_Config = calloc(1,sizeof(*initialUplinkBWP->pusch_Config)); + initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - /// Downlink BWPs - - if (servingcellconfigdedicated) { - bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; - } else { - if(secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList == NULL){ - secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); - } - bwp = calloc(1,sizeof(*bwp)); - bwp->bwp_Id = bwp_loop + 1; - bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); - // copy common BWP size from initial BWP except for bandwdith - memcpy((void*)&bwp->bwp_Common->genericParameters, - &servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters, - sizeof(bwp->bwp_Common->genericParameters)); - bwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - bwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - } - - bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); - bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero=NULL; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet)); - - int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); - - NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); - coreset->controlResourceSetId=1; - // frequency domain resources depends on BWP size - // options are 24, 48 or 96 - coreset->frequencyDomainResources.buf = calloc(1,6); - if (curr_bwp < 48) - coreset->frequencyDomainResources.buf[0] = 0xf0; - else - coreset->frequencyDomainResources.buf[0] = 0xff; - if (curr_bwp < 96) - coreset->frequencyDomainResources.buf[1] = 0; - else - coreset->frequencyDomainResources.buf[1] = 0xff; - coreset->frequencyDomainResources.buf[2] = 0; - coreset->frequencyDomainResources.buf[3] = 0; - coreset->frequencyDomainResources.buf[4] = 0; - coreset->frequencyDomainResources.buf[5] = 0; - coreset->frequencyDomainResources.size = 6; - coreset->frequencyDomainResources.bits_unused = 3; - coreset->duration=1; - coreset->cce_REG_MappingType.present = NR_ControlResourceSet__cce_REG_MappingType_PR_nonInterleaved; - coreset->precoderGranularity = NR_ControlResourceSet__precoderGranularity_sameAsREG_bundle; - - coreset->tci_StatesPDCCH_ToAddList=calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); - NR_TCI_StateId_t *tci[64]; - for (int i=0;i<64;i++) { - if ((bitmap>>(63-i))&0x01){ - tci[i]=calloc(1,sizeof(*tci[i])); - *tci[i] = i; - ASN_SEQUENCE_ADD(&coreset->tci_StatesPDCCH_ToAddList->list,tci[i]); - } - } - coreset->tci_StatesPDCCH_ToReleaseList = NULL; - coreset->tci_PresentInDCI = NULL; - coreset->pdcch_DMRS_ScramblingID = NULL; - - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; - - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=NULL; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); - - NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); - ss->searchSpaceId = 1; - ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); - *ss->controlResourceSetId=1; - ss->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*ss->monitoringSlotPeriodicityAndOffset)); - ss->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; - ss->duration=NULL; - ss->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss->monitoringSymbolsWithinSlot)); - ss->monitoringSymbolsWithinSlot->buf = calloc(1,2); - // should be '1100 0000 0000 00'B (LSB first!), first two symols in slot, adjust if needed - ss->monitoringSymbolsWithinSlot->buf[1] = 0; - ss->monitoringSymbolsWithinSlot->buf[0] = (1<<7) | (1<<6); - ss->monitoringSymbolsWithinSlot->size = 2; - ss->monitoringSymbolsWithinSlot->bits_unused = 2; - ss->nrofCandidates = calloc(1,sizeof(*ss->nrofCandidates)); - ss->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; - ss->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; - ss->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; - ss->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; - ss->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; - ss->searchSpaceType = calloc(1,sizeof(*ss->searchSpaceType)); - ss->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_common; - ss->searchSpaceType->choice.common=calloc(1,sizeof(*ss->searchSpaceType->choice.common)); - ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0 = calloc(1,sizeof(*ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0)); - - ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list,ss); - - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=NULL; - if(get_softmodem_params()->sa) { - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1)); - *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=0; - } - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation=NULL; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->pagingSearchSpace=NULL; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); - *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; - - bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); - bwp->bwp_Common->pdsch_ConfigCommon->present = NR_SetupRelease_PDSCH_ConfigCommon_PR_setup; - bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); - bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); - - // copy PDSCH TimeDomainResourceAllocation from InitialBWP - NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; - for (int i=0;idownlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) { - pdschi= calloc(1,sizeof(*pdschi)); - if(servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0){ - pdschi->k0 = calloc(1,sizeof(*pdschi->k0)); - *pdschi->k0 = *servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0; - } - pdschi->mappingType = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->mappingType; - pdschi->startSymbolAndLength = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; - ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); - } - - if (!servingcellconfigdedicated) { - bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); - } - - bwp->bwp_Dedicated->pdcch_Config=calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config)); - bwp->bwp_Dedicated->pdcch_Config->present = NR_SetupRelease_PDCCH_Config_PR_setup; - bwp->bwp_Dedicated->pdcch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup)); - bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); - - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset); - - bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); - - NR_SearchSpace_t *ss2 = calloc(1,sizeof(*ss2)); - - ss2->searchSpaceId=2; - ss2->controlResourceSetId=calloc(1,sizeof(*ss2->controlResourceSetId)); - *ss2->controlResourceSetId=1; - ss2->monitoringSlotPeriodicityAndOffset=calloc(1,sizeof(*ss2->monitoringSlotPeriodicityAndOffset)); - ss2->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; - ss2->monitoringSlotPeriodicityAndOffset->choice.sl1=(NULL_t)0; - ss2->duration=NULL; - ss2->monitoringSymbolsWithinSlot = calloc(1,sizeof(*ss2->monitoringSymbolsWithinSlot)); - ss2->monitoringSymbolsWithinSlot->buf = calloc(1,2); - ss2->monitoringSymbolsWithinSlot->size = 2; - ss2->monitoringSymbolsWithinSlot->bits_unused = 2; - ss2->monitoringSymbolsWithinSlot->buf[0]=0xc0; - ss2->monitoringSymbolsWithinSlot->buf[1]=0x0; - ss2->nrofCandidates=calloc(1,sizeof(*ss2->nrofCandidates)); - ss2->nrofCandidates->aggregationLevel1 = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; - ss2->nrofCandidates->aggregationLevel2 = NR_SearchSpace__nrofCandidates__aggregationLevel2_n0; - if (curr_bwp < 48) - ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n1; - else if (curr_bwp < 96) - ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n2; - else - ss2->nrofCandidates->aggregationLevel4 = NR_SearchSpace__nrofCandidates__aggregationLevel4_n4; - ss2->nrofCandidates->aggregationLevel8 = NR_SearchSpace__nrofCandidates__aggregationLevel8_n0; - ss2->nrofCandidates->aggregationLevel16 = NR_SearchSpace__nrofCandidates__aggregationLevel16_n0; - ss2->searchSpaceType=calloc(1,sizeof(*ss2->searchSpaceType)); - ss2->searchSpaceType->present = NR_SearchSpace__searchSpaceType_PR_ue_Specific; - ss2->searchSpaceType->choice.ue_Specific = calloc(1,sizeof(*ss2->searchSpaceType->choice.ue_Specific)); - ss2->searchSpaceType->choice.ue_Specific->dci_Formats=NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_1_And_1_1; - - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss2); - - bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToReleaseList = NULL; - - if (!servingcellconfigdedicated) { - bwp->bwp_Dedicated->pdsch_Config = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config)); - bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present= NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); - } - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dataScramblingIdentityPDSCH = NULL; - - if (dl_antenna_ports > 1)// for MIMO, we use DMRS Config Type 2 - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type)); - else - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->maxLength=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID0=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->scramblingID1=NULL; - if (!servingcellconfigdedicated) { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS=NULL; - } - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_AdditionalPosition = NR_DMRS_DownlinkConfig__dmrs_AdditionalPosition_pos0; - - bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList=calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList)); - - n_ssb = 0; - NR_TCI_State_t *tcid[64]; - for (int i=0;i<64;i++) { - if ((bitmap>>(63-i))&0x01){ - tcid[i]=calloc(1,sizeof(*tcid[i])); - tcid[i]->tci_StateId=n_ssb; - tcid[i]->qcl_Type1.cell=NULL; - tcid[i]->qcl_Type1.bwp_Id=calloc(1,sizeof(*tcid[i]->qcl_Type1.bwp_Id)); - *tcid[i]->qcl_Type1.bwp_Id=1; - tcid[i]->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; - tcid[i]->qcl_Type1.referenceSignal.choice.ssb = i; - tcid[i]->qcl_Type1.qcl_Type=NR_QCL_Info__qcl_Type_typeC; - ASN_SEQUENCE_ADD(&bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToAddModList->list,tcid[i]); - n_ssb++; - } - } - - bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; - if (!get_softmodem_params()->use_256qam_table) { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; - } else { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = NR_PDSCH_Config__mcs_Table_qam256; - } - bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = - calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->tci_StatesToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->vrb_ToPRB_Interleaver=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation=NR_PDSCH_Config__resourceAllocation_resourceAllocationType1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_TimeDomainAllocationList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->pdsch_AggregationFactor=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup1=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rateMatchPatternGroup2=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; - if (!get_softmodem_params()->use_256qam_table) { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table=NULL; - } else { - bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->mcs_Table = NR_PDSCH_Config__mcs_Table_qam256; - } - bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = NR_PDSCH_Config__maxNrofCodeWordsScheduledByDCI_n1; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.present = NR_PDSCH_Config__prb_BundlingType_PR_staticBundling; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = - calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize)); - *bwp->bwp_Dedicated->pdsch_Config->choice.setup->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->zp_CSI_RS_ResourceToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->aperiodic_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToAddModList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->sp_ZP_CSI_RS_ResourceSetsToReleaseList=NULL; - bwp->bwp_Dedicated->pdsch_Config->choice.setup->p_ZP_CSI_RS_ResourceSet=NULL; - - bwp->bwp_Dedicated->sps_Config = NULL; //calloc(1,sizeof(struct NR_SetupRelease_SPS_Config)); - bwp->bwp_Dedicated->radioLinkMonitoringConfig = NULL; -#if 0 - bwp->bwp_Dedicated->radioLinkMonitoringConfig = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig)); - bwp->bwp_Dedicated->radioLinkMonitoringConfig->present = NR_SetupRelease_RadioLinkMonitoringConfig_PR_setup; - - bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup)); - bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->failureDetectionResourcesToAddModList=NULL; - bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->failureDetectionResourcesToReleaseList=NULL; - bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureInstanceMaxCount = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureInstanceMaxCount)); - *bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureInstanceMaxCount = NR_RadioLinkMonitoringConfig__beamFailureInstanceMaxCount_n3; - bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer = calloc(1,sizeof(*bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer)); - *bwp->bwp_Dedicated->radioLinkMonitoringConfig->choice.setup->beamFailureDetectionTimer = NR_RadioLinkMonitoringConfig__beamFailureDetectionTimer_pbfd2; -#endif - - if (!servingcellconfigdedicated) { - ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); - } - - if (!servingcellconfigdedicated) { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id=1; - } - - secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; - - if (!servingcellconfigdedicated) { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; - } - - /// Uplink BWPs - - if (!servingcellconfigdedicated) { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig=calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig)); - } - - NR_BWP_UplinkDedicated_t *initialUplinkBWP = calloc(1,sizeof(*initialUplinkBWP)); - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP = initialUplinkBWP; - initialUplinkBWP->pucch_Config = NULL; - initialUplinkBWP->pusch_Config = calloc(1,sizeof(*initialUplinkBWP->pusch_Config)); - initialUplinkBWP->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - NR_PUSCH_Config_t *pusch_Config = NULL; - if (servingcellconfigdedicated) { - pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Dedicated->pusch_Config->choice.setup; - } else { - pusch_Config = calloc(1,sizeof(*pusch_Config)); - } - initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; - pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); - *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA = NULL; - if (!servingcellconfigdedicated) { - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); - } - NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; - NR_DMRS_UplinkConfig->dmrs_Type = NULL; - NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = calloc(1,sizeof(*NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)); - *NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos0; - if (!servingcellconfigdedicated) { - NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; - } - NR_DMRS_UplinkConfig->maxLength=NULL; - NR_DMRS_UplinkConfig->transformPrecodingDisabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingDisabled)); - NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0 = NULL; - NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID1 = NULL; - NR_DMRS_UplinkConfig->transformPrecodingEnabled = NULL; - pusch_Config->pusch_PowerControl = calloc(1,sizeof(*pusch_Config->pusch_PowerControl)); - pusch_Config->pusch_PowerControl->tpc_Accumulation = NULL; - pusch_Config->pusch_PowerControl->msg3_Alpha = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->msg3_Alpha)); - *pusch_Config->pusch_PowerControl->msg3_Alpha = NR_Alpha_alpha1; - pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = NULL; - pusch_Config->pusch_PowerControl->p0_AlphaSets = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->p0_AlphaSets)); - NR_P0_PUSCH_AlphaSet_t *aset = calloc(1,sizeof(*aset)); - aset->p0_PUSCH_AlphaSetId=0; - aset->p0=calloc(1,sizeof(*aset->p0)); - *aset->p0 = 0; - aset->alpha=calloc(1,sizeof(*aset->alpha)); - *aset->alpha=NR_Alpha_alpha1; - ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->p0_AlphaSets->list,aset); - pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList = NULL; - pusch_Config->pusch_PowerControl->pathlossReferenceRSToReleaseList = NULL; - pusch_Config->pusch_PowerControl->twoPUSCH_PC_AdjustmentStates = NULL; - pusch_Config->pusch_PowerControl->deltaMCS = NULL; - pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToAddModList = NULL; - pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToReleaseList = NULL; - pusch_Config->frequencyHopping=NULL; - pusch_Config->frequencyHoppingOffsetLists=NULL; - pusch_Config->resourceAllocation = NR_PUSCH_Config__resourceAllocation_resourceAllocationType1; - pusch_Config->pusch_TimeDomainAllocationList = NULL; - pusch_Config->pusch_AggregationFactor=NULL; - pusch_Config->mcs_Table=NULL; - pusch_Config->mcs_TableTransformPrecoder=NULL; - pusch_Config->transformPrecoder= NULL; - /* if msg3_transformprecoding is set in conf file - pusch config should not disable it */ - if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) { + NR_PUSCH_Config_t *pusch_Config = NULL; + if (servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList) { + pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[0]->bwp_Dedicated->pusch_Config->choice.setup; + } else { + pusch_Config = calloc(1,sizeof(*pusch_Config)); + } + initialUplinkBWP->pusch_Config->choice.setup = pusch_Config; + pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); + *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA = NULL; + if (!pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB) { + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); + } + NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + NR_DMRS_UplinkConfig->dmrs_Type = NULL; + NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = calloc(1,sizeof(*NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)); + *NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos0; + if (!servingcellconfigdedicated) { + NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; + } + NR_DMRS_UplinkConfig->maxLength=NULL; + NR_DMRS_UplinkConfig->transformPrecodingDisabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingDisabled)); + NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0 = NULL; + NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID1 = NULL; + NR_DMRS_UplinkConfig->transformPrecodingEnabled = NULL; + pusch_Config->pusch_PowerControl = calloc(1,sizeof(*pusch_Config->pusch_PowerControl)); + pusch_Config->pusch_PowerControl->tpc_Accumulation = NULL; + pusch_Config->pusch_PowerControl->msg3_Alpha = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->msg3_Alpha)); + *pusch_Config->pusch_PowerControl->msg3_Alpha = NR_Alpha_alpha1; + pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = NULL; + pusch_Config->pusch_PowerControl->p0_AlphaSets = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->p0_AlphaSets)); + NR_P0_PUSCH_AlphaSet_t *aset = calloc(1,sizeof(*aset)); + aset->p0_PUSCH_AlphaSetId=0; + aset->p0=calloc(1,sizeof(*aset->p0)); + *aset->p0 = 0; + aset->alpha=calloc(1,sizeof(*aset->alpha)); + *aset->alpha=NR_Alpha_alpha1; + ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->p0_AlphaSets->list,aset); + pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList = NULL; + pusch_Config->pusch_PowerControl->pathlossReferenceRSToReleaseList = NULL; + pusch_Config->pusch_PowerControl->twoPUSCH_PC_AdjustmentStates = NULL; + pusch_Config->pusch_PowerControl->deltaMCS = NULL; + pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToAddModList = NULL; + pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToReleaseList = NULL; + pusch_Config->frequencyHopping=NULL; + pusch_Config->frequencyHoppingOffsetLists=NULL; + pusch_Config->resourceAllocation = NR_PUSCH_Config__resourceAllocation_resourceAllocationType1; + pusch_Config->pusch_TimeDomainAllocationList = NULL; + pusch_Config->pusch_AggregationFactor=NULL; + pusch_Config->mcs_Table=NULL; + pusch_Config->mcs_TableTransformPrecoder=NULL; + pusch_Config->transformPrecoder= NULL; + /* if msg3_transformprecoding is set in conf file - pusch config should not disable it */ + if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) { pusch_Config->transformPrecoder=calloc(1,sizeof(*pusch_Config->transformPrecoder)); *pusch_Config->transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; - } - pusch_Config->codebookSubset=calloc(1,sizeof(*pusch_Config->codebookSubset)); - *pusch_Config->codebookSubset = NR_PUSCH_Config__codebookSubset_nonCoherent; - pusch_Config->maxRank=calloc(1,sizeof(*pusch_Config->maxRank)); - *pusch_Config->maxRank= 1; - pusch_Config->rbg_Size=NULL; - pusch_Config->uci_OnPUSCH=NULL; - pusch_Config->tp_pi2BPSK=NULL; + } + pusch_Config->codebookSubset=calloc(1,sizeof(*pusch_Config->codebookSubset)); + *pusch_Config->codebookSubset = NR_PUSCH_Config__codebookSubset_nonCoherent; + pusch_Config->maxRank=calloc(1,sizeof(*pusch_Config->maxRank)); + *pusch_Config->maxRank= 1; + pusch_Config->rbg_Size=NULL; + pusch_Config->uci_OnPUSCH=NULL; + pusch_Config->tp_pi2BPSK=NULL; - /*------------------------------TRANSFORM PRECODING- -----------------------------------------------------------------------*/ + uint8_t transform_precoding = NR_PUSCH_Config__transformPrecoder_disabled; - uint8_t transform_precoding = NR_PUSCH_Config__transformPrecoder_disabled; + // TBD: configure this from .conf file, Dedicated params cannot yet be configured in .conf file. + // Enable this to test transform precoding enabled from dedicated config. + /*if (pusch_Config->transformPrecoder == NULL) + pusch_Config->transformPrecoder=calloc(1,sizeof(*pusch_Config->transformPrecoder)); - // TBD: configure this from .conf file, Dedicated params cannot yet be configured in .conf file. - // Enable this to test transform precoding enabled from dedicated config. - /*if (pusch_Config->transformPrecoder == NULL) - pusch_Config->transformPrecoder=calloc(1,sizeof(*pusch_Config->transformPrecoder)); - - *pusch_Config->transformPrecoder = NR_PUSCH_Config__transformPrecoder_enabled; */ + *pusch_Config->transformPrecoder = NR_PUSCH_Config__transformPrecoder_enabled; */ // END ------- - if (pusch_Config->transformPrecoder == NULL) { - if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder != NULL) - transform_precoding = NR_PUSCH_Config__transformPrecoder_enabled; - } - else + if (pusch_Config->transformPrecoder == NULL) { + if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder != NULL) + transform_precoding = NR_PUSCH_Config__transformPrecoder_enabled; + } + else { transform_precoding = *pusch_Config->transformPrecoder; + } - if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled ) { + if (transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled ) { /* Enable DMRS uplink config for transform precoding enabled */ NR_DMRS_UplinkConfig->transformPrecodingEnabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingEnabled)); NR_DMRS_UplinkConfig->transformPrecodingEnabled->nPUSCH_Identity = NULL; NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceGroupHopping = NULL; NR_DMRS_UplinkConfig->transformPrecodingEnabled->sequenceHopping = NULL; NR_DMRS_UplinkConfig->transformPrecodingEnabled->ext1 = NULL; - LOG_I(RRC,"TRANSFORM PRECODING ENABLED......\n"); - } - /*----------------------------------------------------------------------------------------------------------------------------*/ - initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); - initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; - NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); - initialUplinkBWP->srs_Config->choice.setup=srs_Config; - srs_Config->srs_ResourceSetToReleaseList=NULL; - srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); - NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); - srs_resset0->srs_ResourceSetId = 0; - srs_resset0->srs_ResourceIdList=calloc(1,sizeof(*srs_resset0->srs_ResourceIdList)); - NR_SRS_ResourceId_t *srs_resset0_id=calloc(1,sizeof(*srs_resset0_id)); - *srs_resset0_id=0; - ASN_SEQUENCE_ADD(&srs_resset0->srs_ResourceIdList->list,srs_resset0_id); - srs_Config->srs_ResourceToReleaseList=NULL; + initialUplinkBWP->srs_Config = calloc(1,sizeof(*initialUplinkBWP->srs_Config)); + initialUplinkBWP->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; + + NR_SRS_Config_t *srs_Config = calloc(1,sizeof(*srs_Config)); + initialUplinkBWP->srs_Config->choice.setup=srs_Config; + srs_Config->srs_ResourceSetToReleaseList=NULL; + srs_Config->srs_ResourceSetToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceSetToAddModList)); + NR_SRS_ResourceSet_t *srs_resset0=calloc(1,sizeof(*srs_resset0)); + srs_resset0->srs_ResourceSetId = 0; + srs_resset0->srs_ResourceIdList=calloc(1,sizeof(*srs_resset0->srs_ResourceIdList)); + NR_SRS_ResourceId_t *srs_resset0_id=calloc(1,sizeof(*srs_resset0_id)); + *srs_resset0_id=0; + ASN_SEQUENCE_ADD(&srs_resset0->srs_ResourceIdList->list,srs_resset0_id); + srs_Config->srs_ResourceToReleaseList=NULL; if(do_srs) { srs_resset0->resourceType.present = NR_SRS_ResourceSet__resourceType_PR_periodic; @@ -829,35 +1078,35 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_resset0->resourceType.choice.aperiodic->ext1=NULL; } - srs_resset0->usage=NR_SRS_ResourceSet__usage_codebook; - srs_resset0->alpha = calloc(1,sizeof(*srs_resset0->alpha)); - *srs_resset0->alpha = NR_Alpha_alpha1; - srs_resset0->p0=calloc(1,sizeof(*srs_resset0->p0)); - *srs_resset0->p0=-80; - srs_resset0->pathlossReferenceRS=NULL; - srs_resset0->srs_PowerControlAdjustmentStates=NULL; - ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceSetToAddModList->list,srs_resset0); - srs_Config->srs_ResourceToReleaseList=NULL; - srs_Config->srs_ResourceToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceToAddModList)); - NR_SRS_Resource_t *srs_res0=calloc(1,sizeof(*srs_res0)); - srs_res0->srs_ResourceId=0; - srs_res0->nrofSRS_Ports=NR_SRS_Resource__nrofSRS_Ports_port1; - srs_res0->ptrs_PortIndex=NULL; - srs_res0->transmissionComb.present=NR_SRS_Resource__transmissionComb_PR_n2; - srs_res0->transmissionComb.choice.n2=calloc(1,sizeof(*srs_res0->transmissionComb.choice.n2)); - srs_res0->transmissionComb.choice.n2->combOffset_n2=0; - srs_res0->transmissionComb.choice.n2->cyclicShift_n2=0; - srs_res0->resourceMapping.startPosition = 2 + uid%2; - srs_res0->resourceMapping.nrofSymbols=NR_SRS_Resource__resourceMapping__nrofSymbols_n1; - srs_res0->resourceMapping.repetitionFactor=NR_SRS_Resource__resourceMapping__repetitionFactor_n1; - srs_res0->freqDomainPosition=0; - srs_res0->freqDomainShift=0; - srs_res0->freqHopping.b_SRS=0; - srs_res0->freqHopping.b_hop=0; - srs_res0->freqHopping.c_SRS = rrc_get_max_nr_csrs( - NRRIV2BW(servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, 275), - srs_res0->freqHopping.b_SRS); - srs_res0->groupOrSequenceHopping=NR_SRS_Resource__groupOrSequenceHopping_neither; + srs_resset0->usage=NR_SRS_ResourceSet__usage_codebook; + srs_resset0->alpha = calloc(1,sizeof(*srs_resset0->alpha)); + *srs_resset0->alpha = NR_Alpha_alpha1; + srs_resset0->p0=calloc(1,sizeof(*srs_resset0->p0)); + *srs_resset0->p0=-80; + srs_resset0->pathlossReferenceRS=NULL; + srs_resset0->srs_PowerControlAdjustmentStates=NULL; + ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceSetToAddModList->list,srs_resset0); + srs_Config->srs_ResourceToReleaseList=NULL; + srs_Config->srs_ResourceToAddModList=calloc(1,sizeof(*srs_Config->srs_ResourceToAddModList)); + NR_SRS_Resource_t *srs_res0=calloc(1,sizeof(*srs_res0)); + srs_res0->srs_ResourceId=0; + srs_res0->nrofSRS_Ports=NR_SRS_Resource__nrofSRS_Ports_port1; + srs_res0->ptrs_PortIndex=NULL; + srs_res0->transmissionComb.present=NR_SRS_Resource__transmissionComb_PR_n2; + srs_res0->transmissionComb.choice.n2=calloc(1,sizeof(*srs_res0->transmissionComb.choice.n2)); + srs_res0->transmissionComb.choice.n2->combOffset_n2=0; + srs_res0->transmissionComb.choice.n2->cyclicShift_n2=0; + srs_res0->resourceMapping.startPosition = 2 + uid%2; + srs_res0->resourceMapping.nrofSymbols=NR_SRS_Resource__resourceMapping__nrofSymbols_n1; + srs_res0->resourceMapping.repetitionFactor=NR_SRS_Resource__resourceMapping__repetitionFactor_n1; + srs_res0->freqDomainPosition=0; + srs_res0->freqDomainShift=0; + srs_res0->freqHopping.b_SRS=0; + srs_res0->freqHopping.b_hop=0; + srs_res0->freqHopping.c_SRS = rrc_get_max_nr_csrs( + NRRIV2BW(servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, 275), + srs_res0->freqHopping.b_SRS); + srs_res0->groupOrSequenceHopping=NR_SRS_Resource__groupOrSequenceHopping_neither; if(do_srs) { srs_res0->resourceType.present= NR_SRS_Resource__resourceType_PR_periodic; @@ -869,190 +1118,67 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco srs_res0->resourceType.choice.aperiodic=calloc(1,sizeof(*srs_res0->resourceType.choice.aperiodic)); } - srs_res0->sequenceId=40; - srs_res0->spatialRelationInfo=calloc(1,sizeof(*srs_res0->spatialRelationInfo)); - srs_res0->spatialRelationInfo->servingCellId=NULL; - srs_res0->spatialRelationInfo->referenceSignal.present=NR_SRS_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; - srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; - ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); + srs_res0->sequenceId=40; + srs_res0->spatialRelationInfo=calloc(1,sizeof(*srs_res0->spatialRelationInfo)); + srs_res0->spatialRelationInfo->servingCellId=NULL; + srs_res0->spatialRelationInfo->referenceSignal.present=NR_SRS_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; + srs_res0->spatialRelationInfo->referenceSignal.choice.csi_RS_Index=0; + ASN_SEQUENCE_ADD(&srs_Config->srs_ResourceToAddModList->list,srs_res0); - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; - NR_BWP_Uplink_t *ubwp = NULL; - if (servingcellconfigdedicated) { - ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; - } else { - if (secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList == NULL) { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); - } - ubwp = calloc(1,sizeof(*ubwp)); - ubwp->bwp_Id = bwp_loop + 1; + /// Dedicated BWPs - ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); - - // copy bwp_Common from Initial UL BWP except for bandwidth - memcpy((void*)&ubwp->bwp_Common->genericParameters, - (void*)&servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters, - sizeof(servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->genericParameters)); - ubwp->bwp_Common->genericParameters.subcarrierSpacing = 1; - ubwp->bwp_Common->genericParameters.locationAndBandwidth=PRBalloc_to_locationandbandwidth(servingcellconfigcommon->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); - } - - ubwp->bwp_Common->rach_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon; - ubwp->bwp_Common->pusch_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; - ubwp->bwp_Common->pucch_ConfigCommon = servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; - - if (!servingcellconfigdedicated) { - ubwp->bwp_Dedicated = calloc(1,sizeof(*ubwp->bwp_Dedicated)); - } - - ubwp->bwp_Dedicated->pucch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pucch_Config)); - ubwp->bwp_Dedicated->pucch_Config->present = NR_SetupRelease_PUCCH_Config_PR_setup; - NR_PUCCH_Config_t *pucch_Config = calloc(1,sizeof(*pucch_Config)); - ubwp->bwp_Dedicated->pucch_Config->choice.setup=pucch_Config; - pucch_Config->resourceSetToAddModList = calloc(1,sizeof(*pucch_Config->resourceSetToAddModList)); - pucch_Config->resourceSetToReleaseList = NULL; - NR_PUCCH_ResourceSet_t *pucchresset0=calloc(1,sizeof(*pucchresset0)); - NR_PUCCH_ResourceSet_t *pucchresset1=calloc(1,sizeof(*pucchresset1)); - pucchresset0->pucch_ResourceSetId = 0; - NR_PUCCH_ResourceId_t *pucchresset0id0=calloc(1,sizeof(*pucchresset0id0)); - *pucchresset0id0=1; - ASN_SEQUENCE_ADD(&pucchresset0->resourceList.list,pucchresset0id0); - pucchresset0->maxPayloadSize=NULL; - - ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset0); - - pucchresset1->pucch_ResourceSetId = 1; - NR_PUCCH_ResourceId_t *pucchresset1id0=calloc(1,sizeof(*pucchresset1id0)); - *pucchresset1id0=2; - ASN_SEQUENCE_ADD(&pucchresset1->resourceList.list,pucchresset1id0); - pucchresset1->maxPayloadSize=NULL; - ASN_SEQUENCE_ADD(&pucch_Config->resourceSetToAddModList->list,pucchresset1); - - pucch_Config->resourceToAddModList = calloc(1,sizeof(*pucch_Config->resourceToAddModList)); - pucch_Config->resourceToReleaseList = NULL; - NR_PUCCH_Resource_t *pucchres0=calloc(1,sizeof(*pucchres0)); - NR_PUCCH_Resource_t *pucchres1=calloc(1,sizeof(*pucchres1)); - NR_PUCCH_Resource_t *pucchres2=calloc(1,sizeof(*pucchres2)); - NR_PUCCH_Resource_t *pucchres3=calloc(1,sizeof(*pucchres3)); - - pucchres0->pucch_ResourceId=1; - pucchres0->startingPRB= (8 + uid) % curr_bwp; - pucchres0->intraSlotFrequencyHopping=NULL; - pucchres0->secondHopPRB=NULL; - pucchres0->format.present= NR_PUCCH_Resource__format_PR_format0; - pucchres0->format.choice.format0=calloc(1,sizeof(*pucchres0->format.choice.format0)); - pucchres0->format.choice.format0->initialCyclicShift=0; - pucchres0->format.choice.format0->nrofSymbols=1; - pucchres0->format.choice.format0->startingSymbolIndex=13; - ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres0); - - pucchres2->pucch_ResourceId=2; - pucchres2->startingPRB=0; - pucchres2->intraSlotFrequencyHopping=NULL; - pucchres2->secondHopPRB=NULL; - pucchres2->format.present= NR_PUCCH_Resource__format_PR_format2; - pucchres2->format.choice.format2=calloc(1,sizeof(*pucchres2->format.choice.format2)); - pucchres2->format.choice.format2->nrofPRBs=8; - pucchres2->format.choice.format2->nrofSymbols=1; - pucchres2->format.choice.format2->startingSymbolIndex=13; - ASN_SEQUENCE_ADD(&pucch_Config->resourceToAddModList->list,pucchres2); - - pucch_Config->format2=calloc(1,sizeof(*pucch_Config->format2)); - pucch_Config->format2->present=NR_SetupRelease_PUCCH_FormatConfig_PR_setup; - NR_PUCCH_FormatConfig_t *pucchfmt2 = calloc(1,sizeof(*pucchfmt2)); - pucch_Config->format2->choice.setup = pucchfmt2; - pucchfmt2->interslotFrequencyHopping=NULL; - pucchfmt2->additionalDMRS=NULL; - pucchfmt2->maxCodeRate=calloc(1,sizeof(*pucchfmt2->maxCodeRate)); - *pucchfmt2->maxCodeRate=NR_PUCCH_MaxCodeRate_zeroDot35; - pucchfmt2->nrofSlots=NULL; - pucchfmt2->pi2BPSK=NULL; - pucchfmt2->simultaneousHARQ_ACK_CSI=calloc(1,sizeof(*pucchfmt2->simultaneousHARQ_ACK_CSI)); - *pucchfmt2->simultaneousHARQ_ACK_CSI=NR_PUCCH_FormatConfig__simultaneousHARQ_ACK_CSI_true; - - // for scheduling requestresource - pucch_Config->schedulingRequestResourceToAddModList = calloc(1,sizeof(*pucch_Config->schedulingRequestResourceToAddModList)); - NR_SchedulingRequestResourceConfig_t *schedulingRequestResourceConfig = calloc(1,sizeof(*schedulingRequestResourceConfig)); - schedulingRequestResourceConfig->schedulingRequestResourceId = 1; - schedulingRequestResourceConfig->schedulingRequestID = 0; - schedulingRequestResourceConfig->periodicityAndOffset = calloc(1,sizeof(*schedulingRequestResourceConfig->periodicityAndOffset)); - schedulingRequestResourceConfig->periodicityAndOffset->present = NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl10; - schedulingRequestResourceConfig->periodicityAndOffset->choice.sl10 = 7; - schedulingRequestResourceConfig->resource = calloc(1,sizeof(*schedulingRequestResourceConfig->resource)); - *schedulingRequestResourceConfig->resource = 1; - ASN_SEQUENCE_ADD(&pucch_Config->schedulingRequestResourceToAddModList->list,schedulingRequestResourceConfig); - - pucch_Config->schedulingRequestResourceToReleaseList=NULL; - pucch_Config->multi_CSI_PUCCH_ResourceList=NULL; - pucch_Config->dl_DataToUL_ACK = calloc(1,sizeof(*pucch_Config->dl_DataToUL_ACK)); - long *delay[8]; - for (int i=0;i<8;i++) { - delay[i] = calloc(1,sizeof(*delay[i])); - *delay[i] = i+minRXTXTIME; - ASN_SEQUENCE_ADD(&pucch_Config->dl_DataToUL_ACK->list,delay[i]); - } - pucch_Config->spatialRelationInfoToAddModList = calloc(1,sizeof(*pucch_Config->spatialRelationInfoToAddModList)); - NR_PUCCH_SpatialRelationInfo_t *pucchspatial = calloc(1,sizeof(*pucchspatial)); - pucchspatial->pucch_SpatialRelationInfoId = 1; - pucchspatial->servingCellId = NULL; - if(do_csirs) { - pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_csi_RS_Index; - pucchspatial->referenceSignal.choice.csi_RS_Index = 0; - } - else { - pucchspatial->referenceSignal.present = NR_PUCCH_SpatialRelationInfo__referenceSignal_PR_ssb_Index; - pucchspatial->referenceSignal.choice.ssb_Index = 0; - } - pucchspatial->pucch_PathlossReferenceRS_Id = 0; - pucchspatial->p0_PUCCH_Id = 1; - pucchspatial->closedLoopIndex = NR_PUCCH_SpatialRelationInfo__closedLoopIndex_i0; - ASN_SEQUENCE_ADD(&pucch_Config->spatialRelationInfoToAddModList->list,pucchspatial); - pucch_Config->spatialRelationInfoToReleaseList=NULL; - pucch_Config->pucch_PowerControl=calloc(1,sizeof(*pucch_Config->pucch_PowerControl)); - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f0 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f1 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f2 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f3 = 0; - pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4)); - *pucch_Config->pucch_PowerControl->deltaF_PUCCH_f4 = 0; - pucch_Config->pucch_PowerControl->p0_Set = calloc(1,sizeof(*pucch_Config->pucch_PowerControl->p0_Set)); - NR_P0_PUCCH_t *p00 = calloc(1,sizeof(*p00)); - p00->p0_PUCCH_Id=1; - p00->p0_PUCCH_Value = 0; - ASN_SEQUENCE_ADD(&pucch_Config->pucch_PowerControl->p0_Set->list,p00); - pucch_Config->pucch_PowerControl->pathlossReferenceRSs = NULL; - - if (!servingcellconfigdedicated) { - // copy pusch_Config from dedicated initialBWP - ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); - ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; - } - ubwp->bwp_Dedicated->configuredGrantConfig = NULL; - ubwp->bwp_Dedicated->srs_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->srs_Config)); - ubwp->bwp_Dedicated->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; - ubwp->bwp_Dedicated->srs_Config->choice.setup = srs_Config; - - ubwp->bwp_Dedicated->beamFailureRecoveryConfig = NULL; - - if (!servingcellconfigdedicated) { - ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); - } - } - - if (!servingcellconfigdedicated) { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; + // Downlink BWPs + int n_dl_bwp = 1; + if (servingcellconfigdedicated && servingcellconfigdedicated->downlinkBWP_ToAddModList) { + n_dl_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; } + if(n_dl_bwp>0) { + for (int bwp_loop = 0; bwp_loop < n_dl_bwp; bwp_loop++) { + NR_BWP_Downlink_t *bwp = NULL; + if (servingcellconfigdedicated && servingcellconfigdedicated->downlinkBWP_ToAddModList) { + bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; + } else { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); + bwp=calloc(1,sizeof(*bwp)); + } + fill_default_nsa_downlinkBWP(bwp, bwp_loop, secondaryCellGroup, servingcellconfigdedicated, servingcellconfigcommon, dl_antenna_ports, bitmap); + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); + secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = 1; + secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; + } + } + + // Uplink BWPs + int n_ul_bwp = 1; + if (servingcellconfigdedicated && servingcellconfigdedicated->uplinkConfig && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList) { + n_ul_bwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; + } + if(n_ul_bwp>0) { + for (int bwp_loop = 0; bwp_loop < n_ul_bwp; bwp_loop++) { + NR_BWP_Uplink_t *ubwp = NULL; + if (servingcellconfigdedicated && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList) { + ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; + } else { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); + ubwp = calloc(1,sizeof(*ubwp)); + } + fill_default_nsa_uplinkBWP(ubwp, bwp_loop, servingcellconfigdedicated, servingcellconfigcommon, minRXTXTIME, do_csirs, do_srs, uid); + ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; + } + } + + secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; + secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToReleaseList= NULL; + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToReleaseList = NULL; + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig)); NR_PUSCH_ServingCellConfig_t *pusch_scc = calloc(1,sizeof(*pusch_scc)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->present = NR_SetupRelease_PUSCH_ServingCellConfig_PR_setup; secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup = pusch_scc; - pusch_scc->codeBlockGroupTransmission = NULL; pusch_scc->rateMatching = NULL; pusch_scc->xOverhead = NULL; @@ -1305,10 +1431,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ASN_SEQUENCE_ADD(&csirep2->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list,pucchcsires1); csirep2->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP; csirep2->reportQuantity.choice.ssb_Index_RSRP=(NULL_t)0; - csirep2->reportFreqConfiguration = calloc(1,sizeof(*csirep2->reportFreqConfiguration)); - csirep2->reportFreqConfiguration->cqi_FormatIndicator=NR_CSI_ReportConfig__reportFreqConfiguration__cqi_FormatIndicator_widebandCQI; - csirep2->reportFreqConfiguration->pmi_FormatIndicator=NR_CSI_ReportConfig__reportFreqConfiguration__pmi_FormatIndicator_widebandPMI; - csirep2->reportFreqConfiguration->csi_ReportingBand=NULL; + csirep2->reportFreqConfiguration = NULL; csirep2->timeRestrictionForChannelMeasurements= NR_CSI_ReportConfig__timeRestrictionForChannelMeasurements_configured; csirep2->timeRestrictionForInterferenceMeasurements=NR_CSI_ReportConfig__timeRestrictionForInterferenceMeasurements_configured; csirep2->codebookConfig= NULL; @@ -1332,6 +1455,9 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->pathlossReferenceLinking=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->servingCellMO=NULL; + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_SpCellConfig, (void *)secondaryCellGroup->spCellConfig); + } } From 99d158867076c36040025b4a28276439f80da33a Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 18 Feb 2022 00:28:44 +0000 Subject: [PATCH 101/233] Fix phy-test --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 12 +-- openair2/RRC/NR/rrc_gNB_reconfig.c | 118 ++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 39 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 0c0849e362..404c1b2fe2 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1882,11 +1882,11 @@ void fill_initial_SpCellConfig(rnti_t rnti, NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); fill_default_downlinkBWP(bwp, bwp_loop, servingcellconfigdedicated, scc, carrier); ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); - SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = servingcellconfigdedicated->firstActiveDownlinkBWP_Id ? *servingcellconfigdedicated->firstActiveDownlinkBWP_Id : 1; - SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = servingcellconfigdedicated->defaultDownlinkBWP_Id ? *servingcellconfigdedicated->defaultDownlinkBWP_Id : 1; } + SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = servingcellconfigdedicated->firstActiveDownlinkBWP_Id ? *servingcellconfigdedicated->firstActiveDownlinkBWP_Id : 1; + SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = servingcellconfigdedicated->defaultDownlinkBWP_Id ? *servingcellconfigdedicated->defaultDownlinkBWP_Id : 1; } // Uplink BWPs @@ -1900,9 +1900,9 @@ void fill_initial_SpCellConfig(rnti_t rnti, NR_BWP_Uplink_t *ubwp = calloc(1, sizeof(*ubwp)); fill_default_uplinkBWP(ubwp, bwp_loop, servingcellconfigdedicated, scc, carrier, uid); ASN_SEQUENCE_ADD(&SpCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list, ubwp); - SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id ? *servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id : 1; } + SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *SpCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id ? *servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id : 1; } if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 7b9302bda2..9736cf8db3 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -94,8 +94,8 @@ void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); - if(secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && - bwp_loop < secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count) { + if(servingcellconfigdedicated->downlinkBWP_ToAddModList && + bwp_loop < servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count) { bwp->bwp_Id = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; bwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; @@ -529,11 +529,75 @@ void fill_default_nsa_uplinkBWP(NR_BWP_Uplink_t *ubwp, ASN_SEQUENCE_ADD(&pucch_Config->pucch_PowerControl->p0_Set->list,p00); pucch_Config->pucch_PowerControl->pathlossReferenceRSs = NULL; - if (!ubwp->bwp_Dedicated->pusch_Config) { - ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); - ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - ubwp->bwp_Dedicated->pusch_Config->choice.setup = servingcellconfigdedicated->uplinkConfig->initialUplinkBWP->pusch_Config->choice.setup; + ubwp->bwp_Dedicated->pusch_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->pusch_Config)); + ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; + NR_PUSCH_Config_t *pusch_Config = NULL; + if(servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList && + bwp_loop < servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count) { + pusch_Config = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Dedicated->pusch_Config->choice.setup; + } else { + pusch_Config = calloc(1,sizeof(*pusch_Config)); } + ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; + pusch_Config->txConfig=calloc(1,sizeof(*pusch_Config->txConfig)); + *pusch_Config->txConfig= NR_PUSCH_Config__txConfig_codebook; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA = NULL; + if (!pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB) { + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = calloc(1,sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); + } + NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + NR_DMRS_UplinkConfig->dmrs_Type = NULL; + NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = calloc(1,sizeof(*NR_DMRS_UplinkConfig->dmrs_AdditionalPosition)); + *NR_DMRS_UplinkConfig->dmrs_AdditionalPosition = NR_DMRS_UplinkConfig__dmrs_AdditionalPosition_pos0; + if (!servingcellconfigdedicated) { + NR_DMRS_UplinkConfig->phaseTrackingRS=NULL; + } + NR_DMRS_UplinkConfig->maxLength=NULL; + NR_DMRS_UplinkConfig->transformPrecodingDisabled = calloc(1,sizeof(*NR_DMRS_UplinkConfig->transformPrecodingDisabled)); + NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0 = NULL; + NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID1 = NULL; + NR_DMRS_UplinkConfig->transformPrecodingEnabled = NULL; + pusch_Config->pusch_PowerControl = calloc(1,sizeof(*pusch_Config->pusch_PowerControl)); + pusch_Config->pusch_PowerControl->tpc_Accumulation = NULL; + pusch_Config->pusch_PowerControl->msg3_Alpha = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->msg3_Alpha)); + *pusch_Config->pusch_PowerControl->msg3_Alpha = NR_Alpha_alpha1; + pusch_Config->pusch_PowerControl->p0_NominalWithoutGrant = NULL; + pusch_Config->pusch_PowerControl->p0_AlphaSets = calloc(1,sizeof(*pusch_Config->pusch_PowerControl->p0_AlphaSets)); + NR_P0_PUSCH_AlphaSet_t *aset = calloc(1,sizeof(*aset)); + aset->p0_PUSCH_AlphaSetId=0; + aset->p0=calloc(1,sizeof(*aset->p0)); + *aset->p0 = 0; + aset->alpha=calloc(1,sizeof(*aset->alpha)); + *aset->alpha=NR_Alpha_alpha1; + ASN_SEQUENCE_ADD(&pusch_Config->pusch_PowerControl->p0_AlphaSets->list,aset); + pusch_Config->pusch_PowerControl->pathlossReferenceRSToAddModList = NULL; + pusch_Config->pusch_PowerControl->pathlossReferenceRSToReleaseList = NULL; + pusch_Config->pusch_PowerControl->twoPUSCH_PC_AdjustmentStates = NULL; + pusch_Config->pusch_PowerControl->deltaMCS = NULL; + pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToAddModList = NULL; + pusch_Config->pusch_PowerControl->sri_PUSCH_MappingToReleaseList = NULL; + pusch_Config->frequencyHopping=NULL; + pusch_Config->frequencyHoppingOffsetLists=NULL; + pusch_Config->resourceAllocation = NR_PUSCH_Config__resourceAllocation_resourceAllocationType1; + pusch_Config->pusch_TimeDomainAllocationList = NULL; + pusch_Config->pusch_AggregationFactor=NULL; + pusch_Config->mcs_Table=NULL; + pusch_Config->mcs_TableTransformPrecoder=NULL; + pusch_Config->transformPrecoder= NULL; + /* if msg3_transformprecoding is set in conf file - pusch config should not disable it */ + if (servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) { + pusch_Config->transformPrecoder=calloc(1,sizeof(*pusch_Config->transformPrecoder)); + *pusch_Config->transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; + } + pusch_Config->codebookSubset=calloc(1,sizeof(*pusch_Config->codebookSubset)); + *pusch_Config->codebookSubset = NR_PUSCH_Config__codebookSubset_nonCoherent; + pusch_Config->maxRank=calloc(1,sizeof(*pusch_Config->maxRank)); + *pusch_Config->maxRank= 1; + pusch_Config->rbg_Size=NULL; + pusch_Config->uci_OnPUSCH=NULL; + pusch_Config->tp_pi2BPSK=NULL; ubwp->bwp_Dedicated->srs_Config = calloc(1,sizeof(*ubwp->bwp_Dedicated->srs_Config)); ubwp->bwp_Dedicated->srs_Config->present = NR_SetupRelease_SRS_Config_PR_setup; @@ -634,6 +698,8 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco // where the startPosition = 2 or 3 and sl160 = 17, 17, 27 ... 157 only give us 30 different allocations. AssertFatal(uid>=0 && uid<30, "gNB cannot allocate the SRS resources\n"); + fix_servingcellconfigdedicated(servingcellconfigdedicated); + uint64_t bitmap=0; switch (servingcellconfigcommon->ssb_PositionsInBurst->present) { case 1 : @@ -761,12 +827,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->rlf_TimersAndConstants->choice.setup->ext1->t311 = NR_RLF_TimersAndConstants__ext1__t311_ms30000; secondaryCellGroup->spCellConfig->rlmInSyncOutOfSyncThreshold = NULL; - if (servingcellconfigdedicated) { - fix_servingcellconfigdedicated(servingcellconfigdedicated); - secondaryCellGroup->spCellConfig->spCellConfigDedicated = servingcellconfigdedicated; - } else { - secondaryCellGroup->spCellConfig->spCellConfigDedicated = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated)); - } + secondaryCellGroup->spCellConfig->spCellConfigDedicated = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->tdd_UL_DL_ConfigurationDedicated = NULL; /// initialDownlinkBWP @@ -811,7 +872,6 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco } } - #if 0 NR_TCI_State_t*tci0=calloc(1,sizeof(*tci0)); @@ -1133,21 +1193,16 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco n_dl_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; } if(n_dl_bwp>0) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); for (int bwp_loop = 0; bwp_loop < n_dl_bwp; bwp_loop++) { - NR_BWP_Downlink_t *bwp = NULL; - if (servingcellconfigdedicated && servingcellconfigdedicated->downlinkBWP_ToAddModList) { - bwp=servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]; - } else { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); - bwp=calloc(1,sizeof(*bwp)); - } + NR_BWP_Downlink_t *bwp = calloc(1,sizeof(*bwp)); fill_default_nsa_downlinkBWP(bwp, bwp_loop, secondaryCellGroup, servingcellconfigdedicated, servingcellconfigcommon, dl_antenna_ports, bitmap); ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); - secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = 1; - secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; } + secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = 1; + secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; } // Uplink BWPs @@ -1156,19 +1211,14 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco n_ul_bwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; } if(n_ul_bwp>0) { + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); for (int bwp_loop = 0; bwp_loop < n_ul_bwp; bwp_loop++) { - NR_BWP_Uplink_t *ubwp = NULL; - if (servingcellconfigdedicated && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList) { - ubwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]; - } else { - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList)); - ubwp = calloc(1,sizeof(*ubwp)); - } + NR_BWP_Uplink_t *ubwp = calloc(1,sizeof(*ubwp)); fill_default_nsa_uplinkBWP(ubwp, bwp_loop, servingcellconfigdedicated, servingcellconfigcommon, minRXTXTIME, do_csirs, do_srs, uid); ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); - secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; } + secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; } secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; @@ -1455,6 +1505,8 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco secondaryCellGroup->spCellConfig->spCellConfigDedicated->pathlossReferenceLinking=NULL; secondaryCellGroup->spCellConfig->spCellConfigDedicated->servingCellMO=NULL; + *servingcellconfigdedicated = *secondaryCellGroup->spCellConfig->spCellConfigDedicated; + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { xer_fprint(stdout, &asn_DEF_NR_SpCellConfig, (void *)secondaryCellGroup->spCellConfig); } From 589cb2f43ed80a6d6124db52c3d4b41f110d4413 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 18 Feb 2022 00:39:34 +0000 Subject: [PATCH 102/233] Fix segmentation fault for do-ra --- openair2/LAYER2/NR_MAC_gNB/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 3812ec4a7f..245c902702 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -535,7 +535,7 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, if (CellGroup) { - if (!get_softmodem_params()->phy_test) { + if (get_softmodem_params()->sa) { calculate_preferred_dl_tda(Mod_idP, NULL); } From 87c69d2fe402fa3191a3b71ed6d4aeca91da5c36 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 18 Feb 2022 01:22:26 +0000 Subject: [PATCH 103/233] To use initialBWP when there are no dedicated BWPs in config file --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 51 +++++++++++++++++++---------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 404c1b2fe2..ab276a287f 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1009,13 +1009,21 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, NR_ServingCellConfigCommon_t *scc, rrc_gNB_carrier_data_t *carrier) { - bwp->bwp_Id = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; - /// BWP common configuration bwp->bwp_Common = calloc(1,sizeof(*bwp->bwp_Common)); - bwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; - bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; - bwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + if(servingcellconfigdedicated->downlinkBWP_ToAddModList && + bwp_loop < servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count) { + bwp->bwp_Id = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + bwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; + bwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; + bwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + } else { + bwp->bwp_Id=bwp_loop+1; + bwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + bwp->bwp_Common->genericParameters.subcarrierSpacing = scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing; + bwp->bwp_Common->genericParameters.cyclicPrefix = scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.cyclicPrefix; + } + bwp->bwp_Common->pdcch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); bwp->bwp_Common->pdcch_ConfigCommon->present = NR_SetupRelease_PDCCH_ConfigCommon_PR_setup; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup)); @@ -1114,9 +1122,6 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list,ss); bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=NULL; - if(get_softmodem_params()->sa) { - bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceSIB1=NULL; - } bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->pagingSearchSpace=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=NULL; @@ -1242,13 +1247,21 @@ void fill_default_uplinkBWP(NR_BWP_Uplink_t *ubwp, rrc_gNB_carrier_data_t *carrier, int uid) { - ubwp->bwp_Id = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; - /// BWP common configuration ubwp->bwp_Common = calloc(1,sizeof(*ubwp->bwp_Common)); - ubwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; - ubwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; - ubwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + if(servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList && + bwp_loop < servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count) { + ubwp->bwp_Id = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Id; + ubwp->bwp_Common->genericParameters.locationAndBandwidth = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.locationAndBandwidth; + ubwp->bwp_Common->genericParameters.subcarrierSpacing = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.subcarrierSpacing; + ubwp->bwp_Common->genericParameters.cyclicPrefix = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_loop]->bwp_Common->genericParameters.cyclicPrefix; + } else { + ubwp->bwp_Id=bwp_loop+1; + ubwp->bwp_Common->genericParameters.locationAndBandwidth = PRBalloc_to_locationandbandwidth(scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,0); + ubwp->bwp_Common->genericParameters.subcarrierSpacing = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing; + ubwp->bwp_Common->genericParameters.cyclicPrefix = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.cyclicPrefix; + } + ubwp->bwp_Common->rach_ConfigCommon = NULL; ubwp->bwp_Common->pusch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon; ubwp->bwp_Common->pucch_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon; @@ -1872,8 +1885,10 @@ void fill_initial_SpCellConfig(rnti_t rnti, *pdsch_servingcellconfig->ext1->maxMIMO_Layers = 2; // Downlink BWPs - int n_dl_bwp = 0; - if (servingcellconfigdedicated && servingcellconfigdedicated->downlinkBWP_ToAddModList) { + int n_dl_bwp = 1; + if (servingcellconfigdedicated && + servingcellconfigdedicated->downlinkBWP_ToAddModList && + servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count > 0) { n_dl_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; } if(n_dl_bwp>0){ @@ -1890,8 +1905,10 @@ void fill_initial_SpCellConfig(rnti_t rnti, } // Uplink BWPs - int n_ul_bwp = 0; - if (servingcellconfigdedicated && servingcellconfigdedicated->uplinkConfig && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList) { + int n_ul_bwp = 1; + if (servingcellconfigdedicated && servingcellconfigdedicated->uplinkConfig && + servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList && + servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count > 0) { n_ul_bwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; } if(n_ul_bwp>0) { From cce425baf7060050b7f7483c09d84cbacec34397 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 18 Feb 2022 01:43:09 +0000 Subject: [PATCH 104/233] Fix searchSpaceId and controlResourceSetId for NSA --- openair2/RRC/NR/rrc_gNB_reconfig.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 9736cf8db3..bb109ba17d 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -116,7 +116,7 @@ void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, int curr_bwp = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,275); NR_ControlResourceSet_t *coreset = calloc(1,sizeof(*coreset)); - coreset->controlResourceSetId=1; + coreset->controlResourceSetId=bwp->bwp_Id+1; // To uniquely identify each Coreset lets derive it from the BWPId // frequency domain resources depends on BWP size // options are 24, 48 or 96 coreset->frequencyDomainResources.buf = calloc(1,6); @@ -156,9 +156,9 @@ void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList)); NR_SearchSpace_t *ss=calloc(1,sizeof(*ss)); - ss->searchSpaceId = 1; + ss->searchSpaceId = (bwp->bwp_Id<<1)-1; // To uniquely identify each SearchSpace lets derive it from the BWPId ss->controlResourceSetId=calloc(1,sizeof(*ss->controlResourceSetId)); - *ss->controlResourceSetId=1; + *ss->controlResourceSetId=coreset->controlResourceSetId; ss->monitoringSlotPeriodicityAndOffset = calloc(1,sizeof(*ss->monitoringSlotPeriodicityAndOffset)); ss->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; ss->duration=NULL; @@ -221,9 +221,9 @@ void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); NR_SearchSpace_t *ss2 = calloc(1,sizeof(*ss2)); - ss2->searchSpaceId=2; + ss2->searchSpaceId=(bwp->bwp_Id<<1); // To uniquely identify each SearchSpace lets derive it from the BWPId ss2->controlResourceSetId=calloc(1,sizeof(*ss2->controlResourceSetId)); - *ss2->controlResourceSetId=1; + *ss2->controlResourceSetId=coreset->controlResourceSetId; ss2->monitoringSlotPeriodicityAndOffset=calloc(1,sizeof(*ss2->monitoringSlotPeriodicityAndOffset)); ss2->monitoringSlotPeriodicityAndOffset->present = NR_SearchSpace__monitoringSlotPeriodicityAndOffset_PR_sl1; ss2->monitoringSlotPeriodicityAndOffset->choice.sl1=(NULL_t)0; From da251e00b37a9700c90d788bef15367ea3918e65 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 18 Feb 2022 01:59:33 +0000 Subject: [PATCH 105/233] Fix firstActiveBWP and defaultBWP for NSA --- openair2/RRC/NR/rrc_gNB_reconfig.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index bb109ba17d..a25b956c5d 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -1200,9 +1200,9 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list,bwp); } secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = 1; + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = servingcellconfigdedicated->firstActiveDownlinkBWP_Id ? *servingcellconfigdedicated->firstActiveDownlinkBWP_Id : 1; secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = 1; + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = servingcellconfigdedicated->defaultDownlinkBWP_Id ? *servingcellconfigdedicated->defaultDownlinkBWP_Id : 1; } // Uplink BWPs @@ -1218,7 +1218,7 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco ASN_SEQUENCE_ADD(&secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = calloc(1,sizeof(*secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id)); - *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = 1; + *secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id ? *servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id : 1; } secondaryCellGroup->spCellConfig->spCellConfigDedicated->bwp_InactivityTimer = NULL; From 35d6c4af98ebcdaf004c308e075cacddd66e0a01 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 18 Feb 2022 02:09:59 +0000 Subject: [PATCH 106/233] Fix ra_SearchSpace for NSA --- openair2/RRC/NR/rrc_gNB_reconfig.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index a25b956c5d..45b634a837 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -185,7 +185,7 @@ void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceOtherSystemInformation=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->pagingSearchSpace=NULL; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=calloc(1,sizeof(*bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace)); - *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=1; + *bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ra_SearchSpace=ss->searchSpaceId; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->ext1=NULL; bwp->bwp_Common->pdsch_ConfigCommon=calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); From 9eb1137b542deb02db65f9575d318307489b55d9 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 18 Feb 2022 10:14:52 +0100 Subject: [PATCH 107/233] fix for scc/scc_SIB at UE --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 13 +++++-------- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 13 +++++++++++-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 4 ++-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 69d245e7c1..1c40eae725 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -4076,25 +4076,22 @@ uint16_t compute_pucch_prb_size(uint8_t format, } } -int get_bw_tbslbrm(NR_ServingCellConfigCommon_t *scc, +int get_bw_tbslbrm(NR_BWP_t *genericParameters, NR_CellGroupConfig_t *cg) { int bw = 0; - NR_BWP_t genericParameters; if (cg && cg->spCellConfig && cg->spCellConfig->spCellConfigDedicated && cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { struct NR_ServingCellConfig__downlinkBWP_ToAddModList *BWP_list = cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList; for (int i=0; ilist.count; i++) { - genericParameters = BWP_list->list.array[i]->bwp_Common->genericParameters; - int curr_bw = NRRIV2BW(genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + genericParameters = &BWP_list->list.array[i]->bwp_Common->genericParameters; + int curr_bw = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); if (curr_bw > bw) bw = curr_bw; } } - else { - genericParameters = scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; - bw = NRRIV2BW(genericParameters.locationAndBandwidth, MAX_BWP_SIZE); - } + else + bw = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); return bw; } diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index bccc40b2c6..3c953226f4 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -121,7 +121,7 @@ uint8_t get_K_ptrs(uint16_t nrb0, uint16_t nrb1, uint16_t N_RB); uint16_t get_nr_srs_offset(NR_SRS_PeriodicityAndOffset_t periodicityAndOffset); -int get_bw_tbslbrm(NR_ServingCellConfigCommon_t *scc, +int get_bw_tbslbrm(NR_BWP_t *genericParameters, NR_CellGroupConfig_t *cg); uint32_t nr_compute_tbslbrm(uint16_t table, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index f852da7281..0ab103aaa7 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -891,7 +891,14 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr return -1; } - int bw_tbslbrm = get_bw_tbslbrm(mac->scc, mac->cg); + int bw_tbslbrm; + if (mac->scc || mac->scc_SIB || mac->cg) { + NR_BWP_t genericParameters = mac->scc ? mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters : + mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters; + bw_tbslbrm = get_bw_tbslbrm(&genericParameters, mac->cg); + } + else + bw_tbslbrm = dlsch_config_pdu_1_0->BWPSize; dlsch_config_pdu_1_0->tbslbrm = nr_compute_tbslbrm(dlsch_config_pdu_1_0->mcs_table, bw_tbslbrm, 1); @@ -1265,7 +1272,9 @@ int8_t nr_ue_process_dci(module_id_t module_id, int cc_id, uint8_t gNB_index, fr long *maxMIMO_Layers = mac->cg->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup->ext1->maxMIMO_Layers; AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n"); int nl_tbslbrm = *maxMIMO_Layers < 4 ? *maxMIMO_Layers : 4; - int bw_tbslbrm = get_bw_tbslbrm(mac->scc, mac->cg); + NR_BWP_t genericParameters = mac->scc ? mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters : + mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP.genericParameters; + int bw_tbslbrm = get_bw_tbslbrm(&genericParameters, mac->cg); dlsch_config_pdu_1_1->tbslbrm = nr_compute_tbslbrm(dlsch_config_pdu_1_1->mcs_table, bw_tbslbrm, nl_tbslbrm); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 26479b4b88..44b65a5387 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1338,7 +1338,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra uint8_t tb_scaling = 0; nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, tb_scaling); - int bw_tbslbrm = get_bw_tbslbrm(scc, ra->CellGroup); + int bw_tbslbrm = get_bw_tbslbrm(genericParameters, ra->CellGroup); pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(mcsTableIdx, bw_tbslbrm, 1); @@ -1720,7 +1720,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra int x_Overhead = 0; nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, tb_scaling); - int bw_tbslbrm = get_bw_tbslbrm(scc, ra->CellGroup); + int bw_tbslbrm = get_bw_tbslbrm(genericParameters, ra->CellGroup); pdsch_pdu_rel15->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(mcsTableIdx, bw_tbslbrm, 1); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 5b39f44ee2..3557742802 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1145,7 +1145,7 @@ void nr_schedule_ue_spec(module_id_t module_id, AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n"); int nl_tbslbrm = *maxMIMO_Layers < 4 ? *maxMIMO_Layers : 4; // Maximum number of PRBs across all configured DL BWPs - int bw_tbslbrm = get_bw_tbslbrm(scc, cg); + int bw_tbslbrm = get_bw_tbslbrm(genericParameters, cg); pdsch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(ps->mcsTableIdx, bw_tbslbrm, nl_tbslbrm); From 08e97aa9c8a87611c7b2b79abf9e485debfb19f9 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Feb 2022 11:09:55 +0000 Subject: [PATCH 108/233] Fix warnings after merge --- openair2/RRC/NR/rrc_gNB.c | 21 +++++++++++++++++++ .../gnb.sa.band41.fr1.106PRB.usrpb210.conf | 2 +- ...gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 8f0e7df31e..b369080122 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -106,6 +106,27 @@ extern RAN_CONTEXT_t RC; +extern boolean_t nr_rrc_pdcp_config_asn1_req( + const protocol_ctxt_t *const ctxt_pP, + NR_SRB_ToAddModList_t *const srb2add_list, + NR_DRB_ToAddModList_t *const drb2add_list, + NR_DRB_ToReleaseList_t *const drb2release_list, + const uint8_t security_modeP, + uint8_t *const kRRCenc, + uint8_t *const kRRCint, + uint8_t *const kUPenc, + uint8_t *const kUPint + ,LTE_PMCH_InfoList_r9_t *pmch_InfoList_r9 + ,rb_id_t *const defaultDRB, + struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list); + +extern rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP, + const NR_SRB_ToAddModList_t * const srb2add_listP, + const NR_DRB_ToAddModList_t * const drb2add_listP, + const NR_DRB_ToReleaseList_t * const drb2release_listP, + const LTE_PMCH_InfoList_r9_t * const pmch_InfoList_r9_pP, + struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list); + static inline uint64_t bitStr_to_uint64(BIT_STRING_t *asn); mui_t rrc_gNB_mui = 0; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf index 60097a8ecc..3c459cef23 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf @@ -37,7 +37,7 @@ gNBs = pusch_AntennaPorts = 1; min_rxtxtime = 6; sib1_tda = 0; - do_SRS = 1; + do_SRS = 0; pdcch_ConfigSIB1 = ( { diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index a6fbce2145..e85bdbcb69 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -36,7 +36,7 @@ gNBs = pdsch_AntennaPorts = 1; pusch_AntennaPorts = 2; sib1_tda = 0; - do_SRS = 1; + do_SRS = 0; ul_prbblacklist = "51,52,53,54" pdcch_ConfigSIB1 = ( From 7bda635e03ec8b73ca65dd6b21e285bdf952494b Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 1 Mar 2022 10:57:54 +0100 Subject: [PATCH 109/233] handling 2-port CSI report at MAC gNB --- openair1/PHY/INIT/nr_init.c | 8 +++---- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 12 ++++++++++ .../NR_MAC_gNB/gNB_scheduler_primitives.c | 24 +++++++++++++++++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 8 ++++++- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 5 ++++ openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 5 ++++ 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index ee9079fd57..3a6151183f 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -136,7 +136,7 @@ int init_codebook_gNB(PHY_VARS_gNB *gNB) { } } - int max_mimo_layers =(CSI_RS_antenna_portsnr_mimo_precoding_matrix = (int32_t ***)malloc16(max_mimo_layers* sizeof(int32_t **)); int32_t ***mat = gNB->nr_mimo_precoding_matrix; @@ -236,7 +236,7 @@ int init_codebook_gNB(PHY_VARS_gNB *gNB) { if((llb != ll) || (mmb != mm) || ((N1 == 1) && (N2 == 1))){ pmiq += 1; mat[1][pmiq] = (int32_t *)malloc16((2*N1*N2)*(2)*sizeof(int32_t)); - LOG_I(PHY, "layer 2 Codebook pmiq = %d\n",pmiq); + LOG_D(PHY, "layer 2 Codebook pmiq = %d\n",pmiq); for(int j_col=0; j_col<2; j_col++) { if (j_col==0) { llc = llb; @@ -320,7 +320,7 @@ int init_codebook_gNB(PHY_VARS_gNB *gNB) { if((llb != ll) || (mmb != mm)){ pmiq += 1; mat[2][pmiq] = (int32_t *)malloc16((2*N1*N2)*(3)*sizeof(int32_t)); - LOG_I(PHY, "layer 3 Codebook pmiq = %d\n",pmiq); + LOG_D(PHY, "layer 3 Codebook pmiq = %d\n",pmiq); for(int j_col=0; j_col<3; j_col++) { if (j_col==0) { llc = llb; @@ -411,7 +411,7 @@ int init_codebook_gNB(PHY_VARS_gNB *gNB) { if((llb != ll) || (mmb != mm)){ pmiq += 1; mat[3][pmiq] = (int32_t *)malloc16((2*N1*N2)*4*sizeof(int32_t)); - LOG_I(PHY, "layer 4 pmiq = %d\n",pmiq); + LOG_D(PHY, "layer 4 pmiq = %d\n",pmiq); for(int j_col=0; j_col<4; j_col++) { if (j_col==0) { llc = llb; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 92391ed1d5..6fa460026b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1174,6 +1174,18 @@ void nr_schedule_ue_spec(module_id_t module_id, pdsch_pdu->StartSymbolIndex = ps->startSymbolIndex; pdsch_pdu->NrOfSymbols = ps->nrOfSymbols; + // Precoding + if (sched_ctrl->set_pmi) { + int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id; + nr_csi_report_t *csi_report = &UE_info->csi_report_template[UE_id][report_id]; + pdsch_pdu->precodingAndBeamforming.prg_size = pdsch_pdu->rbSize; + pdsch_pdu->precodingAndBeamforming.prgs_list[0].pm_idx = set_pm_index(sched_ctrl, + nrOfLayers, + csi_report->N1, + csi_report->N2, + csi_report->codebook_mode); + } + NR_PDSCH_Config_t *pdsch_Config=NULL; if (bwp && bwp->bwp_Dedicated && diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 721f67295b..59a8811cfc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -145,6 +145,25 @@ uint8_t set_dl_nrOfLayers(NR_UE_sched_ctrl_t *sched_ctrl) { } + +uint16_t set_pm_index(NR_UE_sched_ctrl_t *sched_ctrl, + int layers, + int N1, int N2, + int codebook_mode) { + + int x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1; + int x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2; + int antenna_ports = (N1*N2)<<1; + + sched_ctrl->set_pmi = false; + + if (antenna_ports == 2) + return x2; + else + AssertFatal(1==0,"More than 2 antenna ports not yet supported\n"); +} + + void set_dl_mcs(NR_sched_pdsch_t *sched_pdsch, NR_UE_sched_ctrl_t *sched_ctrl, uint8_t *target_mcs, @@ -186,7 +205,7 @@ void set_dl_mcs(NR_sched_pdsch_t *sched_pdsch, } } } - sched_ctrl->set_mcs = FALSE; + sched_ctrl->set_mcs = false; } } @@ -2147,7 +2166,8 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG compute_csi_bitlen (CellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup, UE_info, UE_id, mod_idP); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; memset(sched_ctrl, 0, sizeof(*sched_ctrl)); - sched_ctrl->set_mcs = TRUE; + sched_ctrl->set_mcs = true; + sched_ctrl->set_pmi = false; sched_ctrl->lcid_mask = 0; if (!get_softmodem_params()->phy_test && !get_softmodem_params()->do_ra && !get_softmodem_params()->sa) { sched_ctrl->lcid_mask = 1<codebookType.choice.type1->subType.choice.typeI_SinglePanel->nrOfAntennaPorts.choice.moreThanTwo); get_x1x2_bitlen_singlepanel(n1,n2,o1,o2,&x1,&x2,i+1,codebookConfig->codebookType.choice.type1->codebookMode); + csi_report->N1 = n1; + csi_report->N2 = n2; + csi_report->codebook_mode = codebookConfig->codebookType.choice.type1->codebookMode; csi_report->csi_meas_bitlen.pmi_x1_bitlen[i]=x1; csi_report->csi_meas_bitlen.pmi_x2_bitlen[i]=x2; } @@ -1210,7 +1213,7 @@ void evaluate_cqi_report(uint8_t *payload, sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_2tb = temp_cqi; LOG_D(MAC,"Wide-band CQI for the second TB %d\n", temp_cqi); } - sched_ctrl->set_mcs = TRUE; + sched_ctrl->set_mcs = true; } @@ -1233,6 +1236,7 @@ uint8_t evaluate_pmi_report(uint8_t *payload, sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1, sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2); + sched_ctrl->set_pmi = true; return tot_bitlen; } @@ -1338,6 +1342,7 @@ void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig, if (r_index != -1) skip_zero_padding(&cumul_bits,csi_report,r_index,bitlen); pmi_bitlen = evaluate_pmi_report(payload,csi_report,cumul_bits,r_index,sched_ctrl); + sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id = csi_report_id; cumul_bits += pmi_bitlen; evaluate_cqi_report(payload,csi_report,cumul_bits,r_index,sched_ctrl,csirep->cqi_Table); break; @@ -1355,6 +1360,7 @@ void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig, if (r_index != -1) skip_zero_padding(&cumul_bits,csi_report,r_index,bitlen); pmi_bitlen = evaluate_pmi_report(payload,csi_report,cumul_bits,r_index,sched_ctrl); + sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id = csi_report_id; cumul_bits += pmi_bitlen; evaluate_cqi_report(payload,csi_report,cumul_bits,r_index,sched_ctrl,csirep->cqi_Table); break; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index fcc020ee83..1319aa6a83 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -455,6 +455,11 @@ void find_SSB_and_RO_available(module_id_t module_idP); void set_dl_dmrs_ports(NR_pdsch_semi_static_t *ps); +uint16_t set_pm_index(NR_UE_sched_ctrl_t *sched_ctrl, + int layers, + int N1, int N2, + int codebook_mode); + void set_dl_mcs(NR_sched_pdsch_t *sched_pdsch, NR_UE_sched_ctrl_t *sched_ctrl, uint8_t *target_mcs, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 22a0932c3f..68f6455f89 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -479,6 +479,7 @@ struct CRI_RI_LI_PMI_CQI { uint8_t wb_cqi_1tb; uint8_t wb_cqi_2tb; uint8_t cqi_table; + uint8_t csi_report_id; }; typedef struct CRI_SSB_RSRP { @@ -522,6 +523,9 @@ typedef struct nr_csi_report { uint8_t nb_of_csi_ssb_report; L1_RSRP_bitlen_t CSI_report_bitlen; CSI_Meas_bitlen_t csi_meas_bitlen; + int codebook_mode; + int N1; + int N2; } nr_csi_report_t; /*! As per the spec 38.212 and table: 6.3.1.1.2-12 in a single UCI sequence we can have multiple CSI_report @@ -621,6 +625,7 @@ typedef struct { struct CSI_Report CSI_report; bool SR; bool set_mcs; + bool set_pmi; /// information about every HARQ process NR_UE_harq_t harq_processes[NR_MAX_NB_HARQ_PROCESSES]; /// HARQ processes that are free From 19f532d450256f3a4948707109cf4cf4c2c93d0b Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 8 Mar 2022 14:54:16 +0000 Subject: [PATCH 110/233] Fix build after merge --- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 -- openair2/RRC/NR/rrc_gNB.c | 2 ++ .../CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 2943da6894..cc32b650ab 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -645,8 +645,6 @@ typedef struct { bool update_pdsch_ps; bool update_pusch_ps; bool set_mcs; - bool update_pdsch_ps; - bool update_pusch_ps; /// information about every HARQ process NR_UE_harq_t harq_processes[NR_MAX_NB_HARQ_PROCESSES]; /// HARQ processes that are free diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index bffbd55bba..53aadbc7d5 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1914,6 +1914,8 @@ int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP NULL, NULL, NULL, + NULL, + NULL, masterCellGroup); nr_rrc_data_req(ctxt_pP, diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index e85bdbcb69..e34c63e7c3 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -100,7 +100,7 @@ gNBs = prach_msg1_FDM = 0; prach_msg1_FrequencyStart = 0; zeroCorrelationZoneConfig = 12; - preambleReceivedTargetPower = -104; + preambleReceivedTargetPower = -96; #preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) preambleTransMax = 6; #powerRampingStep From d058e2a6ed95db703cece750a8bda8efef079314 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 8 Mar 2022 18:13:25 +0000 Subject: [PATCH 111/233] Increase RRC_BUF_SIZE to 1024 to solve encoding failure --- openair2/RRC/LTE/defs_NB_IoT.h | 2 +- openair2/RRC/LTE/rrc_defs.h | 2 +- openair2/RRC/NR/nr_rrc_defs.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openair2/RRC/LTE/defs_NB_IoT.h b/openair2/RRC/LTE/defs_NB_IoT.h index 37a26086b5..c76bdea53f 100644 --- a/openair2/RRC/LTE/defs_NB_IoT.h +++ b/openair2/RRC/LTE/defs_NB_IoT.h @@ -148,7 +148,7 @@ typedef struct UE_RRC_INFO_NB_IoT_s { //Measurement Report not supported in NB-IoT #define PAYLOAD_SIZE_MAX 1024 -#define RRC_BUF_SIZE 512 +#define RRC_BUF_SIZE 1024 #define UNDEF_SECURITY_MODE 0xff #define NO_SECURITY_MODE 0x20 diff --git a/openair2/RRC/LTE/rrc_defs.h b/openair2/RRC/LTE/rrc_defs.h index 3d472fa931..64baf5e2ac 100644 --- a/openair2/RRC/LTE/rrc_defs.h +++ b/openair2/RRC/LTE/rrc_defs.h @@ -316,7 +316,7 @@ typedef enum SL_TRIGGER_e { #define MAX_MEAS_ID 7 #define PAYLOAD_SIZE_MAX 1024 -#define RRC_BUF_SIZE 512 +#define RRC_BUF_SIZE 1024 #define UNDEF_SECURITY_MODE 0xff #define NO_SECURITY_MODE 0x20 diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index d5dfd90e72..4c7fab2fc9 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -128,7 +128,7 @@ typedef enum UE_STATE_NR_e { #define MAX_MEAS_ID 7 #define PAYLOAD_SIZE_MAX 1024 -#define RRC_BUF_SIZE 512 +#define RRC_BUF_SIZE 1024 #define UNDEF_SECURITY_MODE 0xff #define NO_SECURITY_MODE 0x20 From 00c6a4bce5b579e4e228f68efc81fb025b06ad7b Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 8 Mar 2022 19:13:49 +0000 Subject: [PATCH 112/233] Avoid segmentation fault --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index e6634a8a51..04d25c5dda 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -2029,20 +2029,23 @@ void update_cellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, NR_SpCellConfig_t *SpCellConfig = cellGroupConfig->spCellConfig; if (SpCellConfig == NULL) return; - NR_ServingCellConfigCommon_t *scc = carrier->servingcellconfigcommon; + NR_ServingCellConfigCommon_t *scc = carrier ? carrier->servingcellconfigcommon : NULL; - NR_BWP_DownlinkDedicated_t *bwp_Dedicated = SpCellConfig->spCellConfigDedicated->initialDownlinkBWP; - set_dl_mcs_table(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing, - uecap, bwp_Dedicated, scc); - - struct NR_ServingCellConfig__downlinkBWP_ToAddModList *DL_BWP_list = SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList; - if (DL_BWP_list) { - for (int i=0; ilist.count; i++){ - NR_BWP_Downlink_t *bwp = DL_BWP_list->list.array[i]; - int scs = bwp->bwp_Common->genericParameters.subcarrierSpacing; - set_dl_mcs_table(scs, uecap, bwp->bwp_Dedicated, carrier->servingcellconfigcommon); + // Set DL MCS table + if(scc) { + NR_BWP_DownlinkDedicated_t *bwp_Dedicated = SpCellConfig->spCellConfigDedicated->initialDownlinkBWP; + set_dl_mcs_table(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing, + uecap, bwp_Dedicated, scc); + struct NR_ServingCellConfig__downlinkBWP_ToAddModList *DL_BWP_list = SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList; + if (DL_BWP_list) { + for (int i=0; ilist.count; i++){ + NR_BWP_Downlink_t *bwp = DL_BWP_list->list.array[i]; + int scs = bwp->bwp_Common->genericParameters.subcarrierSpacing; + set_dl_mcs_table(scs, uecap, bwp->bwp_Dedicated, scc); + } } } + } @@ -2522,7 +2525,7 @@ int16_t do_RRCReconfiguration( if(cellGroupConfig!=NULL){ update_cellGroupConfig(cellGroupConfig, carrier, - ue_context_pP->ue_context.UE_Capability_nr); + ue_context_pP ? ue_context_pP->ue_context.UE_Capability_nr : NULL); enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, (void *)cellGroupConfig, From 06d488bb23ac6d17e72eda8fa0a6a5f75fdb2644 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 9 Mar 2022 17:35:38 +0100 Subject: [PATCH 113/233] ulsch memory optimization --- openair1/PHY/INIT/nr_init_ue.c | 4 +- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 26 ++++++---- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 6 ++- .../PHY/NR_UE_TRANSPORT/nr_transport_ue.h | 4 +- .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 47 ++++++++++++------- openair1/PHY/defs_gNB.h | 10 ++-- openair1/PHY/defs_nr_common.h | 3 +- openair1/SIMULATION/NR_PHY/ulschsim.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 2 +- 9 files changed, 61 insertions(+), 45 deletions(-) diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index 6626d4614c..5a26bfcfac 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -684,7 +684,7 @@ void term_nr_ue_transport(PHY_VARS_NR_UE *ue) for (int j = 0; j < 2; j++) { for (int k = 0; k < RX_NB_TH_MAX; k++) { free_nr_ue_dlsch(&ue->dlsch[k][i][j], N_RB_DL); - free_nr_ue_ulsch(&ue->ulsch[k][i][j], N_RB_DL); + free_nr_ue_ulsch(&ue->ulsch[k][i][j], N_RB_DL, &ue->frame_parms); } } @@ -702,7 +702,7 @@ void init_nr_ue_transport(PHY_VARS_NR_UE *ue) for (int k=0; kdlsch[k][i][j] = new_nr_ue_dlsch(1,NR_MAX_DLSCH_HARQ_PROCESSES,NSOFT,MAX_LDPC_ITERATIONS,ue->frame_parms.N_RB_DL))!=NULL,"Can't get ue dlsch structures\n"); LOG_D(PHY,"dlsch[%d][%d][%d] => %p\n",k,i,j,ue->dlsch[k][i][j]); - AssertFatal((ue->ulsch[k][i][j] = new_nr_ue_ulsch(ue->frame_parms.N_RB_UL, NR_MAX_ULSCH_HARQ_PROCESSES))!=NULL,"Can't get ue ulsch structures\n"); + AssertFatal((ue->ulsch[k][i][j] = new_nr_ue_ulsch(ue->frame_parms.N_RB_UL, NR_MAX_ULSCH_HARQ_PROCESSES,&ue->frame_parms))!=NULL,"Can't get ue ulsch structures\n"); LOG_D(PHY,"ulsch[%d][%d][%d] => %p\n",k,i,j,ue->ulsch[k][i][j]); } } diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 304dad96f0..4a824dc678 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -57,8 +57,7 @@ void free_gNB_ulsch(NR_gNB_ULSCH_t **ulschptr, uint16_t N_RB_UL) { - int i,r; - uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS; //number of segments to be allocated + uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*NR_MAX_NB_LAYERS; //number of segments to be allocated NR_gNB_ULSCH_t *ulsch = *ulschptr; if (N_RB_UL != 273) { @@ -66,19 +65,21 @@ void free_gNB_ulsch(NR_gNB_ULSCH_t **ulschptr, uint16_t N_RB_UL) a_segments = a_segments/273 +1; } - for (i=0; iharq_processes[i]) { if (ulsch->harq_processes[i]->b) { free_and_zero(ulsch->harq_processes[i]->b); ulsch->harq_processes[i]->b = NULL; } - for (r=0; rharq_processes[i]->c[r]); free_and_zero(ulsch->harq_processes[i]->d[r]); - free_and_zero(ulsch->harq_processes[i]->w[r]); nrLDPC_free_mem(ulsch->harq_processes[i]->p_nrLDPC_procBuf[r]); ulsch->harq_processes[i]->p_nrLDPC_procBuf[r] = NULL; } + free_and_zero(ulsch->harq_processes[i]->c); + free_and_zero(ulsch->harq_processes[i]->d); + free_and_zero(ulsch->harq_processes[i]->p_nrLDPC_procBuf); free_and_zero(ulsch->harq_processes[i]); ulsch->harq_processes[i] = NULL; } @@ -92,7 +93,7 @@ NR_gNB_ULSCH_t *new_gNB_ulsch(uint8_t max_ldpc_iterations, uint16_t N_RB_UL) NR_gNB_ULSCH_t *ulsch; uint8_t i,r; - uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS; //number of segments to be allocated + uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*NR_MAX_NB_LAYERS; //number of segments to be allocated if (N_RB_UL != 273) { a_segments = a_segments*N_RB_UL; @@ -109,11 +110,13 @@ NR_gNB_ULSCH_t *new_gNB_ulsch(uint8_t max_ldpc_iterations, uint16_t N_RB_UL) ulsch->harq_processes[i] = (NR_UL_gNB_HARQ_t *)malloc16_clear(sizeof(NR_UL_gNB_HARQ_t)); ulsch->harq_processes[i]->b = (uint8_t*)malloc16_clear(ulsch_bytes); + ulsch->harq_processes[i]->c = (uint8_t**)malloc16_clear(a_segments); + ulsch->harq_processes[i]->d = (int16_t**)malloc16_clear(a_segments); + ulsch->harq_processes[i]->p_nrLDPC_procBuf = (t_nrLDPC_procBuf **)malloc16_clear(a_segments); for (r=0; rharq_processes[i]->p_nrLDPC_procBuf[r] = nrLDPC_init_mem(); ulsch->harq_processes[i]->c[r] = (uint8_t*)malloc16_clear(8448*sizeof(uint8_t)); ulsch->harq_processes[i]->d[r] = (int16_t*)malloc16_clear((68*384)*sizeof(int16_t)); - ulsch->harq_processes[i]->w[r] = (int16_t*)malloc16_clear((3*(6144+64))*sizeof(int16_t)); } } @@ -265,9 +268,12 @@ void nr_processULSegment(void* arg) { //////////////////////////// ulsch_llr =====> ulsch_harq->e ////////////////////////////// + /// code blocks after bit selection in rate matching for LDPC code (38.212 V15.4.0 section 5.4.2.1) + int16_t harq_e[3*8448]; + nr_deinterleaving_ldpc(E, Qm, - ulsch_harq->e[r], + harq_e, ulsch_llr+r_offset); //for (int i =0; i<16; i++) @@ -292,7 +298,7 @@ void nr_processULSegment(void* arg) { p_decoderParms->BG, p_decoderParms->Z, ulsch_harq->d[r], - ulsch_harq->e[r], + harq_e, ulsch_harq->C, rv_index, ulsch_harq->new_rx, @@ -539,7 +545,7 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, r_offset = 0; - uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS; //number of segments to be allocated + uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*n_layers; //number of segments to be allocated if (nb_rb != 273) { a_segments = a_segments*nb_rb; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 9bc3643557..822edcc3d9 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -63,10 +63,12 @@ void free_nr_ue_dlsch(NR_UE_DLSCH_t **dlsch, uint16_t N_RB_DL); */ NR_UE_DLSCH_t *new_nr_ue_dlsch(uint8_t Kmimo,uint8_t Mdlharq,uint32_t Nsoft,uint8_t max_turbo_iterations,uint16_t N_RB_DL); +void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulschptr, + uint16_t N_RB_UL, + NR_DL_FRAME_PARMS* frame_parms); -void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulsch, uint16_t N_RB_UL); -NR_UE_ULSCH_t *new_nr_ue_ulsch(uint16_t N_RB_UL, int number_of_harq_pids); +NR_UE_ULSCH_t *new_nr_ue_ulsch(uint16_t N_RB_UL, int number_of_harq_pids, NR_DL_FRAME_PARMS* frame_parms); /** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream QPSK/QPSK reception. @param stream0_in Input from channel compensated (MR combined) stream 0 diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h index caf3a19d85..9d5beb9d20 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h @@ -84,9 +84,9 @@ typedef struct { /// Pointer to the payload + CRC uint8_t *b; /// Pointers to transport block segments - uint8_t *c[MAX_NUM_NR_ULSCH_SEGMENTS]; + uint8_t **c; /// LDPC-code outputs - uint8_t *d[MAX_NUM_NR_ULSCH_SEGMENTS]; + uint8_t **d; /// LDPC-code outputs (TS 36.212 V15.4.0, Sec 5.3.2 p. 17) uint8_t *e; /// Rate matching (Interleaving) outputs (TS 36.212 V15.4.0, Sec 5.4.2.2 p. 30) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index 5160a643fb..b1966aaa43 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -43,10 +43,10 @@ //#define DEBUG_ULSCH_CODING -void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulschptr, uint16_t N_RB_UL) +void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulschptr, + uint16_t N_RB_UL, + NR_DL_FRAME_PARMS* frame_parms) { -{ - int i, r; NR_UE_ULSCH_t *ulsch = *ulschptr; if (ulsch) { @@ -54,15 +54,15 @@ void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulschptr, uint16_t N_RB_UL) printf("Freeing ulsch %p\n",ulsch); #endif - uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS; //number of segments to be allocated + int max_layers = (frame_parms->nb_antennas_txnb_antennas_tx : NR_MAX_NB_LAYERS; + uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*max_layers; //number of segments to be allocated - if (N_RB_UL != 273) { - a_segments = a_segments*N_RB_UL; - a_segments = a_segments/273 +1; - } + if (N_RB_UL != 273) { + a_segments = a_segments*N_RB_UL; + a_segments = a_segments/273 +1; + } - - for (i=0; iharq_processes[i]) { if (ulsch->harq_processes[i]->a) { @@ -81,7 +81,7 @@ void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulschptr, uint16_t N_RB_UL) free16(ulsch->harq_processes[i]->f,14*N_RB_UL*12*8); ulsch->harq_processes[i]->f = NULL; } - for (r=0; rharq_processes[i]->c[r]) { free16(ulsch->harq_processes[i]->c[r],((r==0)?8:0) + 3+768); ulsch->harq_processes[i]->c[r] = NULL; @@ -94,6 +94,15 @@ void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulschptr, uint16_t N_RB_UL) } + if (ulsch->harq_processes[i]->c) { + free16(ulsch->harq_processes[i]->c,a_segments); + ulsch->harq_processes[i]->c = NULL; + } + if (ulsch->harq_processes[i]->d) { + free16(ulsch->harq_processes[i]->d,a_segments); + ulsch->harq_processes[i]->d = NULL; + } + free16(ulsch->harq_processes[i],sizeof(NR_UL_UE_HARQ_t)); ulsch->harq_processes[i] = NULL; } @@ -101,13 +110,13 @@ void free_nr_ue_ulsch(NR_UE_ULSCH_t **ulschptr, uint16_t N_RB_UL) free16(ulsch,sizeof(NR_UE_ULSCH_t)); *ulschptr = NULL; } - } -NR_UE_ULSCH_t *new_nr_ue_ulsch(uint16_t N_RB_UL, int number_of_harq_pids) -{ - uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS; //number of segments to be allocated +NR_UE_ULSCH_t *new_nr_ue_ulsch(uint16_t N_RB_UL, int number_of_harq_pids, NR_DL_FRAME_PARMS* frame_parms) { + + int max_layers = (frame_parms->nb_antennas_txnb_antennas_tx : NR_MAX_NB_LAYERS; + uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*max_layers; //number of segments to be allocated if (N_RB_UL != 273) { a_segments = a_segments*N_RB_UL; @@ -140,6 +149,8 @@ NR_UE_ULSCH_t *new_nr_ue_ulsch(uint16_t N_RB_UL, int number_of_harq_pids) DevAssert(ulsch->harq_processes[i]->b); bzero(ulsch->harq_processes[i]->b,ulsch_bytes); + ulsch->harq_processes[i]->c = malloc16(a_segments); + ulsch->harq_processes[i]->d = malloc16(a_segments); for (int r = 0; r < a_segments; r++) { // account for filler in first segment and CRCs for multiple segment case ulsch->harq_processes[i]->c[r] = malloc16(8448); @@ -240,6 +251,8 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, printf("\n"); */ + int max_payload_bytes = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*harq_process->pusch_pdu.nrOfLayers*1056; + if (A > 3824) { // Add 24-bit crc (polynomial A) to payload crc = crc24a(harq_process->a,A)>>8; @@ -251,7 +264,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, harq_process->B = A+24; - AssertFatal((A/8)+4 <= MAX_NR_ULSCH_PAYLOAD_BYTES,"A %d is too big (A/8+4 = %d > %d)\n",A,(A/8)+4,MAX_NR_ULSCH_PAYLOAD_BYTES); + AssertFatal((A/8)+4 <= max_payload_bytes,"A %d is too big (A/8+4 = %d > %d)\n",A,(A/8)+4,max_payload_bytes); memcpy(harq_process->b,harq_process->a,(A/8)+4); } @@ -265,7 +278,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, harq_process->B = A+16; - AssertFatal((A/8)+3 <= MAX_NR_ULSCH_PAYLOAD_BYTES,"A %d is too big (A/8+3 = %d > %d)\n",A,(A/8)+3,MAX_NR_ULSCH_PAYLOAD_BYTES); + AssertFatal((A/8)+3 <= max_payload_bytes,"A %d is too big (A/8+3 = %d > %d)\n",A,(A/8)+3,max_payload_bytes); memcpy(harq_process->b,harq_process->a,(A/8)+3); // using 3 bytes to mimic the case of 24 bit crc } diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 297b58e88e..2bd0bd301a 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -273,7 +273,7 @@ typedef struct { /// The payload + CRC (24 bits) in bits (38.212 V15.4.0 section 5.1) uint32_t B; /// Pointers to code blocks after code block segmentation and CRC attachment (38.212 V15.4.0 section 5.2.2) - uint8_t *c[MAX_NUM_NR_ULSCH_SEGMENTS]; + uint8_t **c; /// Number of bits in each code block (38.212 V15.4.0 section 5.2.2) uint32_t K; /// Number of "Filler" bits added in the code block segmentation (38.212 V15.4.0 section 5.2.2) @@ -281,13 +281,11 @@ typedef struct { /// Number of code blocks after code block segmentation (38.212 V15.4.0 section 5.2.2) uint32_t C; /// Pointers to code blocks after LDPC coding (38.212 V15.4.0 section 5.3.2) - int16_t *d[MAX_NUM_NR_ULSCH_SEGMENTS]; + int16_t **d; /// LDPC processing buffer - t_nrLDPC_procBuf* p_nrLDPC_procBuf[MAX_NUM_NR_ULSCH_SEGMENTS]; + t_nrLDPC_procBuf** p_nrLDPC_procBuf; /// LDPC lifting size (38.212 V15.4.0 table 5.3.2-1) uint32_t Z; - /// code blocks after bit selection in rate matching for LDPC code (38.212 V15.4.0 section 5.4.2.1) - int16_t e[MAX_NUM_NR_DLSCH_SEGMENTS][3*8448]; /// Number of bits in each code block after rate matching for LDPC code (38.212 V15.4.0 section 5.4.2.1) uint32_t E; /// Number of segments processed so far @@ -342,8 +340,6 @@ typedef struct { int16_t q_RI[MAX_RI_PAYLOAD]; /// Temporary h sequence to flag PUSCH_x/PUSCH_y symbols which are not scrambled uint8_t h[MAX_NUM_CHANNEL_BITS]; - /// soft bits for each received segment ("w"-sequence)(for definition see 36-212 V8.6 2009-03, p.15) - int16_t *w[MAX_NUM_NR_ULSCH_SEGMENTS]; ////////////////////////////////////////////////////////////// } NR_UL_gNB_HARQ_t; diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index 5574be705f..a912a896af 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -104,8 +104,7 @@ #define MAX_NUM_NR_DLSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) #define MAX_NR_DLSCH_PAYLOAD_BYTES (MAX_NUM_NR_DLSCH_SEGMENTS*1056) -#define MAX_NUM_NR_ULSCH_SEGMENTS (NR_MAX_NB_LAYERS*34) -#define MAX_NR_ULSCH_PAYLOAD_BYTES (MAX_NUM_NR_ULSCH_SEGMENTS*1056) +#define MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER 34 #define MAX_NUM_NR_CHANNEL_BITS (4*14*273*12*8) // 14 symbols, 273 RB #define MAX_NUM_NR_RE (4*14*273*12) diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 11b94a2221..1a6c788c8d 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -417,7 +417,7 @@ int main(int argc, char **argv) for (sf = 0; sf < 2; sf++) { for (i = 0; i < 2; i++) { - UE->ulsch[sf][0][i] = new_nr_ue_ulsch(N_RB_UL, 8); + UE->ulsch[sf][0][i] = new_nr_ue_ulsch(N_RB_UL, 8, frame_parms); if (!UE->ulsch[sf][0][i]) { printf("Can't get ue ulsch structures.\n"); @@ -628,7 +628,7 @@ int main(int argc, char **argv) for (sf = 0; sf < 2; sf++) for (i = 0; i < 2; i++) - free_nr_ue_ulsch(&UE->ulsch[sf][0][i], N_RB_UL); + free_nr_ue_ulsch(&UE->ulsch[sf][0][i], N_RB_UL, frame_parms); term_nr_ue_signal(UE, 1); free(UE); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index 3851ee011d..1c9ebf3eba 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -743,7 +743,7 @@ uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources, } else if (get_softmodem_params()->nsa) { - uint8_t mac_sdus[MAX_NR_ULSCH_PAYLOAD_BYTES]; + uint8_t mac_sdus[MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*1056]; uint16_t sdu_lengths[NB_RB_MAX] = {0}; int TBS_bytes = 848; int mac_ce_len = 0; From b04957b57c8ae37b274ea87fd57e28b9290aaf65 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 10 Mar 2022 13:19:40 +0000 Subject: [PATCH 114/233] Add configuration files for 217 PRBs (80 MHz) and 273 PRBs (100 MHz) BW in a Dedicated BWP --- .../gnb.sa.band78.217PRB.2x2.usrpn300.conf | 336 ++++++++++++++++++ .../gnb.sa.band78.273PRB.2x2.usrpn300.conf | 336 ++++++++++++++++++ 2 files changed, 672 insertions(+) create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf new file mode 100644 index 0000000000..1f4673b18f --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf @@ -0,0 +1,336 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); + + }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + pusch_AntennaPorts = 2; +# do_CSIRS = 1; + min_rxtxtime = 2; + ul_prbblacklist = "107,108,109,110" + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 10; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.24 + 106*12*30e3 = 3338.40 MHz (5G NR GSCN: 7734) + absoluteFrequencySSB = 622560; + dl_frequencyBand = 78; + # this is 3300.24 MHz + dl_absoluteFrequencyPointA = 620016; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 217; + #initialDownlinkBWP + #genericParameters + # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) + initialDLBWPlocationAndBandwidth = 31899; + # +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 10; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 217; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 31899; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -70; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 16499; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 16499; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); + + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "192.168.70.132"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; + ulsch_max_frame_inactivity = 0; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 120; +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 2 + nb_rx = 2 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + sf_extension = 0; + eNB_instances = [0]; + ##beamforming 1x2 matrix: 1 layer x 2 antennas + bf_weights = [0x00007fff, 0x0000]; + #clock_src = "internal"; + sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" +} +); + +THREAD_STRUCT = ( +{ + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; +} +); + +rfsimulator : +{ + serveraddr = "server"; + serverport = "4043"; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + ngap_log_level ="debug"; + ngap_log_verbosity ="medium"; + f1ap_log_level ="debug"; + f1ap_log_verbosity ="medium"; +}; \ No newline at end of file diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf new file mode 100644 index 0000000000..fc596ded03 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf @@ -0,0 +1,336 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); + + }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + pusch_AntennaPorts = 2; +# do_CSIRS = 1; + min_rxtxtime = 2; + ul_prbblacklist = "107,108,109,110" + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 10; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.24 + 134*12*30e3 = 3348.48 MHz (5G NR GSCN: 7741) + absoluteFrequencySSB = 623232; + dl_frequencyBand = 78; + # this is 3300.24 MHz + dl_absoluteFrequencyPointA = 620016; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 273; + #initialDownlinkBWP + #genericParameters + # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) + initialDLBWPlocationAndBandwidth = 31899; + # +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 10; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 273; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 31899; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -90; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -70; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); + + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "192.168.70.132"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; + ulsch_max_frame_inactivity = 0; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 120; +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 2 + nb_rx = 2 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + sf_extension = 0; + eNB_instances = [0]; + ##beamforming 1x2 matrix: 1 layer x 2 antennas + bf_weights = [0x00007fff, 0x0000]; + #clock_src = "internal"; + sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" +} +); + +THREAD_STRUCT = ( +{ + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; +} +); + +rfsimulator : +{ + serveraddr = "server"; + serverport = "4043"; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + ngap_log_level ="debug"; + ngap_log_verbosity ="medium"; + f1ap_log_level ="debug"; + f1ap_log_verbosity ="medium"; +}; \ No newline at end of file From 3114060a385886a4555b4b7b33cfa9e8c7aeae72 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 10 Mar 2022 13:19:40 +0000 Subject: [PATCH 115/233] Add configuration files for 217 PRBs (80 MHz) and 273 PRBs (100 MHz) BW in a Dedicated BWP --- ...gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf | 336 ++++++++++++++++++ ...gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf | 336 ++++++++++++++++++ 2 files changed, 672 insertions(+) create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf new file mode 100644 index 0000000000..1f4673b18f --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf @@ -0,0 +1,336 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); + + }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + pusch_AntennaPorts = 2; +# do_CSIRS = 1; + min_rxtxtime = 2; + ul_prbblacklist = "107,108,109,110" + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 10; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.24 + 106*12*30e3 = 3338.40 MHz (5G NR GSCN: 7734) + absoluteFrequencySSB = 622560; + dl_frequencyBand = 78; + # this is 3300.24 MHz + dl_absoluteFrequencyPointA = 620016; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 217; + #initialDownlinkBWP + #genericParameters + # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) + initialDLBWPlocationAndBandwidth = 31899; + # +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 10; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 217; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 31899; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -70; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 16499; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 16499; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); + + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "192.168.70.132"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; + ulsch_max_frame_inactivity = 0; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 120; +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 2 + nb_rx = 2 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + sf_extension = 0; + eNB_instances = [0]; + ##beamforming 1x2 matrix: 1 layer x 2 antennas + bf_weights = [0x00007fff, 0x0000]; + #clock_src = "internal"; + sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" +} +); + +THREAD_STRUCT = ( +{ + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; +} +); + +rfsimulator : +{ + serveraddr = "server"; + serverport = "4043"; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + ngap_log_level ="debug"; + ngap_log_verbosity ="medium"; + f1ap_log_level ="debug"; + f1ap_log_verbosity ="medium"; +}; \ No newline at end of file diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf new file mode 100644 index 0000000000..fc596ded03 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf @@ -0,0 +1,336 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + }, + { + sst = 1; + sd = 0x112233; // 0 false, else true + } + ); + + }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts = 1; + pusch_AntennaPorts = 2; +# do_CSIRS = 1; + min_rxtxtime = 2; + ul_prbblacklist = "107,108,109,110" + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 10; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.24 + 134*12*30e3 = 3348.48 MHz (5G NR GSCN: 7741) + absoluteFrequencySSB = 623232; + dl_frequencyBand = 78; + # this is 3300.24 MHz + dl_absoluteFrequencyPointA = 620016; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 273; + #initialDownlinkBWP + #genericParameters + # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) + initialDLBWPlocationAndBandwidth = 31899; + # +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 10; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 273; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 31899; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -90; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -70; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); + + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "192.168.70.132"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; + ulsch_max_frame_inactivity = 0; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 120; +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 2 + nb_rx = 2 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + sf_extension = 0; + eNB_instances = [0]; + ##beamforming 1x2 matrix: 1 layer x 2 antennas + bf_weights = [0x00007fff, 0x0000]; + #clock_src = "internal"; + sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" +} +); + +THREAD_STRUCT = ( +{ + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; +} +); + +rfsimulator : +{ + serveraddr = "server"; + serverport = "4043"; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level ="info"; + global_log_verbosity ="medium"; + hw_log_level ="info"; + hw_log_verbosity ="medium"; + phy_log_level ="info"; + phy_log_verbosity ="medium"; + mac_log_level ="info"; + mac_log_verbosity ="high"; + rlc_log_level ="info"; + rlc_log_verbosity ="medium"; + pdcp_log_level ="info"; + pdcp_log_verbosity ="medium"; + rrc_log_level ="info"; + rrc_log_verbosity ="medium"; + ngap_log_level ="debug"; + ngap_log_verbosity ="medium"; + f1ap_log_level ="debug"; + f1ap_log_verbosity ="medium"; +}; \ No newline at end of file From 7b9f53e9db28c64a184ee5d55253afbacd94df7a Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 21 Mar 2022 10:01:13 +0100 Subject: [PATCH 116/233] option for identity matrix if xp is 1 --- openair2/LAYER2/NR_MAC_gNB/config.c | 6 +++--- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 1 + openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 7 ++++++- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 3 ++- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 ++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 8fe6107e4a..2e37cc39c5 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -137,11 +137,10 @@ void process_CellGroup(NR_CellGroupConfig_t *CellGroup, NR_UE_sched_ctrl_t *sche } -void config_common(int Mod_idP, int ssb_SubcarrierOffset, rrc_pdsch_AntennaPorts_t dl_antenna_ports_struct, int pusch_AntennaPorts, NR_ServingCellConfigCommon_t *scc) { +void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, NR_ServingCellConfigCommon_t *scc) { nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[Mod_idP]->config[0]; RC.nrmac[Mod_idP]->common_channels[0].ServingCellConfigCommon = scc; - int pdsch_AntennaPorts = dl_antenna_ports_struct.N1 * dl_antenna_ports_struct.N2 * dl_antenna_ports_struct.XP; // Carrier configuration @@ -465,9 +464,10 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, LOG_I(NR_MAC,"Configuring common parameters from NR ServingCellConfig\n"); + RC.nrmac[Mod_idP]->pdsch_antenna_ports = pdsch_AntennaPorts.N1 * pdsch_AntennaPorts.N2 * pdsch_AntennaPorts.XP; config_common(Mod_idP, ssb_SubcarrierOffset, - pdsch_AntennaPorts, + RC.nrmac[Mod_idP]->pdsch_antenna_ports, pusch_AntennaPorts, scc); LOG_D(NR_MAC, "%s() %s:%d RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req:%p\n", __FUNCTION__, __FILE__, __LINE__, RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 6fa460026b..a3bf413b29 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1183,6 +1183,7 @@ void nr_schedule_ue_spec(module_id_t module_id, nrOfLayers, csi_report->N1, csi_report->N2, + gNB_mac->pdsch_antenna_ports, csi_report->codebook_mode); } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 59a8811cfc..1eb54ab45c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -149,11 +149,16 @@ uint8_t set_dl_nrOfLayers(NR_UE_sched_ctrl_t *sched_ctrl) { uint16_t set_pm_index(NR_UE_sched_ctrl_t *sched_ctrl, int layers, int N1, int N2, + int config_file_ant_ports, int codebook_mode) { + int antenna_ports = (N1*N2)<<1; + if (antenna_ports!=config_file_ant_ports) + return 0; //identity matrix + int x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1; int x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2; - int antenna_ports = (N1*N2)<<1; + LOG_D(NR_MAC,"PMI report: x1 %d x2 %d\n",x1,x2); sched_ctrl->set_pmi = false; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 1319aa6a83..455538a361 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -40,7 +40,7 @@ void mac_top_init_gNB(void); void config_common(int Mod_idP, int ssb_SubcarrierOffset, - rrc_pdsch_AntennaPorts_t pdsch_AntennaPorts, + int pdsch_AntennaPorts, int pusch_AntennaPorts, NR_ServingCellConfigCommon_t *scc ); @@ -458,6 +458,7 @@ void set_dl_dmrs_ports(NR_pdsch_semi_static_t *ps); uint16_t set_pm_index(NR_UE_sched_ctrl_t *sched_ctrl, int layers, int N1, int N2, + int config_file_ant_ports, int codebook_mode); void set_dl_mcs(NR_sched_pdsch_t *sched_pdsch, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 68f6455f89..8b685fdd02 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -806,6 +806,8 @@ typedef struct gNB_MAC_INST_s { uint16_t cset0_bwp_size; NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config[64]; + int pdsch_antenna_ports; + bool first_MIB; double dl_bler_target_upper; double dl_bler_target_lower; From 4d289b92cc7fb62868019acfe7eccd9a8f23c02a Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 21 Mar 2022 11:59:31 +0100 Subject: [PATCH 117/233] fix xp=1 option and added RUNMODEM description for MIMO configuration --- .../gNB_SA_n78_106PRB.2x2_usrpn310.conf | 2 +- .../gNB_SA_n78_133PRB.2x2_usrpn310.conf | 2 +- .../gnb.band78.nsa_2x2.106PRB.usrpn310.conf | 2 +- .../gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf | 2 +- .../gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf | 2 +- doc/RUNMODEM.md | 17 +++++++++++++++++ doc/images/mimo_antenna_ports.png | Bin 0 -> 18753 bytes openair2/LAYER2/NR_MAC_gNB/config.c | 5 +++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 2 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 7 ++++--- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 2 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- 12 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 doc/images/mimo_antenna_ports.png diff --git a/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf b/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf index 3b08b70ecf..82d665d904 100644 --- a/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf +++ b/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf @@ -38,7 +38,7 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts_N1 = 2; + pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 2; pdcch_ConfigSIB1 = ( diff --git a/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf b/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf index 0d9abc275d..937d287d24 100644 --- a/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf +++ b/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf @@ -39,7 +39,7 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts_N1 = 2; + pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 2; pdcch_ConfigSIB1 = ( diff --git a/ci-scripts/conf_files/gnb.band78.nsa_2x2.106PRB.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.nsa_2x2.106PRB.usrpn310.conf index 96bf3debd8..213c166826 100644 --- a/ci-scripts/conf_files/gnb.band78.nsa_2x2.106PRB.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.nsa_2x2.106PRB.usrpn310.conf @@ -20,7 +20,7 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 31; //0; - pdsch_AntennaPorts_N1 = 2; + pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 2; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf index ff10eee268..7a3afd1127 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf @@ -38,7 +38,7 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts_N1 = 2; + pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 2; ul_prbblacklist = "51,52,53,54" do_SRS = 1; diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf index 78b402d064..fbe7e62a23 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf @@ -38,7 +38,7 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts_N1 = 2; + pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 2; #pusch_TargetSNRx10 = 200; #pucch_TargetSNRx10 = 200; diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index 449859b042..4cc57c2629 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -211,7 +211,24 @@ UE on machine 2: `sudo ./nr-uesoftmodem --if_freq 2169080000 --if_freq_off -400000000` +# MIMO configuration + + + + +
+ + +
+ +This image shows an example of gNB 5G MIMO logical antenna port configuration. It has to be noted that logical antenna ports might not directly correspond to physical antenna ports and each logical antenna port might consist of a sub-array of antennas. + +The basic element is a dual-polarized antenna, therefore the minimal DL MIMO setup with two logical antenna ports would consist of two cross-polarized antenna elements. In a single panel configuration, as the one in the picture, this element can be repeated vertiacally and/or horizontally to form an equi-spaced 1D or 2D array. The values N1 and N2 represent the number of antenna ports in the two dimensions and the supported configurations are specified in Section 5.2.2.2.1 of TS 38.214. + +The DL logical antenna port configuration can be selected thorugh configuration file. `pdsch_AntennaPorts_N1` can be used to set N1 parameter, `pdsch_AntennaPorts_N2` to set N2 and `pdsch_AntennaPorts_XP` to set the cross-polarization configuration (1 for single pol, 2 for cross-pol). To be noted that if XP is 1 but N1 and/or N2 are larger than 1, this would result in a non-standard configuration and the PMI selected would be the identity matrix regardless of CSI report. The default value for each of these parameters is 1. + +# Additional links [Selecting an alternative ldpc implementation at run time](../openair1/PHY/CODING/DOC/LDPCImplementation.md) diff --git a/doc/images/mimo_antenna_ports.png b/doc/images/mimo_antenna_ports.png new file mode 100644 index 0000000000000000000000000000000000000000..ef91f4becc80fe6c09b55f202f9d8054368bd88c GIT binary patch literal 18753 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 = pdsch_AntennaPorts.N1 * pdsch_AntennaPorts.N2 * pdsch_AntennaPorts.XP; + int num_pdsch_antenna_ports = pdsch_AntennaPorts.N1 * pdsch_AntennaPorts.N2 * pdsch_AntennaPorts.XP; + RC.nrmac[Mod_idP]->xp_pdsch_antenna_ports = pdsch_AntennaPorts.XP; config_common(Mod_idP, ssb_SubcarrierOffset, - RC.nrmac[Mod_idP]->pdsch_antenna_ports, + num_pdsch_antenna_ports, pusch_AntennaPorts, scc); LOG_D(NR_MAC, "%s() %s:%d RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req:%p\n", __FUNCTION__, __FILE__, __LINE__, RC.nrmac[Mod_idP]->if_inst->NR_PHY_config_req); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index a3bf413b29..3db53ef809 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1183,7 +1183,7 @@ void nr_schedule_ue_spec(module_id_t module_id, nrOfLayers, csi_report->N1, csi_report->N2, - gNB_mac->pdsch_antenna_ports, + gNB_mac->xp_pdsch_antenna_ports, csi_report->codebook_mode); } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 1eb54ab45c..33390ec637 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -149,12 +149,13 @@ uint8_t set_dl_nrOfLayers(NR_UE_sched_ctrl_t *sched_ctrl) { uint16_t set_pm_index(NR_UE_sched_ctrl_t *sched_ctrl, int layers, int N1, int N2, - int config_file_ant_ports, + int xp_pdsch_antenna_ports, int codebook_mode) { int antenna_ports = (N1*N2)<<1; - if (antenna_ports!=config_file_ant_ports) - return 0; //identity matrix + if (xp_pdsch_antenna_ports == 1 && + antenna_ports>1) + return 0; //identity matrix (basic 5G configuration handled by PMI report is with XP antennas) int x1 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1; int x2 = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 455538a361..7ab738b87c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -458,7 +458,7 @@ void set_dl_dmrs_ports(NR_pdsch_semi_static_t *ps); uint16_t set_pm_index(NR_UE_sched_ctrl_t *sched_ctrl, int layers, int N1, int N2, - int config_file_ant_ports, + int xp_pdsch_antenna_ports, int codebook_mode); void set_dl_mcs(NR_sched_pdsch_t *sched_pdsch, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 8b685fdd02..ecea3a2bd1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -806,7 +806,7 @@ typedef struct gNB_MAC_INST_s { uint16_t cset0_bwp_size; NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config[64]; - int pdsch_antenna_ports; + int xp_pdsch_antenna_ports; bool first_MIB; double dl_bler_target_upper; From 65e47c882fbadcc1ad24ddcd8e43a381a00e504b Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Mon, 21 Mar 2022 14:35:41 +0000 Subject: [PATCH 118/233] Handle RRC processing time at gNB --- openair1/SIMULATION/NR_PHY/dlsim.c | 4 +- openair1/SIMULATION/NR_PHY/ulsim.c | 4 +- openair2/GNB_APP/gnb_config.c | 4 +- openair2/LAYER2/NR_MAC_gNB/config.c | 20 ++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 3 + .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 28 +++- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 5 + .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 4 + openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 7 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 8 ++ openair2/RRC/NR/rrc_gNB.c | 122 +++++++++++++----- openair2/RRC/NR/rrc_gNB_nsa.c | 8 +- 12 files changed, 170 insertions(+), 47 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index e797c01096..ca7c2a4c61 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -808,9 +808,9 @@ int main(int argc, char **argv) AssertFatal((gNB->if_inst = NR_IF_Module_init(0))!=NULL,"Cannot register interface"); gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 0, 0, NULL); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 0, 0, NULL, false); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, false); // reset preprocessor to the one of DLSIM after it has been set during // rrc_mac_config_req_gNB gNB_mac->pre_processor_dl = nr_dlsim_preprocessor; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 76d896a047..a527f2ad56 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -742,9 +742,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,0, 0, NULL); + rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,0, 0, NULL, false); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, false); frame_parms->nb_antennas_tx = n_tx; frame_parms->nb_antennas_rx = n_rx; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 2795c07ce7..34063f852f 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2045,8 +2045,8 @@ void configure_gnb_du_mac(int inst) { NULL, 0, 0, // rnti - (NR_CellGroupConfig_t *)NULL - ); + (NR_CellGroupConfig_t *)NULL, + false); } diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 7833b41f0b..24294dcb29 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -434,9 +434,10 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, int minRXTXTIMEpdsch, NR_ServingCellConfigCommon_t *scc, NR_BCCH_BCH_Message_t *mib, - int add_ue, + int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup) { + NR_CellGroupConfig_t *CellGroup, + bool rrc_reconfiguration_running) { if (scc != NULL ) { AssertFatal((scc->ssb_PositionsInBurst->present > 0) && (scc->ssb_PositionsInBurst->present < 4), "SSB Bitmap type %d is not valid\n",scc->ssb_PositionsInBurst->present); @@ -663,9 +664,22 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, sched_ctrl->maxL = 2; } } + + if (rrc_reconfiguration_running) { + const int UE_id = find_nr_UE_id(Mod_idP,rnti); + if (UE_id >= 0) { + NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->rrc_processing_info.rrc_processing_timer == 0) { + sched_ctrl->rrc_processing_info.rrc_processing_timer = 1; + LOG_I(NR_MAC, "Activating RRC processing timer for UE: %d\n", UE_id); + } + } + } + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_MAC_CONFIG, VCD_FUNCTION_OUT); - return(0); + return 0; }// END rrc_mac_config_req_gNB diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 51541353f4..7c140b3952 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -346,6 +346,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, LOG_I(NR_MAC,"Frame.Slot %d.%d\n%s\n",frame,slot,stats_output); } + // This updates the RRC processing timers running at MAC layer + nr_update_rrc_processing_timer(module_idP, frame, slot); + // This schedules MIB schedule_nr_mib(module_idP, frame, slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index f366b2c4f9..2bb39098fa 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -514,14 +514,14 @@ void nr_store_dlsch_buffer(module_id_t module_id, 0); stop_meas(&RC.nrmac[module_id]->rlc_status_ind); } - if(sched_ctrl->rlc_status[DL_SCH_LCID_DCCH].bytes_in_buffer > 0){ + if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH].bytes_in_buffer > 0) { lcid = DL_SCH_LCID_DCCH; - } - else if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH1].bytes_in_buffer > 0) - { + } else if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH1].bytes_in_buffer > 0) { lcid = DL_SCH_LCID_DCCH1; - }else{ + } else if (sched_ctrl->rrc_processing_info.rrc_processing_timer == 0) { lcid = DL_SCH_LCID_DTCH; + } else { + continue; } sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; @@ -1003,6 +1003,24 @@ nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(module_id_t module_id, int CC_id) return nr_fr1_dlsch_preprocessor; } +void nr_update_rrc_processing_timer(module_id_t module_id, + frame_t frame, + sub_frame_t slot) { + + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + const NR_list_t *UE_list = &UE_info->list; + for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->rrc_processing_info.rrc_processing_timer > 0) { + sched_ctrl->rrc_processing_info.rrc_processing_timer++; + if (sched_ctrl->rrc_processing_info.rrc_processing_timer >= sched_ctrl->rrc_processing_info.rrc_processing_delay) { + sched_ctrl->rrc_processing_info.rrc_processing_timer = 0; + LOG_I(NR_MAC, "De-activating RRC processing timer for UE: %d\n", UE_id); + } + } + } +} + void nr_schedule_ue_spec(module_id_t module_id, frame_t frame, sub_frame_t slot) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 4d05320e64..cae806e3d1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2225,6 +2225,11 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG scc, genericParameters, NULL); + + sched_ctrl->rrc_processing_info.rrc_processing_timer = 0; + int rrc_processing_delay[5] = {10, 20, 40, 80, 160}; // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs + sched_ctrl->rrc_processing_info.rrc_processing_delay = rrc_processing_delay[genericParameters->subcarrierSpacing]; + const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; if (ubwpList) AssertFatal(ubwpList->list.count == 1, "uplinkBWP_ToAddModList has %d BWP!\n", diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index bd2836c3e7..ce9637662f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1176,6 +1176,10 @@ void pf_ul(module_id_t module_id, if (B == 0 && !do_sched) continue; + if (sched_ctrl->rrc_processing_info.rrc_processing_timer > 0) { + continue; + } + /* Schedule UE on SR or UL inactivity and no data (otherwise, will be scheduled * based on data to transmit) */ if (B == 0 && do_sched) { diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 28e9d689fa..5ded67deba 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -55,13 +55,18 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_BCCH_BCH_Message_t *mib, int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup); + NR_CellGroupConfig_t *CellGroup, + bool rrc_reconfiguration_running); void clear_nr_nfapi_information(gNB_MAC_INST * gNB, int CC_idP, frame_t frameP, sub_frame_t subframeP); +void nr_update_rrc_processing_timer(module_id_t module_id, + frame_t frame, + sub_frame_t slot); + void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame_rxP, sub_frame_t slot_rxP); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index b1e1241487..b0ddbb50d1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -547,6 +547,11 @@ typedef struct NR_UE_ul_harq { NR_sched_pusch_t sched_pusch; } NR_UE_ul_harq_t; +typedef struct NR_RRC_processing_info { + uint32_t rrc_processing_timer; + uint32_t rrc_processing_delay; +} NR_RRC_processing_info_t; + /*! \brief scheduling control information set through an API */ #define MAX_CSI_REPORTS 48 typedef struct { @@ -559,6 +564,9 @@ typedef struct { NR_ControlResourceSet_t *coreset; NR_sched_pdcch_t sched_pdcch; + /// Timer for RRC processing procedures + NR_RRC_processing_info_t rrc_processing_info; + /// CCE index and Aggr. Level are shared for PUSCH/PDSCH allocation decisions /// corresponding to the sched_pusch/sched_pdsch structures below int cce_index; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index d6c658f01d..b53a4470aa 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -166,17 +166,17 @@ static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration if (NODE_IS_MONOLITHIC(rrc->node_type)){ rrc_mac_config_req_gNB(rrc->module_id, - rrc->carrier.ssb_SubcarrierOffset, - rrc->carrier.pdsch_AntennaPorts, - rrc->carrier.pusch_AntennaPorts, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, rrc->carrier.sib1_tda, rrc->carrier.minRXTXTIME, - (NR_ServingCellConfigCommon_t *)rrc->carrier.servingcellconfigcommon, - &rrc->carrier.mib, - 0, - 0, // WIP hardcoded rnti - (NR_CellGroupConfig_t *)NULL - ); + (NR_ServingCellConfigCommon_t *)rrc->carrier.servingcellconfigcommon, + &rrc->carrier.mib, + 0, + 0, // WIP hardcoded rnti + (NR_CellGroupConfig_t *)NULL, + false); } /* set flag to indicate that cell information is configured. This is required @@ -279,17 +279,18 @@ void apply_macrlc_config(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *const ue_context_pP, const protocol_ctxt_t *const ctxt_pP ) { - rrc_mac_config_req_gNB(rrc->module_id, - rrc->carrier.ssb_SubcarrierOffset, - rrc->carrier.pdsch_AntennaPorts, - rrc->carrier.pusch_AntennaPorts, - rrc->carrier.sib1_tda, - rrc->carrier.minRXTXTIME, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup : (NR_CellGroupConfig_t *)NULL); + rrc_mac_config_req_gNB(rrc->module_id, + rrc->carrier.ssb_SubcarrierOffset, + rrc->carrier.pdsch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, + rrc->carrier.sib1_tda, + rrc->carrier.minRXTXTIME, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup : (NR_CellGroupConfig_t *)NULL, + false); nr_rrc_rlc_config_asn1_req(ctxt_pP, ue_context_pP->ue_context.SRB_configList, @@ -474,8 +475,8 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( &rrc_instance_p->carrier.mib, 0, ue_context_pP->ue_context.rnti, - (NR_CellGroupConfig_t *)NULL - ); + (NR_CellGroupConfig_t *)NULL, + false); LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", @@ -730,6 +731,19 @@ rrc_gNB_generate_defaultRRCReconfiguration( LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + 0, + 0, + 0, + 0, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + true); + /* Free all NAS PDUs */ for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { @@ -988,7 +1002,20 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( &rrc->carrier, NULL, cellGroupConfig); - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); + LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + 0, + 0, + 0, + 0, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + true); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1162,6 +1189,19 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + 0, + 0, + 0, + 0, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + true); + /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_modify_pdusessions; i++) { if (ue_context_pP->ue_context.modify_pdusession[i].param.nas_pdu.buffer != NULL) { @@ -1262,8 +1302,20 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( NULL); ue_context_pP->ue_context.pdu_session_release_command_flag = 1; - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, - "[MSG] RRC Reconfiguration\n"); + LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + 0, + 0, + 0, + 0, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + true); /* Free all NAS PDUs */ if (nas_length > 0) { @@ -1371,8 +1423,8 @@ rrc_gNB_process_RRCReconfigurationComplete( NULL, 0, ue_context_pP->ue_context.rnti, - ue_context_pP->ue_context.masterCellGroup - ); + ue_context_pP->ue_context.masterCellGroup, + false); LOG_D(NR_RRC,"Configuring RLC DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); nr_rrc_rlc_config_asn1_req(ctxt_pP, SRB_configList, // NULL, @@ -1811,8 +1863,20 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( NULL, NULL, NULL); - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, - "[MSG] RRC Reconfiguration\n"); + LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + 0, + 0, + 0, + 0, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + true); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 70dfebaaf7..271f699ca5 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -361,14 +361,15 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ rrc_mac_config_req_gNB(rrc->module_id, rrc->carrier.ssb_SubcarrierOffset, rrc->carrier.pdsch_AntennaPorts, - rrc->carrier.pusch_AntennaPorts, + rrc->carrier.pusch_AntennaPorts, rrc->carrier.sib1_tda, rrc->carrier.minRXTXTIME, rrc->carrier.servingcellconfigcommon, &rrc->carrier.mib, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup); + ue_context_p->ue_context.secondaryCellGroup, + false); } else { rrc_mac_config_req_gNB(rrc->module_id, rrc->carrier.ssb_SubcarrierOffset, @@ -380,7 +381,8 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ NULL, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup); + ue_context_p->ue_context.secondaryCellGroup, + false); } if(m == NULL){ From a9bbf18d9f46a7c5578ca6528500f441cef61e7e Mon Sep 17 00:00:00 2001 From: rmagueta Date: Mon, 21 Mar 2022 17:14:50 +0000 Subject: [PATCH 119/233] Simplification of rrc_processing_delay computation --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index cae806e3d1..019a47ee03 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2227,8 +2227,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG NULL); sched_ctrl->rrc_processing_info.rrc_processing_timer = 0; - int rrc_processing_delay[5] = {10, 20, 40, 80, 160}; // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs - sched_ctrl->rrc_processing_info.rrc_processing_delay = rrc_processing_delay[genericParameters->subcarrierSpacing]; + sched_ctrl->rrc_processing_info.rrc_processing_delay = 10<subcarrierSpacing; // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; if (ubwpList) AssertFatal(ubwpList->list.count == 1, From bc7a404629c9d58597bc9e8f649a93fca72a2957 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Tue, 22 Mar 2022 20:18:23 +0000 Subject: [PATCH 120/233] Move rrc_processing_timer from MAC to RRC --- openair1/SIMULATION/NR_PHY/dlsim.c | 4 +- openair1/SIMULATION/NR_PHY/ulsim.c | 4 +- openair2/GNB_APP/gnb_config.c | 4 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 5 + openair2/LAYER2/NR_MAC_gNB/config.c | 16 +-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 3 - .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 22 +--- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 4 - .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 7 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 8 -- openair2/RRC/NR/L2_nr_interface.c | 17 +++ openair2/RRC/NR/nr_rrc_defs.h | 6 +- openair2/RRC/NR/rrc_gNB.c | 107 ++++++------------ openair2/RRC/NR/rrc_gNB_nsa.c | 6 +- 15 files changed, 74 insertions(+), 141 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 7edcd29d81..c3ff79ab07 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -808,9 +808,9 @@ int main(int argc, char **argv) AssertFatal((gNB->if_inst = NR_IF_Module_init(0))!=NULL,"Cannot register interface"); gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 0, 0, NULL, false); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, false); + rrc_mac_config_req_gNB(0,0, n_tx, n_tx, 0, 6, scc, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); // reset preprocessor to the one of DLSIM after it has been set during // rrc_mac_config_req_gNB gNB_mac->pre_processor_dl = nr_dlsim_preprocessor; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 8f17a9021b..5e4ae2deb7 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -742,9 +742,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,0, 0, NULL, false); + rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, false); + rrc_mac_config_req_gNB(0,0, n_tx, n_rx, 0, 6, scc, &rrc.carrier.mib,1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); frame_parms->nb_antennas_tx = n_tx; frame_parms->nb_antennas_rx = n_rx; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index d8202d7dda..f5ff7ddf9e 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2051,8 +2051,8 @@ void configure_gnb_du_mac(int inst) { NULL, 0, 0, // rnti - (NR_CellGroupConfig_t *)NULL, - false); + (NR_CellGroupConfig_t *)NULL + ); } diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index c150c4f134..a05b26599d 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -202,4 +202,9 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, const frame_t frameP, const sub_frame_t subframeP, const rnti_t rntiP); + +bool nr_rrc_disabled_mac_scheduling(const module_id_t Mod_instP, + const frame_t frameP, + const sub_frame_t subframeP, + const rnti_t rntiP); #endif diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 24294dcb29..9ad57b2d84 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -436,8 +436,7 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_BCCH_BCH_Message_t *mib, int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup, - bool rrc_reconfiguration_running) { + NR_CellGroupConfig_t *CellGroup) { if (scc != NULL ) { AssertFatal((scc->ssb_PositionsInBurst->present > 0) && (scc->ssb_PositionsInBurst->present < 4), "SSB Bitmap type %d is not valid\n",scc->ssb_PositionsInBurst->present); @@ -664,19 +663,6 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, sched_ctrl->maxL = 2; } } - - if (rrc_reconfiguration_running) { - const int UE_id = find_nr_UE_id(Mod_idP,rnti); - if (UE_id >= 0) { - NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; - NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - if (sched_ctrl->rrc_processing_info.rrc_processing_timer == 0) { - sched_ctrl->rrc_processing_info.rrc_processing_timer = 1; - LOG_I(NR_MAC, "Activating RRC processing timer for UE: %d\n", UE_id); - } - } - } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_MAC_CONFIG, VCD_FUNCTION_OUT); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 418e14c611..540198a22a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -346,9 +346,6 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, LOG_I(NR_MAC,"Frame.Slot %d.%d\n%s\n",frame,slot,stats_output); } - // This updates the RRC processing timers running at MAC layer - nr_update_rrc_processing_timer(module_idP, frame, slot); - // This schedules MIB schedule_nr_mib(module_idP, frame, slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 802225951e..9e09c704d7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -518,12 +518,12 @@ void nr_store_dlsch_buffer(module_id_t module_id, lcid = DL_SCH_LCID_DCCH; } else if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH1].bytes_in_buffer > 0) { lcid = DL_SCH_LCID_DCCH1; - } else if (sched_ctrl->rrc_processing_info.rrc_processing_timer == 0) { + } else if (!nr_rrc_disabled_mac_scheduling(module_id, frame, slot, rnti)) { lcid = DL_SCH_LCID_DTCH; } else { continue; } - + sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; //later multiplex here. Just select DCCH/SRB before DTCH/DRB sched_ctrl->lcid_to_schedule = lcid; @@ -1003,24 +1003,6 @@ nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(module_id_t module_id, int CC_id) return nr_fr1_dlsch_preprocessor; } -void nr_update_rrc_processing_timer(module_id_t module_id, - frame_t frame, - sub_frame_t slot) { - - NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; - const NR_list_t *UE_list = &UE_info->list; - for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { - NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - if (sched_ctrl->rrc_processing_info.rrc_processing_timer > 0) { - sched_ctrl->rrc_processing_info.rrc_processing_timer++; - if (sched_ctrl->rrc_processing_info.rrc_processing_timer >= sched_ctrl->rrc_processing_info.rrc_processing_delay) { - sched_ctrl->rrc_processing_info.rrc_processing_timer = 0; - LOG_I(NR_MAC, "De-activating RRC processing timer for UE: %d\n", UE_id); - } - } - } -} - void nr_schedule_ue_spec(module_id_t module_id, frame_t frame, sub_frame_t slot) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index b41d5c05c2..ac0b186218 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2254,10 +2254,6 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG scc, genericParameters, NULL); - - sched_ctrl->rrc_processing_info.rrc_processing_timer = 0; - sched_ctrl->rrc_processing_info.rrc_processing_delay = 10<subcarrierSpacing; // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs - const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; if (ubwpList) AssertFatal(ubwpList->list.count == 1, "uplinkBWP_ToAddModList has %d BWP!\n", diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index bba7f7d878..e6764b779f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1180,7 +1180,7 @@ void pf_ul(module_id_t module_id, if (B == 0 && !do_sched) continue; - if (sched_ctrl->rrc_processing_info.rrc_processing_timer > 0) { + if (nr_rrc_disabled_mac_scheduling(module_id, frame, slot, UE_info->rnti[UE_id])) { continue; } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 9d822eba99..8b734e3b95 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -55,18 +55,13 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_BCCH_BCH_Message_t *mib, int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup, - bool rrc_reconfiguration_running); + NR_CellGroupConfig_t *CellGroup); void clear_nr_nfapi_information(gNB_MAC_INST * gNB, int CC_idP, frame_t frameP, sub_frame_t subframeP); -void nr_update_rrc_processing_timer(module_id_t module_id, - frame_t frame, - sub_frame_t slot); - void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame_rxP, sub_frame_t slot_rxP); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 4ec51f964f..eccffca031 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -553,11 +553,6 @@ typedef struct NR_UE_ul_harq { NR_sched_pusch_t sched_pusch; } NR_UE_ul_harq_t; -typedef struct NR_RRC_processing_info { - uint32_t rrc_processing_timer; - uint32_t rrc_processing_delay; -} NR_RRC_processing_info_t; - /*! \brief scheduling control information set through an API */ #define MAX_CSI_REPORTS 48 typedef struct { @@ -570,9 +565,6 @@ typedef struct { NR_ControlResourceSet_t *coreset; NR_sched_pdcch_t sched_pdcch; - /// Timer for RRC processing procedures - NR_RRC_processing_info_t rrc_processing_info; - /// CCE index and Aggr. Level are shared for PUSCH/PDSCH allocation decisions /// corresponding to the sched_pusch/sched_pdsch structures below int cce_index; diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index 1cf690ac0c..54f6f7c899 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -392,3 +392,20 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, LOG_W(RRC,"Frame %d, Subframe %d: UL failure reset: UE %x unknown \n",frameP,subframeP,rntiP); } } + +bool nr_rrc_disabled_mac_scheduling(const module_id_t Mod_instP, + const frame_t frameP, + const sub_frame_t subframeP, + const rnti_t rntiP) { + bool ret = false; + struct rrc_gNB_ue_context_s *ue_context_p = NULL; + ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_instP], rntiP); + if (ue_context_p != NULL) { + if(ue_context_p->ue_context.rrc_processing_timer > 0) { + ret = true; + } + } else { + LOG_W(RRC,"Frame %d, Subframe %d: UE 0x%04x unknown \n",frameP,subframeP,rntiP); + } + return ret; +} diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 1b2c38c835..ed6a9b1555 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -96,6 +96,8 @@ typedef struct nr_uid_linear_allocator_s { #define PROTOCOL_NR_RRC_CTXT_FMT PROTOCOL_CTXT_FMT #define PROTOCOL_NR_RRC_CTXT_ARGS(CTXT_Pp) PROTOCOL_NR_CTXT_ARGS(CTXT_Pp) +// 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs +#define NR_RRC_PROCESSING_DELAY_MS 10 #define NR_UE_MODULE_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! #define NR_UE_INDEX_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! used to be -1 @@ -372,8 +374,10 @@ typedef struct gNB_RRC_UE_s { rb_id_t gnb_gtp_ebi[S1AP_MAX_E_RAB]; rb_id_t gnb_gtp_psi[S1AP_MAX_E_RAB]; //GTPV1 F1-U TUNNELS - uint32_t incoming_teid[S1AP_MAX_E_RAB]; + uint32_t incoming_teid[S1AP_MAX_E_RAB]; + uint32_t rrc_processing_timer; + uint32_t rrc_processing_delay; uint32_t ul_failure_timer; uint32_t ue_release_timer; uint32_t ue_release_timer_thres; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index b53a4470aa..d174736404 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -175,8 +175,7 @@ static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration &rrc->carrier.mib, 0, 0, // WIP hardcoded rnti - (NR_CellGroupConfig_t *)NULL, - false); + (NR_CellGroupConfig_t *)NULL); } /* set flag to indicate that cell information is configured. This is required @@ -289,8 +288,7 @@ void apply_macrlc_config(gNB_RRC_INST *rrc, NULL, 0, ue_context_pP->ue_context.rnti, - get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup : (NR_CellGroupConfig_t *)NULL, - false); + get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup : (NR_CellGroupConfig_t *)NULL); nr_rrc_rlc_config_asn1_req(ctxt_pP, ue_context_pP->ue_context.SRB_configList, @@ -475,8 +473,7 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( &rrc_instance_p->carrier.mib, 0, ue_context_pP->ue_context.rnti, - (NR_CellGroupConfig_t *)NULL, - false); + (NR_CellGroupConfig_t *)NULL); LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", @@ -727,23 +724,13 @@ rrc_gNB_generate_defaultRRCReconfiguration( NULL, ue_p->masterCellGroup); + int common_scs = rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + ue_context_pP->ue_context.rrc_processing_timer = 1; + ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<ue_context.nas_pdu.buffer); LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - 0, - 0, - 0, - 0, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - true); - /* Free all NAS PDUs */ for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { @@ -1002,20 +989,12 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( &rrc->carrier, NULL, cellGroupConfig); - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - 0, - 0, - 0, - 0, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - true); + int common_scs = rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + ue_context_pP->ue_context.rrc_processing_timer = 1; + ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<ue_context.nb_of_pdusessions; i++) { @@ -1187,20 +1166,12 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL, NULL, NULL); - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - 0, - 0, - 0, - 0, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - true); + int common_scs = RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + ue_context_pP->ue_context.rrc_processing_timer = 1; + ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<ue_context.nb_of_modify_pdusessions; i++) { @@ -1301,21 +1272,12 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( NULL, NULL); + int common_scs = RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + ue_context_pP->ue_context.rrc_processing_timer = 1; + ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<ue_context.pdu_session_release_command_flag = 1; - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - 0, - 0, - 0, - 0, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - true); + LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); /* Free all NAS PDUs */ if (nas_length > 0) { @@ -1423,8 +1385,7 @@ rrc_gNB_process_RRCReconfigurationComplete( NULL, 0, ue_context_pP->ue_context.rnti, - ue_context_pP->ue_context.masterCellGroup, - false); + ue_context_pP->ue_context.masterCellGroup); LOG_D(NR_RRC,"Configuring RLC DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); nr_rrc_rlc_config_asn1_req(ctxt_pP, SRB_configList, // NULL, @@ -1863,20 +1824,12 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( NULL, NULL, NULL); - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - 0, - 0, - 0, - 0, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - true); + int common_scs = RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; + ue_context_pP->ue_context.rrc_processing_timer = 1; + ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<ue_context.nb_of_pdusessions; i++) { @@ -3493,6 +3446,14 @@ void nr_rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { get_ul_mimo_layers(RC.nrrrc[0],ue_context_p->ue_context.UE_Capability_nr)); } } + + if (ue_context_p->ue_context.rrc_processing_timer > 0) { + ue_context_p->ue_context.rrc_processing_timer++; + if(ue_context_p->ue_context.rrc_processing_timer >= ue_context_p->ue_context.rrc_processing_delay) { + ue_context_p->ue_context.rrc_processing_timer = 0; + } + } + if (ue_context_p->ue_context.ul_failure_timer > 0) { ue_context_p->ue_context.ul_failure_timer++; diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 271f699ca5..adb44d89cb 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -368,8 +368,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ &rrc->carrier.mib, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup, - false); + ue_context_p->ue_context.secondaryCellGroup); } else { rrc_mac_config_req_gNB(rrc->module_id, rrc->carrier.ssb_SubcarrierOffset, @@ -381,8 +380,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ NULL, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup, - false); + ue_context_p->ue_context.secondaryCellGroup); } if(m == NULL){ From 07d20ba12196cf3680319d0fc3e12883d6fc2e9f Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 23 Mar 2022 09:58:55 +0100 Subject: [PATCH 121/233] fix for memory issues --- openair1/PHY/INIT/nr_init_ue.c | 17 ----------------- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 6 +++--- .../PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 4 ++-- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 19 +++++++++++-------- openair1/PHY/defs_nr_UE.h | 8 -------- 5 files changed, 16 insertions(+), 38 deletions(-) diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index a68da3257c..6d3e88faf0 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -155,23 +155,6 @@ void phy_term_nr_ue__PDSCH(NR_UE_PDSCH* pdsch, const NR_DL_FRAME_PARMS *const fp free_and_zero(pdsch->dl_ch_ptrs_estimates_ext); } -void phy_init_nr_ue_PUSCH(NR_UE_PUSCH *const pusch, - const NR_DL_FRAME_PARMS *const fp) { - AssertFatal( pusch, "pusch==0" ); - - int max_encoded_length = fp->N_RB_UL*NR_SYMBOLS_PER_SLOT*NR_NB_SC_PER_RB*8*NR_MAX_NB_LAYERS; - - for (int i=0; itxdataF_precoding[i] = (int32_t *)malloc16_clear((max_encoded_length)*sizeof(int32_t *)); - } -} - -void phy_term_nr_ue_PUSCH(NR_UE_PUSCH *pusch) -{ - for (int i = 0; i < NR_MAX_NB_LAYERS; i++) - free_and_zero(pusch->txdataF_precoding[i]); -} - int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB) { // create shortcuts diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 7a76d0736c..33b4b8b17d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -110,9 +110,9 @@ NR_gNB_ULSCH_t *new_gNB_ulsch(uint8_t max_ldpc_iterations, uint16_t N_RB_UL) ulsch->harq_processes[i] = (NR_UL_gNB_HARQ_t *)malloc16_clear(sizeof(NR_UL_gNB_HARQ_t)); ulsch->harq_processes[i]->b = (uint8_t*)malloc16_clear(ulsch_bytes); - ulsch->harq_processes[i]->c = (uint8_t**)malloc16_clear(a_segments); - ulsch->harq_processes[i]->d = (int16_t**)malloc16_clear(a_segments); - ulsch->harq_processes[i]->p_nrLDPC_procBuf = (t_nrLDPC_procBuf **)malloc16_clear(a_segments); + ulsch->harq_processes[i]->c = (uint8_t**)malloc16_clear(a_segments*sizeof(uint8_t *)); + ulsch->harq_processes[i]->d = (int16_t**)malloc16_clear(a_segments*sizeof(int16_t *)); + ulsch->harq_processes[i]->p_nrLDPC_procBuf = (t_nrLDPC_procBuf **)malloc16_clear(a_segments*sizeof(t_nrLDPC_procBuf *)); for (r=0; rharq_processes[i]->p_nrLDPC_procBuf[r] = nrLDPC_init_mem(); ulsch->harq_processes[i]->c[r] = (uint8_t*)malloc16_clear(8448*sizeof(uint8_t)); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index 0ee817ca77..30219875d9 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -149,8 +149,8 @@ NR_UE_ULSCH_t *new_nr_ue_ulsch(uint16_t N_RB_UL, int number_of_harq_pids, NR_DL_ DevAssert(ulsch->harq_processes[i]->b); bzero(ulsch->harq_processes[i]->b,ulsch_bytes); - ulsch->harq_processes[i]->c = malloc16(a_segments); - ulsch->harq_processes[i]->d = malloc16(a_segments); + ulsch->harq_processes[i]->c = malloc16(a_segments*sizeof(uint8_t *)); + ulsch->harq_processes[i]->d = malloc16(a_segments*sizeof(uint16_t *)); for (int r = 0; r < a_segments; r++) { // account for filler in first segment and CRCs for multiple segment case ulsch->harq_processes[i]->c[r] = malloc16(8448); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index e8f2b51ee7..380487172f 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -122,8 +122,6 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, int sample_offsetF, N_RE_prime; NR_DL_FRAME_PARMS *frame_parms = &UE->frame_parms; - NR_UE_PUSCH *pusch_ue = UE->pusch_vars[thread_id][gNB_id]; - int16_t **tx_precoding = (int16_t **)pusch_ue->txdataF_precoding; int32_t **txdataF = UE->common_vars.txdataF; int N_PRB_oh = 0; // higher layer (RRC) parameter xOverhead in PUSCH-ServingCellConfig @@ -333,13 +331,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, #endif } - else - memcpy(y, tx_layers[0], (available_bits/mod_order)*sizeof(int32_t)); - for (int nl = 0; nl < Nl; nl++) - free_and_zero(tx_layers[nl]); - free_and_zero(tx_layers); - /////////// //////////////////////////////////////////////////////////////////////// @@ -348,6 +340,10 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH RE mapping///////////////////////// /////////// + int encoded_length = frame_parms->N_RB_UL*number_of_symbols*NR_NB_SC_PER_RB*mod_order*Nl; + int16_t **tx_precoding = (int16_t **)malloc16_clear(Nl*sizeof(int16_t *)); + for (int nl=0; nlulsch[thread_id][gNB_id]->harq_processes[harq_pid]; harq_process_ulsch->status = SCH_IDLE; + for (int nl = 0; nl < Nl; nl++) { + free_and_zero(tx_layers[nl]); + free_and_zero(tx_precoding[nl]); + } + free_and_zero(tx_layers); + free_and_zero(tx_precoding); + /////////// //////////////////////////////////////////////////////////////////////// diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 7a8d680d70..04080e7215 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -225,11 +225,6 @@ typedef struct { } NR_UE_COMMON_PER_THREAD; -typedef struct { - /// TX buffers for UE-spec transmission - int32_t *txdataF_precoding[NR_MAX_NB_LAYERS]; - } NR_UE_PUSCH; - typedef struct { bool active[2]; fapi_nr_ul_config_pucch_pdu pucch_pdu[2]; @@ -793,9 +788,6 @@ typedef struct { NR_UE_DLSCH_t *dlsch_p[NUMBER_OF_CONNECTED_gNB_MAX]; NR_UE_DLSCH_t *dlsch_MCH[NUMBER_OF_CONNECTED_gNB_MAX]; - // to be removed again possibly - NR_UE_PUSCH *pusch_vars[RX_NB_TH_MAX][NUMBER_OF_CONNECTED_gNB_MAX]; - //Paging parameters uint32_t IMSImod1024; uint32_t PF; From 726cebaa9d49313e4b698c461bc6de791212dd6b Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 11:17:52 +0000 Subject: [PATCH 122/233] Create function to enable nr_rrc_processing_timer --- openair2/RRC/NR/L2_nr_interface.c | 2 +- openair2/RRC/NR/nr_rrc_defs.h | 4 +-- openair2/RRC/NR/rrc_gNB.c | 47 ++++++++++++++++++------------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index 54f6f7c899..ae3587e43f 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -401,7 +401,7 @@ bool nr_rrc_disabled_mac_scheduling(const module_id_t Mod_instP, struct rrc_gNB_ue_context_s *ue_context_p = NULL; ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_instP], rntiP); if (ue_context_p != NULL) { - if(ue_context_p->ue_context.rrc_processing_timer > 0) { + if(ue_context_p->ue_context.nr_rrc_processing_timer > 0) { ret = true; } } else { diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index ed6a9b1555..448b76d785 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -376,8 +376,8 @@ typedef struct gNB_RRC_UE_s { //GTPV1 F1-U TUNNELS uint32_t incoming_teid[S1AP_MAX_E_RAB]; - uint32_t rrc_processing_timer; - uint32_t rrc_processing_delay; + uint32_t nr_rrc_processing_timer; + uint32_t nr_rrc_processing_delay; uint32_t ul_failure_timer; uint32_t ue_release_timer; uint32_t ue_release_timer_thres; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index d174736404..3ca8386247 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -140,6 +140,13 @@ void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { RC.nrrrc[ctxt_pP->module_id]->carrier.Srb0.Active = 1; } +void enable_nr_rrc_processing_timer(rrc_gNB_ue_context_t *ue_context_pP, + NR_SubcarrierSpacing_t subcarrierSpacing, + uint32_t delay_ms) { + ue_context_pP->ue_context.nr_rrc_processing_timer = 1; + ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms << subcarrierSpacing; +} + ///---------------------------------------------------------------------------------------------------------------/// ///---------------------------------------------------------------------------------------------------------------/// @@ -724,9 +731,10 @@ rrc_gNB_generate_defaultRRCReconfiguration( NULL, ue_p->masterCellGroup); - int common_scs = rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - ue_context_pP->ue_context.rrc_processing_timer = 1; - ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + NR_RRC_PROCESSING_DELAY_MS); + free(ue_context_pP->ue_context.nas_pdu.buffer); LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); @@ -990,9 +998,9 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( NULL, cellGroupConfig); - int common_scs = rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - ue_context_pP->ue_context.rrc_processing_timer = 1; - ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + NR_RRC_PROCESSING_DELAY_MS); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -1167,9 +1175,9 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL, NULL); - int common_scs = RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - ue_context_pP->ue_context.rrc_processing_timer = 1; - ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + NR_RRC_PROCESSING_DELAY_MS); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); @@ -1272,9 +1280,10 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( NULL, NULL); - int common_scs = RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - ue_context_pP->ue_context.rrc_processing_timer = 1; - ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + NR_RRC_PROCESSING_DELAY_MS); + ue_context_pP->ue_context.pdu_session_release_command_flag = 1; LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -1825,9 +1834,9 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( NULL, NULL); - int common_scs = RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - ue_context_pP->ue_context.rrc_processing_timer = 1; - ue_context_pP->ue_context.rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS<module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + NR_RRC_PROCESSING_DELAY_MS); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -3447,10 +3456,10 @@ void nr_rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { } } - if (ue_context_p->ue_context.rrc_processing_timer > 0) { - ue_context_p->ue_context.rrc_processing_timer++; - if(ue_context_p->ue_context.rrc_processing_timer >= ue_context_p->ue_context.rrc_processing_delay) { - ue_context_p->ue_context.rrc_processing_timer = 0; + if (ue_context_p->ue_context.nr_rrc_processing_timer > 0) { + ue_context_p->ue_context.nr_rrc_processing_timer++; + if(ue_context_p->ue_context.nr_rrc_processing_timer >= ue_context_p->ue_context.nr_rrc_processing_delay) { + ue_context_p->ue_context.nr_rrc_processing_timer = 0; } } From 6f5ea27e0957ad91c68fc23910061f1c3f3ffc70 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 13:11:04 +0000 Subject: [PATCH 123/233] Improve nr_rrc_processing_timer procedures --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 4 ---- openair2/LAYER2/NR_MAC_gNB/config.c | 4 ++++ .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 2 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 1 + .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 5 +--- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 2 ++ openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 ++ openair2/RRC/NR/L2_nr_interface.c | 19 +-------------- openair2/RRC/NR/rrc_gNB.c | 24 ++++++++++++------- 9 files changed, 28 insertions(+), 35 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index a05b26599d..7fd870ed1f 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -203,8 +203,4 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, const sub_frame_t subframeP, const rnti_t rntiP); -bool nr_rrc_disabled_mac_scheduling(const module_id_t Mod_instP, - const frame_t frameP, - const sub_frame_t subframeP, - const rnti_t rntiP); #endif diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 9ad57b2d84..62ec240a11 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -425,6 +425,10 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts } +void nr_rrc_mac_schedule_ue_enabled(module_id_t module_id, rnti_t rnti, bool schedule_enabled) { + const int UE_id = find_nr_UE_id(module_id,rnti); + RC.nrmac[module_id]->UE_info.UE_sched_ctrl[UE_id].schedule_enabled = schedule_enabled; +} int rrc_mac_config_req_gNB(module_id_t Mod_idP, int ssb_SubcarrierOffset, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 9e09c704d7..8e4d8a3ed9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -518,7 +518,7 @@ void nr_store_dlsch_buffer(module_id_t module_id, lcid = DL_SCH_LCID_DCCH; } else if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH1].bytes_in_buffer > 0) { lcid = DL_SCH_LCID_DCCH1; - } else if (!nr_rrc_disabled_mac_scheduling(module_id, frame, slot, rnti)) { + } else if (sched_ctrl->schedule_enabled == true) { lcid = DL_SCH_LCID_DTCH; } else { continue; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index ac0b186218..1cad885d85 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2220,6 +2220,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG sched_ctrl->pucch_consecutive_dtx_cnt = 0; sched_ctrl->pusch_consecutive_dtx_cnt = 0; sched_ctrl->ul_failure = 0; + sched_ctrl->schedule_enabled = true; sched_ctrl->sched_srs.frame = -1; sched_ctrl->sched_srs.slot = -1; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index e6764b779f..f8edd3fa86 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1177,10 +1177,7 @@ void pf_ul(module_id_t module_id, const bool do_sched = nr_UE_is_to_be_scheduled(module_id, 0, UE_id, sched_pusch->frame, sched_pusch->slot); LOG_D(NR_MAC,"pf_ul: do_sched UE %d => %s\n",UE_id,do_sched ? "yes" : "no"); - if (B == 0 && !do_sched) - continue; - - if (nr_rrc_disabled_mac_scheduling(module_id, frame, slot, UE_info->rnti[UE_id])) { + if ( (B == 0 && !do_sched) || (sched_ctrl->schedule_enabled == false) ) { continue; } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 8b734e3b95..84a49182c7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -45,6 +45,8 @@ void config_common(int Mod_idP, NR_ServingCellConfigCommon_t *scc ); +void nr_rrc_mac_schedule_ue_enabled(module_id_t module_id, rnti_t rnti, bool schedule_enabled); + int rrc_mac_config_req_gNB(module_id_t Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index eccffca031..d5061972b0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -646,6 +646,8 @@ typedef struct { /// UL HARQ processes that await retransmission NR_list_t retrans_ul_harq; NR_UE_mac_ce_ctrl_t UE_mac_ce_ctrl;// MAC CE related information + /// Flag to indicate if MAC scheduling is enabled or disabled by RRC (e.g. during RRC processing time) + bool schedule_enabled; } NR_UE_sched_ctrl_t; typedef struct { diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index ae3587e43f..a1f776b5ca 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -391,21 +391,4 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, } else { LOG_W(RRC,"Frame %d, Subframe %d: UL failure reset: UE %x unknown \n",frameP,subframeP,rntiP); } -} - -bool nr_rrc_disabled_mac_scheduling(const module_id_t Mod_instP, - const frame_t frameP, - const sub_frame_t subframeP, - const rnti_t rntiP) { - bool ret = false; - struct rrc_gNB_ue_context_s *ue_context_p = NULL; - ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_instP], rntiP); - if (ue_context_p != NULL) { - if(ue_context_p->ue_context.nr_rrc_processing_timer > 0) { - ret = true; - } - } else { - LOG_W(RRC,"Frame %d, Subframe %d: UE 0x%04x unknown \n",frameP,subframeP,rntiP); - } - return ret; -} +} \ No newline at end of file diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 3ca8386247..9ee81870f4 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -140,11 +140,13 @@ void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { RC.nrrrc[ctxt_pP->module_id]->carrier.Srb0.Active = 1; } -void enable_nr_rrc_processing_timer(rrc_gNB_ue_context_t *ue_context_pP, - NR_SubcarrierSpacing_t subcarrierSpacing, - uint32_t delay_ms) { +void enable_nr_rrc_processing_timer(module_id_t module_id, + rrc_gNB_ue_context_t *ue_context_pP, + NR_SubcarrierSpacing_t subcarrierSpacing, + uint32_t delay_ms) { ue_context_pP->ue_context.nr_rrc_processing_timer = 1; ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms << subcarrierSpacing; + nr_rrc_mac_schedule_ue_enabled(module_id, ue_context_pP->ue_context.rnti, false); } ///---------------------------------------------------------------------------------------------------------------/// @@ -731,7 +733,8 @@ rrc_gNB_generate_defaultRRCReconfiguration( NULL, ue_p->masterCellGroup); - enable_nr_rrc_processing_timer(ue_context_pP, + enable_nr_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP, rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, NR_RRC_PROCESSING_DELAY_MS); @@ -998,7 +1001,8 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( NULL, cellGroupConfig); - enable_nr_rrc_processing_timer(ue_context_pP, + enable_nr_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP, rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, NR_RRC_PROCESSING_DELAY_MS); @@ -1175,7 +1179,8 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL, NULL); - enable_nr_rrc_processing_timer(ue_context_pP, + enable_nr_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP, RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, NR_RRC_PROCESSING_DELAY_MS); @@ -1280,7 +1285,8 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( NULL, NULL); - enable_nr_rrc_processing_timer(ue_context_pP, + enable_nr_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP, RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, NR_RRC_PROCESSING_DELAY_MS); @@ -1834,7 +1840,8 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( NULL, NULL); - enable_nr_rrc_processing_timer(ue_context_pP, + enable_nr_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP, RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, NR_RRC_PROCESSING_DELAY_MS); @@ -3460,6 +3467,7 @@ void nr_rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { ue_context_p->ue_context.nr_rrc_processing_timer++; if(ue_context_p->ue_context.nr_rrc_processing_timer >= ue_context_p->ue_context.nr_rrc_processing_delay) { ue_context_p->ue_context.nr_rrc_processing_timer = 0; + nr_rrc_mac_schedule_ue_enabled(ctxt_pP->module_id, ue_context_p->ue_context.rnti, true); } } From daf3fb0e2c59817f47a054a61ba33d87ccdd4b3c Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 13:29:07 +0000 Subject: [PATCH 124/233] Enable nr_rrc_processing_timer for RRCSetup --- openair2/RRC/NR/rrc_gNB.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 9ee81870f4..5a1c188602 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -353,6 +353,11 @@ rrc_gNB_generate_RRCSetup( masterCellGroup_from_DU, scc,&rrc->carrier); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP, + rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + NR_RRC_PROCESSING_DELAY_MS); + LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), ue_p->Srb0.Tx_buffer.payload_size, @@ -463,6 +468,11 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( NULL, scc,&rrc_instance_p->carrier); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP, + rrc_instance_p->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, + NR_RRC_PROCESSING_DELAY_MS); + LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), ue_p->Srb0.Tx_buffer.payload_size, From d0f6174feaf9f91d15312aa964a1fdc8068f3dae Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 14:15:16 +0000 Subject: [PATCH 125/233] Remove obsolete code --- openair2/RRC/NR/rrc_gNB.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 690eb5b890..8921d07044 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -107,27 +107,6 @@ extern RAN_CONTEXT_t RC; -extern boolean_t nr_rrc_pdcp_config_asn1_req( - const protocol_ctxt_t *const ctxt_pP, - NR_SRB_ToAddModList_t *const srb2add_list, - NR_DRB_ToAddModList_t *const drb2add_list, - NR_DRB_ToReleaseList_t *const drb2release_list, - const uint8_t security_modeP, - uint8_t *const kRRCenc, - uint8_t *const kRRCint, - uint8_t *const kUPenc, - uint8_t *const kUPint - ,LTE_PMCH_InfoList_r9_t *pmch_InfoList_r9 - ,rb_id_t *const defaultDRB, - struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list); - -extern rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP, - const NR_SRB_ToAddModList_t * const srb2add_listP, - const NR_DRB_ToAddModList_t * const drb2add_listP, - const NR_DRB_ToReleaseList_t * const drb2release_listP, - const LTE_PMCH_InfoList_r9_t * const pmch_InfoList_r9_pP, - struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list); - static inline uint64_t bitStr_to_uint64(BIT_STRING_t *asn); mui_t rrc_gNB_mui = 0; From 4510041aa6e9654874e18a386a22a8eacff815f8 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 17:08:36 +0000 Subject: [PATCH 126/233] Fix RRC processing time to match RRC ticks (at 1 ms) and add implementation for RRCSetup processing time --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 2 + openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 5 +++ openair2/RRC/NR/L2_nr_interface.c | 11 +++++ openair2/RRC/NR/nr_rrc_defs.h | 3 ++ openair2/RRC/NR/rrc_gNB.c | 40 ++++--------------- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index 7fd870ed1f..21dbee692e 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -203,4 +203,6 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, const sub_frame_t subframeP, const rnti_t rntiP); +void nr_mac_gNB_enable_rrc_processing_timer_req(const module_id_t Mod_instP, const rnti_t rntiP); + #endif diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index f36dbc4a16..9d0a2d1fbe 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1942,6 +1942,11 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram LOG_A(NR_MAC, "(ue %i, rnti 0x%04x) Received Ack of RA-Msg4. CBRA procedure succeeded!\n", UE_id, ra->rnti); UE_info->active[UE_id] = true; UE_info->Msg4_ACKed[UE_id] = true; + + // Pause scheduling according to: + // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs + sched_ctrl->schedule_enabled = false; + nr_mac_gNB_enable_rrc_processing_timer_req(module_id, ra->rnti); } else { LOG_I(NR_MAC, "(ue %i, rnti 0x%04x) RA Procedure failed at Msg4!\n", UE_id, ra->rnti); diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index a1f776b5ca..77890d4a08 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -391,4 +391,15 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, } else { LOG_W(RRC,"Frame %d, Subframe %d: UL failure reset: UE %x unknown \n",frameP,subframeP,rntiP); } +} + +void nr_mac_gNB_enable_rrc_processing_timer_req(const module_id_t Mod_instP, const rnti_t rntiP) { + struct rrc_gNB_ue_context_s *ue_context_p = NULL; + ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_instP], rntiP); + if (ue_context_p != NULL) { + ue_context_p->ue_context.nr_rrc_processing_timer = 1; + ue_context_p->ue_context.nr_rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS; + } else { + LOG_D(RRC,"%s: Unknown RNTI 0x%04x\n", __FUNCTION__, rntiP); + } } \ No newline at end of file diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 448b76d785..588ba5c03f 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -96,6 +96,9 @@ typedef struct nr_uid_linear_allocator_s { #define PROTOCOL_NR_RRC_CTXT_FMT PROTOCOL_CTXT_FMT #define PROTOCOL_NR_RRC_CTXT_ARGS(CTXT_Pp) PROTOCOL_NR_CTXT_ARGS(CTXT_Pp) +// Delay to take in consideration for sl_ahead value +#define TX_SL_AHEAD_DELAY 3 + // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs #define NR_RRC_PROCESSING_DELAY_MS 10 diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 5a1c188602..d8474de67e 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -142,10 +142,9 @@ void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { void enable_nr_rrc_processing_timer(module_id_t module_id, rrc_gNB_ue_context_t *ue_context_pP, - NR_SubcarrierSpacing_t subcarrierSpacing, uint32_t delay_ms) { ue_context_pP->ue_context.nr_rrc_processing_timer = 1; - ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms << subcarrierSpacing; + ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms; nr_rrc_mac_schedule_ue_enabled(module_id, ue_context_pP->ue_context.rnti, false); } @@ -353,11 +352,6 @@ rrc_gNB_generate_RRCSetup( masterCellGroup_from_DU, scc,&rrc->carrier); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, - ue_context_pP, - rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - NR_RRC_PROCESSING_DELAY_MS); - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), ue_p->Srb0.Tx_buffer.payload_size, @@ -468,11 +462,6 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( NULL, scc,&rrc_instance_p->carrier); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, - ue_context_pP, - rrc_instance_p->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - NR_RRC_PROCESSING_DELAY_MS); - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)(ue_p->Srb0.Tx_buffer.Payload), ue_p->Srb0.Tx_buffer.payload_size, @@ -743,10 +732,7 @@ rrc_gNB_generate_defaultRRCReconfiguration( NULL, ue_p->masterCellGroup); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, - ue_context_pP, - rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - NR_RRC_PROCESSING_DELAY_MS); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); free(ue_context_pP->ue_context.nas_pdu.buffer); @@ -1011,10 +997,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( NULL, cellGroupConfig); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, - ue_context_pP, - rrc->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - NR_RRC_PROCESSING_DELAY_MS); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -1189,10 +1172,7 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL, NULL); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, - ue_context_pP, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - NR_RRC_PROCESSING_DELAY_MS); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); @@ -1295,10 +1275,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( NULL, NULL); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, - ue_context_pP, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - NR_RRC_PROCESSING_DELAY_MS); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); ue_context_pP->ue_context.pdu_session_release_command_flag = 1; @@ -1850,10 +1827,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( NULL, NULL); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, - ue_context_pP, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, - NR_RRC_PROCESSING_DELAY_MS); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -3475,7 +3449,7 @@ void nr_rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { if (ue_context_p->ue_context.nr_rrc_processing_timer > 0) { ue_context_p->ue_context.nr_rrc_processing_timer++; - if(ue_context_p->ue_context.nr_rrc_processing_timer >= ue_context_p->ue_context.nr_rrc_processing_delay) { + if (ue_context_p->ue_context.nr_rrc_processing_timer > ue_context_p->ue_context.nr_rrc_processing_delay) { ue_context_p->ue_context.nr_rrc_processing_timer = 0; nr_rrc_mac_schedule_ue_enabled(ctxt_pP->module_id, ue_context_p->ue_context.rnti, true); } From d36549f80a988a880e65a071e4ebe2be6f393bfc Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 23 Mar 2022 19:15:02 +0100 Subject: [PATCH 127/233] fix memory issues --- cmake_targets/autotests/test_case_list.xml | 2 +- openair1/PHY/INIT/nr_init.c | 9 ++++++++- openair1/PHY/NR_TRANSPORT/nr_scrambling.c | 1 + openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 2 +- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 7 +++---- openair1/SIMULATION/NR_PHY/ulsim.c | 4 ++-- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml index 36dbe9de07..5687810e69 100755 --- a/cmake_targets/autotests/test_case_list.xml +++ b/cmake_targets/autotests/test_case_list.xml @@ -1324,7 +1324,7 @@ -n100 -s5 -T 2 1 2 -U 2 0 2 -n100 -s5 -T 2 2 2 -U 2 1 2 -n100 -s5 -a4 -b8 -T 2 1 2 -U 2 1 3 - -n100 -s5 -Z + -n100 -Z -s5 -n100 -s5 -Z -r75 -n50 -s5 -Z -r216 -R217 -n50 -s5 -Z -r270 -R273 diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 131e74469e..8e698b09cd 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -285,7 +285,7 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, for (i=0; irxdataF_ext[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); pusch_vars[ULSCH_id]->rxdataF_ext2[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot ); - pusch_vars[ULSCH_id]->rho[i] = (int32_t **)malloc16_clear( NR_MAX_NB_LAYERS*NR_MAX_NB_LAYERS*sizeof(int32_t*)); + pusch_vars[ULSCH_id]->rho[i] = (int32_t **)malloc16_clear(NR_MAX_NB_LAYERS*NR_MAX_NB_LAYERS*sizeof(int32_t*)); for (int j=0; j< max_ul_mimo_layers; j++) { for (int k=0; kpusch_vars; for (int ULSCH_id=0; ULSCH_idnumber_of_nr_ulsch_max; ULSCH_id++) { + for (int i=0; i< max_ul_mimo_layers; i++) + free_and_zero(pusch_vars[ULSCH_id]->llr_layers[i]); for (int i = 0; i < Prx; i++) { free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext[i]); free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext2[i]); + for (int j=0; j< max_ul_mimo_layers; j++) { + for (int k=0; krho[i][j*max_ul_mimo_layers+k]); + } free_and_zero(pusch_vars[ULSCH_id]->rho[i]); } for (int i = 0; i < n_buf; i++) { @@ -434,6 +440,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_and_zero(pusch_vars[ULSCH_id]->ul_ch_mag[i]); free_and_zero(pusch_vars[ULSCH_id]->ul_ch_magb[i]); } + free_and_zero(pusch_vars[ULSCH_id]->llr_layers); free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext); free_and_zero(pusch_vars[ULSCH_id]->rxdataF_ext2); free_and_zero(pusch_vars[ULSCH_id]->ul_ch_estimates); diff --git a/openair1/PHY/NR_TRANSPORT/nr_scrambling.c b/openair1/PHY/NR_TRANSPORT/nr_scrambling.c index 4bbecfd994..8a49a7ffe0 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_scrambling.c +++ b/openair1/PHY/NR_TRANSPORT/nr_scrambling.c @@ -52,6 +52,7 @@ void nr_codeword_scrambling(uint8_t *in, ((uint16_t*)&in32)[0] = _mm_movemask_epi8(_mm_slli_epi16(in128[0],7)); ((uint16_t*)&in32)[1] = _mm_movemask_epi8(_mm_slli_epi16(in128[1],7)); out[i]=(in32^s); + LOG_D(PHY,"in[%d] %x => %x\n",i,in32,out[i]); s=lte_gold_generic(&x1, &x2, 0); } //#elsif defined(__arm__) || defined(__aarch64) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index 30219875d9..1acb722ecd 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -318,7 +318,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, printf("start ldpc encoder segment %d/%d\n",r,harq_process->C); printf("input %d %d %d %d %d \n", harq_process->c[r][0], harq_process->c[r][1], harq_process->c[r][2],harq_process->c[r][3], harq_process->c[r][4]); for (int cnt =0 ; cnt < 22*(*pz)/8; cnt ++){ - printf("%d ", harq_process->c[r][cnt]); + printf("%d ", harq_process->c[r][cnt]); } printf("\n"); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 380487172f..740299477b 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -114,7 +114,6 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, LOG_D(PHY,"nr_ue_ulsch_procedures hard_id %d %d.%d\n",harq_pid,frame,slot); - uint32_t available_bits; int8_t Wf[2], Wt[2]; int l_prime[2], delta; uint8_t nb_dmrs_re_per_rb; @@ -183,7 +182,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH scrambling///////////////////////// /////////// - available_bits = G; + uint32_t available_bits = G; uint32_t scrambled_output[(available_bits>>5)+1]; memset(scrambled_output, 0, ((available_bits>>5)+1)*sizeof(uint32_t)); @@ -200,7 +199,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH modulation///////////////////////// /////////// - int max_num_re = number_of_symbols*nb_rb*NR_NB_SC_PER_RB; + int max_num_re = Nl*number_of_symbols*nb_rb*NR_NB_SC_PER_RB; int32_t d_mod[max_num_re] __attribute__ ((aligned(16))); nr_modulation(scrambled_output, // assume one codeword for the moment @@ -340,7 +339,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /////////////////////////ULSCH RE mapping///////////////////////// /////////// - int encoded_length = frame_parms->N_RB_UL*number_of_symbols*NR_NB_SC_PER_RB*mod_order*Nl; + int encoded_length = frame_parms->N_RB_UL*14*NR_NB_SC_PER_RB*mod_order*Nl; int16_t **tx_precoding = (int16_t **)malloc16_clear(Nl*sizeof(int16_t *)); for (int nl=0; nlul_dmrs_symb_pos); ptrsSymbPerSlot = get_ptrs_symbols_in_slot(ptrsSymPos, pusch_pdu->start_symbol_index, pusch_pdu->nr_of_symbols); ptrsRePerSymb = ((pusch_pdu->rb_size + ptrs_freq_density - 1)/ptrs_freq_density); - printf("[ULSIM] PTRS Symbols in a slot: %2u, RE per Symbol: %3u, RE in a slot %4d\n", ptrsSymbPerSlot,ptrsRePerSymb, ptrsSymbPerSlot*ptrsRePerSymb ); + LOG_D(PHY,"[ULSIM] PTRS Symbols in a slot: %2u, RE per Symbol: %3u, RE in a slot %4d\n", ptrsSymbPerSlot,ptrsRePerSymb, ptrsSymbPerSlot*ptrsRePerSymb ); } //////////////////////////////////////////////////////////// From 7ba2517334dfe2af09abd2574bce7ed3f2953ce8 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 20:20:12 +0000 Subject: [PATCH 128/233] Improve nr_rrc_processing_timer to support BWP switching --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 10 ++++++---- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 2 -- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 4 ---- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 3 +-- openair2/RRC/NR/L2_nr_interface.c | 8 +++++++- openair2/RRC/NR/nr_rrc_defs.h | 1 + openair2/RRC/NR/rrc_gNB.c | 20 +++++++++++++++++-- 7 files changed, 33 insertions(+), 15 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 954dfbfb91..8fd98a0fe3 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -518,7 +518,7 @@ void nr_store_dlsch_buffer(module_id_t module_id, lcid = DL_SCH_LCID_DCCH; } else if (sched_ctrl->rlc_status[DL_SCH_LCID_DCCH1].bytes_in_buffer > 0) { lcid = DL_SCH_LCID_DCCH1; - } else if ( (sched_ctrl->bwp_switch_info.bwp_switch_state != BWP_SWITCH_RUNNING) || (sched_ctrl->schedule_enabled == true) ) { + } else if (sched_ctrl->schedule_enabled == true) { lcid = DL_SCH_LCID_DTCH; } else { continue; @@ -1015,14 +1015,16 @@ void nr_bwp_switch(module_id_t module_id, case BWP_SWITCH_TO_START: LOG_W(NR_MAC,"(%d.%d) [UE_id %d] Schedule BWP switch from bwp_id %ld to %d\n", frame, slot, UE_id, UE_info->UE_sched_ctrl[UE_id].active_bwp->bwp_Id, bwp_id); - sched_ctrl->bwp_switch_info.bwp_switch_timer = 0; sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_RUNNING; + sched_ctrl->bwp_switch_info.bwp_switch_slot = slot; nr_mac_rrc_bwp_switch_req(module_id, frame, slot, UE_info->rnti[UE_id], bwp_id); break; case BWP_SWITCH_RUNNING: - sched_ctrl->bwp_switch_info.bwp_switch_timer++; - if(sched_ctrl->bwp_switch_info.bwp_switch_timer == sched_ctrl->bwp_switch_info.bwp_switch_delay) { + // The BWP switching takes at least 10+6 ms (3GPP TS 38.331 Section 12) + // gNB needs time to schedule and send RRCReconfiguration message + // Therefore, we wait for the same slot in next frame (10 ms) before update bwp_id in the gNB + if (sched_ctrl->bwp_switch_info.bwp_switch_slot == slot) { const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; const int bwp_id = servingCellConfig && servingCellConfig->firstActiveDownlinkBWP_Id ? diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 453a10f82a..b0b877f84e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2260,9 +2260,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG NULL); sched_ctrl->bwp_switch_info.current_bwp = bwp_id; sched_ctrl->bwp_switch_info.next_bwp = -1; - sched_ctrl->bwp_switch_info.bwp_switch_timer = 0; sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_INACTIVE; - sched_ctrl->bwp_switch_info.bwp_switch_delay = 40; const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; if (ubwpList) AssertFatal(ubwpList->list.count <= NR_MAX_NUM_BWP, "uplinkBWP_ToAddModList has %d BWP!\n", diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3e1448ce87..15db47d9cb 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1181,10 +1181,6 @@ void pf_ul(module_id_t module_id, continue; } - if(sched_ctrl->bwp_switch_info.bwp_switch_state == BWP_SWITCH_RUNNING) { - continue; - } - /* Schedule UE on SR or UL inactivity and no data (otherwise, will be scheduled * based on data to transmit) */ if (B == 0 && do_sched) { diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 0bb26e6f55..ae33a3b6b4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -566,8 +566,7 @@ typedef enum { typedef struct NR_BWP_switch_info { NR_BWP_Id_t current_bwp; NR_BWP_Id_t next_bwp; - int bwp_switch_timer; - uint8_t bwp_switch_delay; + sub_frame_t bwp_switch_slot; NR_BWP_switch_states_t bwp_switch_state; } NR_BWP_switch_info_t; diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index 06489e18b4..19b6a83105 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -414,7 +414,13 @@ void nr_mac_gNB_enable_rrc_processing_timer_req(const module_id_t Mod_instP, con ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_instP], rntiP); if (ue_context_p != NULL) { ue_context_p->ue_context.nr_rrc_processing_timer = 1; - ue_context_p->ue_context.nr_rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS; + uint32_t delay_ms = NR_RRC_PROCESSING_DELAY_MS; + if (ue_context_p->ue_context.masterCellGroup && + ue_context_p->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_p->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { + delay_ms += NR_RRC_BWP_SWITCH_DELAY_MS; + } + ue_context_p->ue_context.nr_rrc_processing_delay = delay_ms; } else { LOG_D(RRC,"%s: Unknown RNTI 0x%04x\n", __FUNCTION__, rntiP); } diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index f7e6ab3943..3927c4ec26 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -101,6 +101,7 @@ typedef struct nr_uid_linear_allocator_s { // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs #define NR_RRC_PROCESSING_DELAY_MS 10 +#define NR_RRC_BWP_SWITCH_DELAY_MS 6 #define NR_UE_MODULE_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! #define NR_UE_INDEX_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! used to be -1 diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 188810f626..1f0dc9d8dd 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -743,7 +743,14 @@ rrc_gNB_generate_defaultRRCReconfiguration( NULL, ue_p->masterCellGroup); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); + uint32_t delay_ms = TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS; + if (ue_p->masterCellGroup && + ue_p->masterCellGroup->spCellConfig && + ue_p->masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_p->masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { + delay_ms += NR_RRC_BWP_SWITCH_DELAY_MS; + } + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, delay_ms); free(ue_context_pP->ue_context.nas_pdu.buffer); @@ -1008,7 +1015,14 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( NULL, cellGroupConfig); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); + uint32_t delay_ms = TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS; + if (cellGroupConfig && + cellGroupConfig->spCellConfig && + cellGroupConfig->spCellConfig->spCellConfigDedicated && + cellGroupConfig->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { + delay_ms += NR_RRC_BWP_SWITCH_DELAY_MS; + } + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, delay_ms); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -1914,6 +1928,8 @@ int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP NULL, masterCellGroup); + enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS); + nr_rrc_data_req(ctxt_pP, DCCH, rrc_gNB_mui++, From 9c45ee05bb60c81c4f829901749cab5abb5b76aa Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 22:17:03 +0000 Subject: [PATCH 129/233] Fix RIV value for 162 PRBs --- .../conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf | 4 ++-- .../CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf index 2361437f7c..fef2a47b9b 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf @@ -83,7 +83,7 @@ gNBs = #initialDownlinkBWP #genericParameters # this is RBstart=0,L=106 (275*(L-1))+RBstart - initialDLBWPlocationAndBandwidth = 31899; + initialDLBWPlocationAndBandwidth = 31624; # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialDLBWPsubcarrierSpacing = 1; @@ -103,7 +103,7 @@ gNBs = pMax = 20; #initialUplinkBWP #genericParameters - initialULBWPlocationAndBandwidth = 31899; + initialULBWPlocationAndBandwidth = 31624; # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialULBWPsubcarrierSpacing = 1; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf index fac54a298b..3e2c8fc95c 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf @@ -66,7 +66,7 @@ gNBs = #initialDownlinkBWP #genericParameters # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) - initialDLBWPlocationAndBandwidth = 31899; + initialDLBWPlocationAndBandwidth = 31624; # # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 @@ -100,7 +100,7 @@ gNBs = pMax = 20; #initialUplinkBWP #genericParameters - initialULBWPlocationAndBandwidth = 31899; + initialULBWPlocationAndBandwidth = 31624; # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialULBWPsubcarrierSpacing = 1; From 5d6d25c97233270e43f6e698104431ed8a36d5a7 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 23 Mar 2022 22:34:32 +0000 Subject: [PATCH 130/233] Fix RIV value for 162 PRBs, update and synchronization of configuration files --- .../gnb.sa.band78.217PRB.2x2.usrpn300.conf | 336 ------------------ .../gnb.sa.band78.273PRB.2x2.usrpn300.conf | 336 ------------------ ...gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf | 33 +- ...gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf | 4 +- ...gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf | 6 +- 5 files changed, 7 insertions(+), 708 deletions(-) delete mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf delete mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf deleted file mode 100644 index 1f4673b18f..0000000000 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.217PRB.2x2.usrpn300.conf +++ /dev/null @@ -1,336 +0,0 @@ -Active_gNBs = ( "gNB-OAI"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -gNBs = -( - { - ////////// Identification parameters: - gNB_ID = 0xe00; - gNB_name = "gNB-OAI"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - plmn_list = ({ - mcc = 208; - mnc = 99; - mnc_length = 2; - snssaiList = ( - { - sst = 1; - sd = 0x1; // 0 false, else true - }, - { - sst = 1; - sd = 0x112233; // 0 false, else true - } - ); - - }); - - nr_cellid = 12345678L; - - ////////// Physical parameters: - - ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts = 1; - pusch_AntennaPorts = 2; -# do_CSIRS = 1; - min_rxtxtime = 2; - ul_prbblacklist = "107,108,109,110" - - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 10; - } - ); - - servingCellConfigCommon = ( - { - #spCellConfigCommon - - physCellId = 0; - -# downlinkConfigCommon - #frequencyInfoDL - # this is 3300.24 + 106*12*30e3 = 3338.40 MHz (5G NR GSCN: 7734) - absoluteFrequencySSB = 622560; - dl_frequencyBand = 78; - # this is 3300.24 MHz - dl_absoluteFrequencyPointA = 620016; - #scs-SpecificCarrierList - dl_offstToCarrier = 0; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_subcarrierSpacing = 1; - dl_carrierBandwidth = 217; - #initialDownlinkBWP - #genericParameters - # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) - initialDLBWPlocationAndBandwidth = 31899; - # -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - initialDLBWPsubcarrierSpacing = 1; - #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 12; - initialDLBWPsearchSpaceZero = 10; - - #uplinkConfigCommon - #frequencyInfoUL - ul_frequencyBand = 78; - #scs-SpecificCarrierList - ul_offstToCarrier = 0; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_subcarrierSpacing = 1; - ul_carrierBandwidth = 217; - pMax = 20; - #initialUplinkBWP - #genericParameters - initialULBWPlocationAndBandwidth = 31899; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - initialULBWPsubcarrierSpacing = 1; - #rach-ConfigCommon - #rach-ConfigGeneric - prach_ConfigurationIndex = 98; -#prach_msg1_FDM -#0 = one, 1=two, 2=four, 3=eight - prach_msg1_FDM = 0; - prach_msg1_FrequencyStart = 0; - zeroCorrelationZoneConfig = 12; - preambleReceivedTargetPower = -96; -#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) - preambleTransMax = 6; -#powerRampingStep -# 0=dB0,1=dB2,2=dB4,3=dB6 - powerRampingStep = 1; -#ra_ReponseWindow -#1,2,4,8,10,20,40,80 - ra_ResponseWindow = 4; -#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR -#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen - ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3; -#oneHalf (0..15) 4,8,12,16,...60,64 - ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; -#ra_ContentionResolutionTimer -#(0..7) 8,16,24,32,40,48,56,64 - ra_ContentionResolutionTimer = 7; - rsrp_ThresholdSSB = 19; -#prach-RootSequenceIndex_PR -#1 = 839, 2 = 139 - prach_RootSequenceIndex_PR = 2; - prach_RootSequenceIndex = 1; - # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex - # - msg1_SubcarrierSpacing = 1, -# restrictedSetConfig -# 0=unrestricted, 1=restricted type A, 2=restricted type B - restrictedSetConfig = 0, - - msg3_DeltaPreamble = 1; - p0_NominalWithGrant =-90; - -# pucch-ConfigCommon setup : -# pucchGroupHopping -# 0 = neither, 1= group hopping, 2=sequence hopping - pucchGroupHopping = 0; - hoppingId = 40; - p0_nominal = -70; -# ssb_PositionsInBurs_BitmapPR -# 1=short, 2=medium, 3=long - ssb_PositionsInBurst_PR = 2; - ssb_PositionsInBurst_Bitmap = 1; - -# ssb_periodicityServingCell -# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 - ssb_periodicityServingCell = 2; - -# dmrs_TypeA_position -# 0 = pos2, 1 = pos3 - dmrs_TypeA_Position = 0; - -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - subcarrierSpacing = 1; - - - #tdd-UL-DL-ConfigurationCommon -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - referenceSubcarrierSpacing = 1; - # pattern1 - # dl_UL_TransmissionPeriodicity - # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 - dl_UL_TransmissionPeriodicity = 6; - nrofDownlinkSlots = 7; - nrofDownlinkSymbols = 6; - nrofUplinkSlots = 2; - nrofUplinkSymbols = 4; - - ssPBCH_BlockPower = -25; - } - - ); - - -# Dedicated Serving Cell Configuration -servingCellConfigDedicated = ({ - # BWP-Downlink - # BWP 1 Configuration - dl_bwp-Id_1 = 1; - dl_bwp1_locationAndBandwidth = 16499; // RBstart=0, L=217 (80 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp1_subcarrierSpacing = 1; - - firstActiveDownlinkBWP-Id = 1; #BWP-Id - defaultDownlinkBWP-Id = 1; #BWP-Id - - # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, - # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, - # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, - # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } - - # UplinkConfig - # BWP-Uplink - # BWP 1 Configuration - ul_bwp-Id_1 = 1; - ul_bwp1_locationAndBandwidth = 16499; // RBstart=0, L=217 (80 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp1_subcarrierSpacing = 1; - - firstActiveUplinkBWP-Id = 1; #BWP-Id - } -); - - - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - - - ////////// AMF parameters: - amf_ip_address = ( { ipv4 = "192.168.70.132"; - ipv6 = "192:168:30::17"; - active = "yes"; - preference = "ipv4"; - } - ); - - - NETWORK_INTERFACES : - { - GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; - GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; - GNB_PORT_FOR_S1U = 2152; # Spec 2152 - }; - - } -); - -MACRLCs = ( -{ - num_cc = 1; - tr_s_preference = "local_L1"; - tr_n_preference = "local_RRC"; - pusch_TargetSNRx10 = 200; - pucch_TargetSNRx10 = 200; - ulsch_max_frame_inactivity = 0; -} -); - -L1s = ( -{ - num_cc = 1; - tr_n_preference = "local_mac"; - prach_dtx_threshold = 120; - pucch0_dtx_threshold = 120; -} -); - -RUs = ( -{ - local_rf = "yes" - nb_tx = 2 - nb_rx = 2 - att_tx = 0 - att_rx = 0; - bands = [78]; - max_pdschReferenceSignalPower = -27; - max_rxgain = 75; - sf_extension = 0; - eNB_instances = [0]; - ##beamforming 1x2 matrix: 1 layer x 2 antennas - bf_weights = [0x00007fff, 0x0000]; - #clock_src = "internal"; - sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" -} -); - -THREAD_STRUCT = ( -{ - #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" - parallel_config = "PARALLEL_SINGLE_THREAD"; - #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" - worker_config = "WORKER_ENABLE"; -} -); - -rfsimulator : -{ - serveraddr = "server"; - serverport = "4043"; - options = (); #("saviq"); or/and "chanmod" - modelname = "AWGN"; - IQfile = "/tmp/rfsimulator.iqs"; -}; - -security = { - # preferred ciphering algorithms - # the first one of the list that an UE supports in chosen - # valid values: nea0, nea1, nea2, nea3 - ciphering_algorithms = ( "nea0" ); - - # preferred integrity algorithms - # the first one of the list that an UE supports in chosen - # valid values: nia0, nia1, nia2, nia3 - integrity_algorithms = ( "nia2", "nia0" ); - - # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter - # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' - drb_ciphering = "yes"; - drb_integrity = "no"; -}; - -log_config : -{ - global_log_level ="info"; - global_log_verbosity ="medium"; - hw_log_level ="info"; - hw_log_verbosity ="medium"; - phy_log_level ="info"; - phy_log_verbosity ="medium"; - mac_log_level ="info"; - mac_log_verbosity ="high"; - rlc_log_level ="info"; - rlc_log_verbosity ="medium"; - pdcp_log_level ="info"; - pdcp_log_verbosity ="medium"; - rrc_log_level ="info"; - rrc_log_verbosity ="medium"; - ngap_log_level ="debug"; - ngap_log_verbosity ="medium"; - f1ap_log_level ="debug"; - f1ap_log_verbosity ="medium"; -}; \ No newline at end of file diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf deleted file mode 100644 index fc596ded03..0000000000 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273PRB.2x2.usrpn300.conf +++ /dev/null @@ -1,336 +0,0 @@ -Active_gNBs = ( "gNB-OAI"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -gNBs = -( - { - ////////// Identification parameters: - gNB_ID = 0xe00; - gNB_name = "gNB-OAI"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - plmn_list = ({ - mcc = 208; - mnc = 99; - mnc_length = 2; - snssaiList = ( - { - sst = 1; - sd = 0x1; // 0 false, else true - }, - { - sst = 1; - sd = 0x112233; // 0 false, else true - } - ); - - }); - - nr_cellid = 12345678L; - - ////////// Physical parameters: - - ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts = 1; - pusch_AntennaPorts = 2; -# do_CSIRS = 1; - min_rxtxtime = 2; - ul_prbblacklist = "107,108,109,110" - - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 10; - } - ); - - servingCellConfigCommon = ( - { - #spCellConfigCommon - - physCellId = 0; - -# downlinkConfigCommon - #frequencyInfoDL - # this is 3300.24 + 134*12*30e3 = 3348.48 MHz (5G NR GSCN: 7741) - absoluteFrequencySSB = 623232; - dl_frequencyBand = 78; - # this is 3300.24 MHz - dl_absoluteFrequencyPointA = 620016; - #scs-SpecificCarrierList - dl_offstToCarrier = 0; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_subcarrierSpacing = 1; - dl_carrierBandwidth = 273; - #initialDownlinkBWP - #genericParameters - # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) - initialDLBWPlocationAndBandwidth = 31899; - # -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - initialDLBWPsubcarrierSpacing = 1; - #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 12; - initialDLBWPsearchSpaceZero = 10; - - #uplinkConfigCommon - #frequencyInfoUL - ul_frequencyBand = 78; - #scs-SpecificCarrierList - ul_offstToCarrier = 0; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_subcarrierSpacing = 1; - ul_carrierBandwidth = 273; - pMax = 20; - #initialUplinkBWP - #genericParameters - initialULBWPlocationAndBandwidth = 31899; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - initialULBWPsubcarrierSpacing = 1; - #rach-ConfigCommon - #rach-ConfigGeneric - prach_ConfigurationIndex = 98; -#prach_msg1_FDM -#0 = one, 1=two, 2=four, 3=eight - prach_msg1_FDM = 0; - prach_msg1_FrequencyStart = 0; - zeroCorrelationZoneConfig = 12; - preambleReceivedTargetPower = -90; -#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) - preambleTransMax = 6; -#powerRampingStep -# 0=dB0,1=dB2,2=dB4,3=dB6 - powerRampingStep = 1; -#ra_ReponseWindow -#1,2,4,8,10,20,40,80 - ra_ResponseWindow = 4; -#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR -#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen - ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3; -#oneHalf (0..15) 4,8,12,16,...60,64 - ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; -#ra_ContentionResolutionTimer -#(0..7) 8,16,24,32,40,48,56,64 - ra_ContentionResolutionTimer = 7; - rsrp_ThresholdSSB = 19; -#prach-RootSequenceIndex_PR -#1 = 839, 2 = 139 - prach_RootSequenceIndex_PR = 2; - prach_RootSequenceIndex = 1; - # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex - # - msg1_SubcarrierSpacing = 1, -# restrictedSetConfig -# 0=unrestricted, 1=restricted type A, 2=restricted type B - restrictedSetConfig = 0, - - msg3_DeltaPreamble = 1; - p0_NominalWithGrant =-90; - -# pucch-ConfigCommon setup : -# pucchGroupHopping -# 0 = neither, 1= group hopping, 2=sequence hopping - pucchGroupHopping = 0; - hoppingId = 40; - p0_nominal = -70; -# ssb_PositionsInBurs_BitmapPR -# 1=short, 2=medium, 3=long - ssb_PositionsInBurst_PR = 2; - ssb_PositionsInBurst_Bitmap = 1; - -# ssb_periodicityServingCell -# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 - ssb_periodicityServingCell = 2; - -# dmrs_TypeA_position -# 0 = pos2, 1 = pos3 - dmrs_TypeA_Position = 0; - -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - subcarrierSpacing = 1; - - - #tdd-UL-DL-ConfigurationCommon -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - referenceSubcarrierSpacing = 1; - # pattern1 - # dl_UL_TransmissionPeriodicity - # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 - dl_UL_TransmissionPeriodicity = 6; - nrofDownlinkSlots = 7; - nrofDownlinkSymbols = 6; - nrofUplinkSlots = 2; - nrofUplinkSymbols = 4; - - ssPBCH_BlockPower = -25; - } - - ); - - -# Dedicated Serving Cell Configuration -servingCellConfigDedicated = ({ - # BWP-Downlink - # BWP 1 Configuration - dl_bwp-Id_1 = 1; - dl_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp1_subcarrierSpacing = 1; - - firstActiveDownlinkBWP-Id = 1; #BWP-Id - defaultDownlinkBWP-Id = 1; #BWP-Id - - # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, - # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, - # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, - # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } - - # UplinkConfig - # BWP-Uplink - # BWP 1 Configuration - ul_bwp-Id_1 = 1; - ul_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp1_subcarrierSpacing = 1; - - firstActiveUplinkBWP-Id = 1; #BWP-Id - } -); - - - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - - - ////////// AMF parameters: - amf_ip_address = ( { ipv4 = "192.168.70.132"; - ipv6 = "192:168:30::17"; - active = "yes"; - preference = "ipv4"; - } - ); - - - NETWORK_INTERFACES : - { - GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; - GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; - GNB_PORT_FOR_S1U = 2152; # Spec 2152 - }; - - } -); - -MACRLCs = ( -{ - num_cc = 1; - tr_s_preference = "local_L1"; - tr_n_preference = "local_RRC"; - pusch_TargetSNRx10 = 200; - pucch_TargetSNRx10 = 200; - ulsch_max_frame_inactivity = 0; -} -); - -L1s = ( -{ - num_cc = 1; - tr_n_preference = "local_mac"; - prach_dtx_threshold = 120; - pucch0_dtx_threshold = 120; -} -); - -RUs = ( -{ - local_rf = "yes" - nb_tx = 2 - nb_rx = 2 - att_tx = 0 - att_rx = 0; - bands = [78]; - max_pdschReferenceSignalPower = -27; - max_rxgain = 75; - sf_extension = 0; - eNB_instances = [0]; - ##beamforming 1x2 matrix: 1 layer x 2 antennas - bf_weights = [0x00007fff, 0x0000]; - #clock_src = "internal"; - sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" -} -); - -THREAD_STRUCT = ( -{ - #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" - parallel_config = "PARALLEL_SINGLE_THREAD"; - #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" - worker_config = "WORKER_ENABLE"; -} -); - -rfsimulator : -{ - serveraddr = "server"; - serverport = "4043"; - options = (); #("saviq"); or/and "chanmod" - modelname = "AWGN"; - IQfile = "/tmp/rfsimulator.iqs"; -}; - -security = { - # preferred ciphering algorithms - # the first one of the list that an UE supports in chosen - # valid values: nea0, nea1, nea2, nea3 - ciphering_algorithms = ( "nea0" ); - - # preferred integrity algorithms - # the first one of the list that an UE supports in chosen - # valid values: nia0, nia1, nia2, nia3 - integrity_algorithms = ( "nia2", "nia0" ); - - # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter - # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' - drb_ciphering = "yes"; - drb_integrity = "no"; -}; - -log_config : -{ - global_log_level ="info"; - global_log_verbosity ="medium"; - hw_log_level ="info"; - hw_log_verbosity ="medium"; - phy_log_level ="info"; - phy_log_verbosity ="medium"; - mac_log_level ="info"; - mac_log_verbosity ="high"; - rlc_log_level ="info"; - rlc_log_verbosity ="medium"; - pdcp_log_level ="info"; - pdcp_log_verbosity ="medium"; - rrc_log_level ="info"; - rrc_log_verbosity ="medium"; - ngap_log_level ="debug"; - ngap_log_verbosity ="medium"; - f1ap_log_level ="debug"; - f1ap_log_verbosity ="medium"; -}; \ No newline at end of file diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf index 3e2c8fc95c..7ea628ddf4 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf @@ -40,7 +40,7 @@ gNBs = ul_prbblacklist = "79,80,81,82" pdcch_ConfigSIB1 = ( { - controlResourceSetZero = 11; + controlResourceSetZero = 12; searchSpaceZero = 10; } ); @@ -72,21 +72,8 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialDLBWPsubcarrierSpacing = 1; #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPcontrolResourceSetZero = 12; initialDLBWPsearchSpaceZero = 10; - #pdsch-ConfigCommon - #pdschTimeDomainAllocationList (up to 16 entries) - initialDLBWPk0_0 = 0; - #initialULBWPmappingType - #0=typeA,1=typeB - initialDLBWPmappingType_0 = 0; - #this is SS=1,L=13 - initialDLBWPstartSymbolAndLength_0 = 40; - - initialDLBWPk0_1 = 0; - initialDLBWPmappingType_1 = 0; - #this is SS=1,L=5 - initialDLBWPstartSymbolAndLength_1 = 57; #uplinkConfigCommon #frequencyInfoUL @@ -141,22 +128,6 @@ gNBs = # 0=unrestricted, 1=restricted type A, 2=restricted type B restrictedSetConfig = 0, - # pusch-ConfigCommon (up to 16 elements) - initialULBWPk2_0 = 2; - initialULBWPmappingType_0 = 1 - # this is SS=2 L=13 - initialULBWPstartSymbolAndLength_0 = 41; - - initialULBWPk2_1 = 2; - initialULBWPmappingType_1 = 1; - # this is SS=0 L=4 - initialULBWPstartSymbolAndLength_1 = 52; - - initialULBWPk2_2 = 7; - initialULBWPmappingType_2 = 1; - # this is SS=10 L=4 - initialULBWPstartSymbolAndLength_2 = 52; - msg3_DeltaPreamble = 1; p0_NominalWithGrant =-90; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf index 1f4673b18f..264ab137c0 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf @@ -68,7 +68,7 @@ gNBs = #initialDownlinkBWP #genericParameters # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) - initialDLBWPlocationAndBandwidth = 31899; + initialDLBWPlocationAndBandwidth = 31624; # # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 @@ -89,7 +89,7 @@ gNBs = pMax = 20; #initialUplinkBWP #genericParameters - initialULBWPlocationAndBandwidth = 31899; + initialULBWPlocationAndBandwidth = 31624; # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialULBWPsubcarrierSpacing = 1; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf index fc596ded03..7a1d087715 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf @@ -37,7 +37,7 @@ gNBs = pusch_AntennaPorts = 2; # do_CSIRS = 1; min_rxtxtime = 2; - ul_prbblacklist = "107,108,109,110" + ul_prbblacklist = "135,136,137,138" pdcch_ConfigSIB1 = ( { @@ -68,7 +68,7 @@ gNBs = #initialDownlinkBWP #genericParameters # this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart)) - initialDLBWPlocationAndBandwidth = 31899; + initialDLBWPlocationAndBandwidth = 31624; # # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 @@ -89,7 +89,7 @@ gNBs = pMax = 20; #initialUplinkBWP #genericParameters - initialULBWPlocationAndBandwidth = 31899; + initialULBWPlocationAndBandwidth = 31624; # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialULBWPsubcarrierSpacing = 1; From a1739a1175d5874790e5aefbc56c994b0b0167ed Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 25 Mar 2022 00:59:43 +0000 Subject: [PATCH 131/233] Avoid segmentation fault --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 0a61d38357..2ecc704c73 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -749,7 +749,8 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; uint32_t n_RS_Id = 0; - if (NR_DMRS_ulconfig->transformPrecodingEnabled->nPUSCH_Identity != NULL) + if (NR_DMRS_ulconfig->transformPrecodingEnabled && + NR_DMRS_ulconfig->transformPrecodingEnabled->nPUSCH_Identity != NULL) n_RS_Id = *NR_DMRS_ulconfig->transformPrecodingEnabled->nPUSCH_Identity; else n_RS_Id = *mac->scc->physCellId; From 11577f6b0b9ec2539664a9d76d1015d5bbbfe8eb Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 25 Mar 2022 01:01:29 +0000 Subject: [PATCH 132/233] Fix DU to CU RRC message encode --- openair2/COMMON/f1ap_messages_types.h | 6 ++++-- openair2/RRC/NR/L2_nr_interface.c | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index cb1a8c9577..5e5b4c37d3 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -60,7 +60,9 @@ #define F1AP_MAX_NB_CELLS 2 #define F1AP_MAX_NO_OF_TNL_ASSOCIATIONS 32 -#define F1AP_MAX_NO_UE_ID 1024 +#define F1AP_MAX_NO_UE_ID 1024 +#define F1AP_MAX_DU2CU_RRC_LENGTH 1024 + typedef struct f1ap_net_ip_address_s { unsigned ipv4:1; unsigned ipv6:1; @@ -307,7 +309,7 @@ typedef struct f1ap_initial_ul_rrc_message_s { uint16_t crnti; uint8_t *rrc_container; int rrc_container_length; - char du2cu_rrc_container[200]; + char du2cu_rrc_container[F1AP_MAX_DU2CU_RRC_LENGTH]; int du2cu_rrc_container_length; } f1ap_initial_ul_rrc_message_t; diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index 19b6a83105..ff215d7cc8 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -343,10 +343,10 @@ int8_t nr_mac_rrc_data_ind(const module_id_t module_idP, f1ap_initial_ul_rrc_message_t *msg = &F1AP_INITIAL_UL_RRC_MESSAGE(tmp); asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, - NULL, - (void *)&cellGroupConfig, - msg->du2cu_rrc_container, - 1024); //sizeof(msg->du2cu_rrc_container)); + NULL, + (void *)&cellGroupConfig, + msg->du2cu_rrc_container, + F1AP_MAX_DU2CU_RRC_LENGTH); if (enc_rval.encoded == -1) { LOG_E(F1AP,"Could not encoded cellGroupConfig, failed element %s\n",enc_rval.failed_type->name); From 5f64d2a1db4fdb9fbbd1fa3ac06bb095cf6a2eba Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 25 Mar 2022 01:51:04 +0000 Subject: [PATCH 133/233] Avoid CU from trying to access RC.nrmac --- openair2/RRC/NR/rrc_gNB.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 1f0dc9d8dd..63f0e72423 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -143,9 +143,11 @@ void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { void enable_nr_rrc_processing_timer(module_id_t module_id, rrc_gNB_ue_context_t *ue_context_pP, uint32_t delay_ms) { - ue_context_pP->ue_context.nr_rrc_processing_timer = 1; - ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms; - nr_rrc_mac_schedule_ue_enabled(module_id, ue_context_pP->ue_context.rnti, false); + if (NODE_IS_DU(RC.nrrrc[module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[module_id]->node_type)) { + ue_context_pP->ue_context.nr_rrc_processing_timer = 1; + ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms; + nr_rrc_mac_schedule_ue_enabled(module_id, ue_context_pP->ue_context.rnti, false); + } } ///---------------------------------------------------------------------------------------------------------------/// From 94a296d79c8181bdf37e2346e62e6dc261a50717 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 25 Mar 2022 15:07:16 +0000 Subject: [PATCH 134/233] Fix selected BWP for SRS at UE --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 2ecc704c73..cfd70305d5 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -949,9 +949,10 @@ bool nr_ue_periodic_srs_scheduling(module_id_t mod_id, frame_t frame, slot_t slo continue; } - NR_BWP_t ubwp = mac->ULbwp[0] ? - mac->ULbwp[0]->bwp_Common->genericParameters : - mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters; + NR_BWP_Id_t ul_bwp_id = mac->UL_BWP_Id; + NR_BWP_t ubwp = ul_bwp_id > 0 && mac->ULbwp[ul_bwp_id-1] ? + mac->ULbwp[ul_bwp_id-1]->bwp_Common->genericParameters : + mac->scc_SIB->uplinkConfigCommon->initialUplinkBWP.genericParameters; uint16_t period = srs_period[srs_resource->resourceType.choice.periodic->periodicityAndOffset_p.present]; uint16_t offset = get_nr_srs_offset(srs_resource->resourceType.choice.periodic->periodicityAndOffset_p); From d5f431a1fd988e7e65ffa1e165746b0e193f1d44 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 25 Mar 2022 15:29:41 +0000 Subject: [PATCH 135/233] Move nr_bwp_switch and schedule_nr_bwp_switch functions to gNB_scheduler_primitives.c --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 66 ------------------- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 65 ++++++++++++++++++ 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 8fd98a0fe3..394e9794c9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1001,72 +1001,6 @@ nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(module_id_t module_id, int CC_id) return nr_fr1_dlsch_preprocessor; } -void nr_bwp_switch(module_id_t module_id, - frame_t frame, - sub_frame_t slot, - int UE_id, - int bwp_id) { - - NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; - NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - - switch(sched_ctrl->bwp_switch_info.bwp_switch_state) { - - case BWP_SWITCH_TO_START: - LOG_W(NR_MAC,"(%d.%d) [UE_id %d] Schedule BWP switch from bwp_id %ld to %d\n", - frame, slot, UE_id, UE_info->UE_sched_ctrl[UE_id].active_bwp->bwp_Id, bwp_id); - sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_RUNNING; - sched_ctrl->bwp_switch_info.bwp_switch_slot = slot; - nr_mac_rrc_bwp_switch_req(module_id, frame, slot, UE_info->rnti[UE_id], bwp_id); - break; - - case BWP_SWITCH_RUNNING: - // The BWP switching takes at least 10+6 ms (3GPP TS 38.331 Section 12) - // gNB needs time to schedule and send RRCReconfiguration message - // Therefore, we wait for the same slot in next frame (10 ms) before update bwp_id in the gNB - if (sched_ctrl->bwp_switch_info.bwp_switch_slot == slot) { - const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; - const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; - const int bwp_id = servingCellConfig && servingCellConfig->firstActiveDownlinkBWP_Id ? - *servingCellConfig->firstActiveDownlinkBWP_Id : 0; - sched_ctrl->active_bwp = bwpList && bwp_id > 0 ? bwpList->list.array[bwp_id - 1] : NULL; - const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; - const int ubwp_id = servingCellConfig && servingCellConfig->uplinkConfig && servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id ? - *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; - sched_ctrl->active_ubwp = ubwpList && ubwp_id > 0 ? ubwpList->list.array[ubwp_id - 1] : NULL; - sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_INACTIVE; - } - break; - - case BWP_SWITCH_INACTIVE: - break; - - default: - AssertFatal(1==0,"Invalid bwp switch state\n"); - break; - } -} - -void schedule_nr_bwp_switch(module_id_t module_id, - frame_t frame, - sub_frame_t slot) { - - NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; - const NR_list_t *UE_list = &UE_info->list; - - // TODO: Implementation of a algorithm to perform: - // - the BWP switch trigger: sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_TO_START - // - the BWP selection: sched_ctrl->bwp_switch_info.next_bwp = bwp_id - - for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { - NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - if(sched_ctrl->bwp_switch_info.bwp_switch_state > BWP_SWITCH_INACTIVE) { - nr_bwp_switch(module_id, frame, slot, UE_id, sched_ctrl->bwp_switch_info.next_bwp); - } - } - -} - void nr_schedule_ue_spec(module_id_t module_id, frame_t frame, sub_frame_t slot) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index b0b877f84e..e6a115f64b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2704,6 +2704,71 @@ void nr_csirs_scheduling(int Mod_idP, } } +void nr_bwp_switch(module_id_t module_id, + frame_t frame, + sub_frame_t slot, + int UE_id, + int bwp_id) { + + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + + switch(sched_ctrl->bwp_switch_info.bwp_switch_state) { + + case BWP_SWITCH_TO_START: + LOG_W(NR_MAC,"(%d.%d) [UE_id %d] Schedule BWP switch from bwp_id %ld to %d\n", + frame, slot, UE_id, UE_info->UE_sched_ctrl[UE_id].active_bwp->bwp_Id, bwp_id); + sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_RUNNING; + sched_ctrl->bwp_switch_info.bwp_switch_slot = slot; + nr_mac_rrc_bwp_switch_req(module_id, frame, slot, UE_info->rnti[UE_id], bwp_id); + break; + + case BWP_SWITCH_RUNNING: + // The BWP switching takes at least 10+6 ms (3GPP TS 38.331 Section 12) + // gNB needs time to schedule and send RRCReconfiguration message + // Therefore, we wait for the same slot in next frame (10 ms) before update bwp_id in the gNB + if (sched_ctrl->bwp_switch_info.bwp_switch_slot == slot) { + const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; + const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; + const int bwp_id = servingCellConfig && servingCellConfig->firstActiveDownlinkBWP_Id ? + *servingCellConfig->firstActiveDownlinkBWP_Id : 0; + sched_ctrl->active_bwp = bwpList && bwp_id > 0 ? bwpList->list.array[bwp_id - 1] : NULL; + const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; + const int ubwp_id = servingCellConfig && servingCellConfig->uplinkConfig && servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id ? + *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; + sched_ctrl->active_ubwp = ubwpList && ubwp_id > 0 ? ubwpList->list.array[ubwp_id - 1] : NULL; + sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_INACTIVE; + } + break; + + case BWP_SWITCH_INACTIVE: + break; + + default: + AssertFatal(1==0,"Invalid bwp switch state\n"); + break; + } +} + +void schedule_nr_bwp_switch(module_id_t module_id, + frame_t frame, + sub_frame_t slot) { + + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + const NR_list_t *UE_list = &UE_info->list; + + // TODO: Implementation of a algorithm to perform: + // - the BWP switch trigger: sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_TO_START + // - the BWP selection: sched_ctrl->bwp_switch_info.next_bwp = bwp_id + + for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if(sched_ctrl->bwp_switch_info.bwp_switch_state > BWP_SWITCH_INACTIVE) { + nr_bwp_switch(module_id, frame, slot, UE_id, sched_ctrl->bwp_switch_info.next_bwp); + } + } + +} /*void fill_nfapi_coresets_and_searchspaces(NR_CellGroupConfig_t *cg, nfapi_nr_coreset_t *coreset, From 6c1f1a02bb10c41e37a753ddcca9aadcbe09d2cd Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 25 Mar 2022 01:51:04 +0000 Subject: [PATCH 136/233] Avoid CU from trying to access RC.nrmac --- openair2/RRC/NR/rrc_gNB.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index d8474de67e..c6fa57b80b 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -143,9 +143,11 @@ void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { void enable_nr_rrc_processing_timer(module_id_t module_id, rrc_gNB_ue_context_t *ue_context_pP, uint32_t delay_ms) { - ue_context_pP->ue_context.nr_rrc_processing_timer = 1; - ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms; - nr_rrc_mac_schedule_ue_enabled(module_id, ue_context_pP->ue_context.rnti, false); + if (NODE_IS_DU(RC.nrrrc[module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[module_id]->node_type)) { + ue_context_pP->ue_context.nr_rrc_processing_timer = 1; + ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms; + nr_rrc_mac_schedule_ue_enabled(module_id, ue_context_pP->ue_context.rnti, false); + } } ///---------------------------------------------------------------------------------------------------------------/// From 65d0318504147bfedbca10de248174d222cc4b4d Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 25 Mar 2022 17:52:44 +0100 Subject: [PATCH 137/233] using tbslbrm at mac also for ulsch as in scf spec --- .../nfapi/public_inc/nfapi_nr_interface_scf.h | 6 +++++- openair1/PHY/CODING/coding_defs.h | 6 ++---- openair1/PHY/CODING/nr_rate_matching.c | 10 ++++------ openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 4 +--- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 6 ++---- openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c | 5 +---- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c | 4 +--- openair1/PHY/defs_gNB.h | 1 + openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 ++ 9 files changed, 19 insertions(+), 25 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index ee680f7fc7..def9af3e7a 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -1200,6 +1200,10 @@ typedef struct #define PUSCH_PDU_BITMAP_PUSCH_PTRS 0x4 #define PUSCH_PDU_BITMAP_DFTS_OFDM 0x8 +typedef struct { + uint32_t tbSizeLbrmBytes; +}nfapi_v3_pusch_maintenance_parameters_t; + typedef struct { uint16_t pdu_bit_map;//Bitmap indicating presence of optional PDUs (see above) @@ -1245,7 +1249,7 @@ typedef struct nfapi_nr_dfts_ofdm_t dfts_ofdm; //beamforming nfapi_nr_ul_beamforming_t beamforming; - + nfapi_v3_pdsch_maintenance_parameters_t maintenance_parms_v3; } nfapi_nr_pusch_pdu_t; //for pucch_pdu: diff --git a/openair1/PHY/CODING/coding_defs.h b/openair1/PHY/CODING/coding_defs.h index f6aba66830..51cae1bd8c 100644 --- a/openair1/PHY/CODING/coding_defs.h +++ b/openair1/PHY/CODING/coding_defs.h @@ -485,8 +485,7 @@ void nr_interleaving_ldpc(uint32_t E, uint8_t Qm, uint8_t *e,uint8_t *f); void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e,int16_t *f); -int nr_rate_matching_ldpc(uint8_t Ilbrm, - uint32_t Tbslbrm, +int nr_rate_matching_ldpc(uint32_t Tbslbrm, uint8_t BG, uint16_t Z, uint8_t *w, @@ -497,8 +496,7 @@ int nr_rate_matching_ldpc(uint8_t Ilbrm, uint8_t rvidx, uint32_t E); -int nr_rate_matching_ldpc_rx(uint8_t Ilbrm, - uint32_t Tbslbrm, +int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm, uint8_t BG, uint16_t Z, int16_t *w, diff --git a/openair1/PHY/CODING/nr_rate_matching.c b/openair1/PHY/CODING/nr_rate_matching.c index 29cba0a557..a828230718 100644 --- a/openair1/PHY/CODING/nr_rate_matching.c +++ b/openair1/PHY/CODING/nr_rate_matching.c @@ -373,8 +373,7 @@ void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e,int16_t *f) } -int nr_rate_matching_ldpc(uint8_t Ilbrm, - uint32_t Tbslbrm, +int nr_rate_matching_ldpc(uint32_t Tbslbrm, uint8_t BG, uint16_t Z, uint8_t *w, @@ -395,7 +394,7 @@ int nr_rate_matching_ldpc(uint8_t Ilbrm, //Bit selection N = (BG==1)?(66*Z):(50*Z); - if (Ilbrm == 0) + if (Tbslbrm == 0) Ncb = N; else { Nref = 3*Tbslbrm/(2*C); //R_LBRM = 2/3 @@ -457,8 +456,7 @@ int nr_rate_matching_ldpc(uint8_t Ilbrm, return 0; } -int nr_rate_matching_ldpc_rx(uint8_t Ilbrm, - uint32_t Tbslbrm, +int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm, uint8_t BG, uint16_t Z, int16_t *w, @@ -484,7 +482,7 @@ int nr_rate_matching_ldpc_rx(uint8_t Ilbrm, //Bit selection N = (BG==1)?(66*Z):(50*Z); - if (Ilbrm == 0) + if (Tbslbrm == 0) Ncb = N; else { Nref = (3*Tbslbrm/(2*C)); //R_LBRM = 2/3 diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index 73bb9bd991..e9b1050156 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -242,12 +242,10 @@ void ldpc8blocks( void *p) { mod_order,nb_rb); uint32_t Tbslbrm = rel15->maintenance_parms_v3.tbSizeLbrmBytes; - uint8_t Ilbrm = 1; uint8_t e[E]; bzero (e, E); - nr_rate_matching_ldpc(Ilbrm, - Tbslbrm, + nr_rate_matching_ldpc(Tbslbrm, impp->BG, *impp->Zc, impp->d[rr], diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 06cc2aaea8..5b33939a0d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -246,8 +246,6 @@ void nr_processULSegment(void* arg) { __m128i *pv = (__m128i*)&z; __m128i *pl = (__m128i*)&l; - - uint8_t Ilbrm = 0; Kr = ulsch_harq->K; Kr_bytes = Kr>>3; @@ -295,8 +293,7 @@ void nr_processULSegment(void* arg) { //start_meas(&phy_vars_gNB->ulsch_rate_unmatching_stats); - if (nr_rate_matching_ldpc_rx(Ilbrm, - 0, + if (nr_rate_matching_ldpc_rx(rdata->tbslbrm, p_decoderParms->BG, p_decoderParms->Z, ulsch_harq->d[r], @@ -594,6 +591,7 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, rdata->offset = offset; rdata->ulsch = ulsch; rdata->ulsch_id = ULSCH_id; + rdata->tbslbrm = pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes; pushTpool(phy_vars_gNB->threadPool,req); phy_vars_gNB->nbDecode++; LOG_D(PHY,"Added a block to decode, in pipe: %d\n",phy_vars_gNB->nbDecode); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c index 34081659ac..7b7eec3d63 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c @@ -292,8 +292,6 @@ void nr_processDLSegment(void* arg) { __m128i *pv = (__m128i*)&z; __m128i *pl = (__m128i*)&l; - uint8_t Ilbrm = 1; - Kr = harq_process->K; // [hna] overwrites this line "Kr = p_decParams->Z*kb" Kr_bytes = Kr>>3; K_bits_F = Kr-harq_process->F; @@ -329,8 +327,7 @@ void nr_processDLSegment(void* arg) { harq_process->round); */ //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_RATE_MATCHING, VCD_FUNCTION_IN); - if (nr_rate_matching_ldpc_rx(Ilbrm, - Tbslbrm, + if (nr_rate_matching_ldpc_rx(Tbslbrm, p_decoderParms->BG, p_decoderParms->Z, harq_process->d[r], diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index be51f1f8b2..5de615a0cc 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -188,7 +188,6 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, uint16_t Kr=0; uint32_t r_offset=0; uint32_t F=0; - uint8_t Ilbrm = 0; float Coderate = 0.0; /////////// @@ -378,8 +377,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RATE_MATCHING_LDPC, VCD_FUNCTION_IN); start_meas(&ue->ulsch_rate_matching_stats); - if (nr_rate_matching_ldpc(Ilbrm, - 0, + if (nr_rate_matching_ldpc(0, harq_process->BG, *pz, harq_process->d[r], diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 8faf4a50ce..e19468ad1b 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -927,6 +927,7 @@ typedef struct LDPCDecode_s { int r_offset; int offset; int decodeIterations; + uint32_t tbslbrm; } ldpcDecode_t; struct ldpcReqId { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index aa49b14fe5..273ad0dd40 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1767,6 +1767,8 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pusch_pdu->pusch_data.tb_size = sched_pusch->tb_size; pusch_pdu->pusch_data.num_cb = 0; //CBG not supported + pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = 0; + LOG_D(NR_MAC,"PUSCH PDU : data_scrambling_identity %x, dmrs_scrambling_id %x\n",pusch_pdu->data_scrambling_id,pusch_pdu->ul_dmrs_scrambling_id); /* TRANSFORM PRECODING --------------------------------------------------------*/ From 3b3866feee52611f81cb00efa5ab7b8a5ba329f5 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Mon, 28 Mar 2022 10:31:50 +0100 Subject: [PATCH 138/233] Fix warning after merge --- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 9d689d6574..7e8a7dcf3b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -38,12 +38,13 @@ void set_cset_offset(uint16_t); void mac_top_init_gNB(void); +void process_CellGroup(NR_CellGroupConfig_t *CellGroup, NR_UE_sched_ctrl_t *sched_ctrl); + void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, - NR_ServingCellConfigCommon_t *scc - ); + NR_ServingCellConfigCommon_t *scc); void nr_rrc_mac_schedule_ue_enabled(module_id_t module_id, rnti_t rnti, bool schedule_enabled); From 8e5ea001b12be342479eb62b40397984671ab05a Mon Sep 17 00:00:00 2001 From: rmagueta Date: Mon, 28 Mar 2022 10:31:50 +0100 Subject: [PATCH 139/233] Fix warning after merge --- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 84a49182c7..2f3759b9a4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -38,12 +38,13 @@ void set_cset_offset(uint16_t); void mac_top_init_gNB(void); +void process_CellGroup(NR_CellGroupConfig_t *CellGroup, NR_UE_sched_ctrl_t *sched_ctrl); + void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, - NR_ServingCellConfigCommon_t *scc - ); + NR_ServingCellConfigCommon_t *scc); void nr_rrc_mac_schedule_ue_enabled(module_id_t module_id, rnti_t rnti, bool schedule_enabled); From 0152e8ce38027fb54fc7e5386253ff88bcd62c53 Mon Sep 17 00:00:00 2001 From: francescomani Date: Sun, 3 Apr 2022 20:26:05 +0200 Subject: [PATCH 140/233] fix dlsim compilation error --- openair1/SIMULATION/NR_PHY/dlsim.c | 5 ++--- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index fdd0d2295e..b9c74c6337 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -207,10 +207,9 @@ int nr_derive_key(int alg_type, uint8_t alg_id, void config_common(int Mod_idP, int ssb_SubcarrierOffset, - rrc_pdsch_AntennaPorts_t pdsch_AntennaPorts, + int pdsch_AntennaPorts, int pusch_AntennaPorts, - NR_ServingCellConfigCommon_t *scc - ); + NR_ServingCellConfigCommon_t *scc); int generate_dlsch_header(unsigned char *mac_header, unsigned char num_sdus, diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index c1845b6555..0c780cd839 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -42,8 +42,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, - NR_ServingCellConfigCommon_t *scc - ); + NR_ServingCellConfigCommon_t *scc); int rrc_mac_config_req_gNB(module_id_t Mod_idP, int ssb_SubcarrierOffset, From 4d257e1a99e0b84bb5c856cfa9022f3648abfcf7 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 5 Apr 2022 12:03:22 +0100 Subject: [PATCH 141/233] Fix build after merge --- openair2/RRC/NR/rrc_gNB.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 52fb987a1b..181658e963 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1928,6 +1928,7 @@ int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP NULL, NULL, NULL, + NULL, masterCellGroup); enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS); From c65593d0888dbc4b6b0e910f76a7e421cc02f3a5 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Wed, 6 Apr 2022 11:06:55 +0100 Subject: [PATCH 142/233] Fix build after merge --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 5 ++--- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 438f427c5f..8867e1cc08 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1385,8 +1385,7 @@ void set_r_pucch_parms(int rsetindex, void prepare_dci(const NR_CellGroupConfig_t *CellGroup, dci_pdu_rel15_t *dci_pdu_rel15, nr_dci_format_t format, - int bwp_id, - NR_ControlResourceSetId_t controlResourceSetId) { + int bwp_id) { AssertFatal(CellGroup!=NULL,"CellGroup shouldn't be null here\n"); @@ -1492,7 +1491,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, pdcch_dci_pdu->PayloadSizeBits = dci_size; AssertFatal(dci_size <= 64, "DCI sizes above 64 bits not yet supported"); if (dci_format == NR_DL_DCI_FORMAT_1_1 || dci_format == NR_UL_DCI_FORMAT_0_1) - prepare_dci(CellGroup, dci_pdu_rel15, dci_format, bwp_id, controlResourceSetId); + prepare_dci(CellGroup, dci_pdu_rel15, dci_format, bwp_id); /// Payload generation switch (dci_format) { diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 20f4a9c9c5..d1bcf57ccc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -305,8 +305,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, void prepare_dci(const NR_CellGroupConfig_t *CellGroup, dci_pdu_rel15_t *dci_pdu_rel15, nr_dci_format_t format, - int bwp_id, - NR_ControlResourceSetId_t controlResourceSetId); + int bwp_id); void set_r_pucch_parms(int rsetindex, int r_pucch, From eb03322545a95624aa6adc2ce4f344094ccd19a0 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Wed, 6 Apr 2022 13:18:46 +0100 Subject: [PATCH 143/233] Change LOG level --- openair2/RRC/NR/L2_nr_interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index a280786fbc..c22a5bd71f 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -400,6 +400,6 @@ void nr_mac_gNB_enable_rrc_processing_timer_req(const module_id_t Mod_instP, con ue_context_p->ue_context.nr_rrc_processing_timer = 1; ue_context_p->ue_context.nr_rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS; } else { - LOG_D(RRC,"%s: Unknown RNTI 0x%04x\n", __FUNCTION__, rntiP); + LOG_E(NR_RRC,"%s: Unknown RNTI 0x%04x\n", __FUNCTION__, rntiP); } } \ No newline at end of file From 2b1cafb509e37f54317ff8fadc5aa736ebdfaffd Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 6 Apr 2022 18:06:02 +0100 Subject: [PATCH 144/233] Do not create a Dedicated BWP when it is not configured in the configuration file --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index ee99da539f..3226cab656 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1890,7 +1890,7 @@ void fill_initial_SpCellConfig(int uid, *pdsch_servingcellconfig->ext1->maxMIMO_Layers = 2; // Downlink BWPs - int n_dl_bwp = 1; + int n_dl_bwp = 0; if (servingcellconfigdedicated && servingcellconfigdedicated->downlinkBWP_ToAddModList && servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count > 0) { @@ -1910,7 +1910,7 @@ void fill_initial_SpCellConfig(int uid, } // Uplink BWPs - int n_ul_bwp = 1; + int n_ul_bwp = 0; if (servingcellconfigdedicated && servingcellconfigdedicated->uplinkConfig && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count > 0) { From 0270b7b1fb8f213d81785f0f7cc7c2a4a8e59e6d Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 14 Apr 2022 15:49:29 +0100 Subject: [PATCH 145/233] Move RRC processing timer implementation from RRC to MAC --- openair1/SIMULATION/NR_PHY/dlsim.c | 4 +- openair1/SIMULATION/NR_PHY/ulsim.c | 4 +- openair2/GNB_APP/gnb_config.c | 4 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 3 - openair2/LAYER2/NR_MAC_gNB/config.c | 23 +++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 3 + openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 8 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 20 +++- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 1 - .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 9 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 5 +- openair2/RRC/NR/L2_nr_interface.c | 11 -- openair2/RRC/NR/nr_rrc_defs.h | 5 - openair2/RRC/NR/rrc_gNB.c | 113 ++++++++++++------ openair2/RRC/NR/rrc_gNB_nsa.c | 6 +- 16 files changed, 142 insertions(+), 79 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 1de8d6ced4..b15f649d3e 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -827,9 +827,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 0, 0, NULL); + rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 0, 0, NULL, 0); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, 0); // reset preprocessor to the one of DLSIM after it has been set during // rrc_mac_config_req_gNB gNB_mac->pre_processor_dl = nr_dlsim_preprocessor; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 09499248b2..e4c693d602 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -751,9 +751,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 0, 0, NULL); + rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 0, 0, NULL, 0); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, 0); frame_parms->nb_antennas_tx = n_tx; frame_parms->nb_antennas_rx = n_rx; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 434a2896de..7751172cdb 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2066,8 +2066,8 @@ void configure_gnb_du_mac(int inst) { NULL, 0, 0, // rnti - (NR_CellGroupConfig_t *)NULL - ); + NULL, + 0); } diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index 2d69e0d64f..0f2cb42b88 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -203,7 +203,4 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, const frame_t frameP, const sub_frame_t subframeP, const rnti_t rntiP); - -void nr_mac_gNB_enable_rrc_processing_timer_req(const module_id_t Mod_instP, const rnti_t rntiP); - #endif diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index abdca03faa..4f19a313ae 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -449,10 +449,6 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, rrc_pdsch_AntennaPorts } -void nr_rrc_mac_schedule_ue_enabled(module_id_t module_id, rnti_t rnti, bool schedule_enabled) { - const int UE_id = find_nr_UE_id(module_id,rnti); - RC.nrmac[module_id]->UE_info.UE_sched_ctrl[UE_id].schedule_enabled = schedule_enabled; -} int rrc_mac_config_req_gNB(module_id_t Mod_idP, int ssb_SubcarrierOffset, @@ -465,7 +461,23 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_BCCH_DL_SCH_Message_t *sib1, int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup) { + NR_CellGroupConfig_t *CellGroup, + uint32_t rrc_reconfiguration_delay) { + + if (scc && rrc_reconfiguration_delay > 0) { + const int UE_id = find_nr_UE_id(Mod_idP,rnti); + if (UE_id >= 0) { + NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->rrc_processing_timer == 0) { + const uint16_t sf_ahead = (uint16_t) ceil((float)6/(0x01<<(*scc->ssbSubcarrierSpacing))); + const uint16_t sl_ahead = sf_ahead * (0x01<<(*scc->ssbSubcarrierSpacing)); + sched_ctrl->rrc_processing_timer = (rrc_reconfiguration_delay << (*scc->ssbSubcarrierSpacing)) + sl_ahead; + LOG_I(NR_MAC, "Activating RRC processing timer for UE %d\n", UE_id); + } + } + return 0; + } if (scc != NULL ) { AssertFatal((scc->ssb_PositionsInBurst->present > 0) && (scc->ssb_PositionsInBurst->present < 4), "SSB Bitmap type %d is not valid\n",scc->ssb_PositionsInBurst->present); @@ -695,6 +707,7 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, sched_ctrl->maxL = 2; } } + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_MAC_CONFIG, VCD_FUNCTION_OUT); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 540198a22a..ecc2ef0cff 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -346,6 +346,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, LOG_I(NR_MAC,"Frame.Slot %d.%d\n%s\n",frame,slot,stats_output); } + // This updates the timers running at MAC layer + nr_mac_update_timers(module_idP, frame, slot); + // This schedules MIB schedule_nr_mib(module_idP, frame, slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index cb68ea0324..c76e6d238a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1947,10 +1947,12 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram UE_info->active[UE_id] = true; UE_info->Msg4_ACKed[UE_id] = true; - // Pause scheduling according to: + const NR_SIB1_t *sib1 = RC.nrmac[module_id]->common_channels[0].sib1 ? RC.nrmac[module_id]->common_channels[0].sib1->message.choice.c1->choice.systemInformationBlockType1 : NULL; + NR_BWP_t *genericParameters = get_dl_bwp_genericParameters(sched_ctrl->active_bwp, + RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon, + sib1); // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs - sched_ctrl->schedule_enabled = false; - nr_mac_gNB_enable_rrc_processing_timer_req(module_id, ra->rnti); + sched_ctrl->rrc_processing_timer = (10 << genericParameters->subcarrierSpacing); // RRCSetup 10 ms } else { LOG_I(NR_MAC, "(ue %i, rnti 0x%04x) RA Procedure failed at Msg4!\n", UE_id, ra->rnti); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 9b3b02d0d4..f72ccaf6e5 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -490,7 +490,7 @@ void nr_store_dlsch_buffer(module_id_t module_id, const uint16_t rnti = UE_info->rnti[UE_id]; LOG_D(NR_MAC, "In %s: UE %d/%x: LCID %d\n", __FUNCTION__, UE_id, rnti, lcid); - if(lcid == DL_SCH_LCID_DTCH && sched_ctrl->schedule_enabled == false) { + if (lcid == DL_SCH_LCID_DTCH && sched_ctrl->rrc_processing_timer > 0) { continue; } @@ -498,7 +498,7 @@ void nr_store_dlsch_buffer(module_id_t module_id, sched_ctrl->rlc_status[lcid] = mac_rlc_status_ind(module_id, rnti, module_id, - frame, + frame, slot, ENB_FLAG_YES, MBMS_FLAG_NO, @@ -1056,6 +1056,22 @@ nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(module_id_t module_id, int CC_id) return nr_fr1_dlsch_preprocessor; } +void nr_mac_update_timers(module_id_t module_id, + frame_t frame, + sub_frame_t slot) { + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + const NR_list_t *UE_list = &UE_info->list; + for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->rrc_processing_timer > 0) { + sched_ctrl->rrc_processing_timer--; + if (sched_ctrl->rrc_processing_timer == 0) { + LOG_I(NR_MAC, "(%d.%d) De-activating RRC processing timer for UE %d\n", frame, slot, UE_id); + } + } + } +} + void nr_schedule_ue_spec(module_id_t module_id, frame_t frame, sub_frame_t slot) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 67609ad105..6f17556c91 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2312,7 +2312,6 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG sched_ctrl->pucch_consecutive_dtx_cnt = 0; sched_ctrl->pusch_consecutive_dtx_cnt = 0; sched_ctrl->ul_failure = 0; - sched_ctrl->schedule_enabled = true; sched_ctrl->sched_srs.frame = -1; sched_ctrl->sched_srs.slot = -1; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 5a4d1d4d1b..e6637f266a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1252,7 +1252,7 @@ void pf_ul(module_id_t module_id, const bool do_sched = nr_UE_is_to_be_scheduled(module_id, 0, UE_id, sched_pusch->frame, sched_pusch->slot); LOG_D(NR_MAC,"pf_ul: do_sched UE %d => %s\n",UE_id,do_sched ? "yes" : "no"); - if ( (B == 0 && !do_sched) || (sched_ctrl->schedule_enabled == false) ) { + if ((B == 0 && !do_sched) || (sched_ctrl->rrc_processing_timer > 0)) { continue; } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index afe1bb47f0..da2a59a568 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -46,8 +46,6 @@ void config_common(int Mod_idP, int pusch_AntennaPorts, NR_ServingCellConfigCommon_t *scc); -void nr_rrc_mac_schedule_ue_enabled(module_id_t module_id, rnti_t rnti, bool schedule_enabled); - int rrc_mac_config_req_gNB(module_id_t Mod_idP, int ssb_SubcarrierOffset, rrc_pdsch_AntennaPorts_t pdsch_AntennaPorts, @@ -59,13 +57,18 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_BCCH_DL_SCH_Message_t *sib1, int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup); + NR_CellGroupConfig_t *CellGroup, + uint32_t rrc_reconfiguration_delay); void clear_nr_nfapi_information(gNB_MAC_INST * gNB, int CC_idP, frame_t frameP, sub_frame_t subframeP); +void nr_mac_update_timers(module_id_t module_id, + frame_t frame, + sub_frame_t slot); + void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame_rxP, sub_frame_t slot_rxP); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 9156d45865..f7282045b9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -646,12 +646,13 @@ typedef struct { /// UL HARQ processes that await retransmission NR_list_t retrans_ul_harq; NR_UE_mac_ce_ctrl_t UE_mac_ce_ctrl;// MAC CE related information - /// Flag to indicate if MAC scheduling is enabled or disabled by RRC (e.g. during RRC processing time) - bool schedule_enabled; /// number of active DL LCs uint8_t dl_lc_num; /// order in which DLSCH scheduler should allocate LCs uint8_t dl_lc_ids[NR_MAX_NUM_LCID]; + + /// Timer for RRC processing procedures + uint32_t rrc_processing_timer; } NR_UE_sched_ctrl_t; typedef struct { diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index c22a5bd71f..e30b55cc94 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -392,14 +392,3 @@ void nr_mac_gNB_rrc_ul_failure_reset(const module_id_t Mod_instP, LOG_W(RRC,"Frame %d, Subframe %d: UL failure reset: UE %x unknown \n",frameP,subframeP,rntiP); } } - -void nr_mac_gNB_enable_rrc_processing_timer_req(const module_id_t Mod_instP, const rnti_t rntiP) { - struct rrc_gNB_ue_context_s *ue_context_p = NULL; - ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_instP], rntiP); - if (ue_context_p != NULL) { - ue_context_p->ue_context.nr_rrc_processing_timer = 1; - ue_context_p->ue_context.nr_rrc_processing_delay = NR_RRC_PROCESSING_DELAY_MS; - } else { - LOG_E(NR_RRC,"%s: Unknown RNTI 0x%04x\n", __FUNCTION__, rntiP); - } -} \ No newline at end of file diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index e37e932220..268168750c 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -96,9 +96,6 @@ typedef struct nr_uid_linear_allocator_s { #define PROTOCOL_NR_RRC_CTXT_FMT PROTOCOL_CTXT_FMT #define PROTOCOL_NR_RRC_CTXT_ARGS(CTXT_Pp) PROTOCOL_NR_CTXT_ARGS(CTXT_Pp) -// Delay to take in consideration for sl_ahead value -#define TX_SL_AHEAD_DELAY 3 - // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs #define NR_RRC_PROCESSING_DELAY_MS 10 @@ -379,8 +376,6 @@ typedef struct gNB_RRC_UE_s { //GTPV1 F1-U TUNNELS uint32_t incoming_teid[S1AP_MAX_E_RAB]; - uint32_t nr_rrc_processing_timer; - uint32_t nr_rrc_processing_delay; uint32_t ul_failure_timer; uint32_t ue_release_timer; uint32_t ue_release_timer_thres; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index cbb963693f..a7e6e49052 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -140,16 +140,6 @@ void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { RC.nrrrc[ctxt_pP->module_id]->carrier.Srb0.Active = 1; } -void enable_nr_rrc_processing_timer(module_id_t module_id, - rrc_gNB_ue_context_t *ue_context_pP, - uint32_t delay_ms) { - if (NODE_IS_DU(RC.nrrrc[module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[module_id]->node_type)) { - ue_context_pP->ue_context.nr_rrc_processing_timer = 1; - ue_context_pP->ue_context.nr_rrc_processing_delay = delay_ms; - nr_rrc_mac_schedule_ue_enabled(module_id, ue_context_pP->ue_context.rnti, false); - } -} - ///---------------------------------------------------------------------------------------------------------------/// ///---------------------------------------------------------------------------------------------------------------/// @@ -186,7 +176,8 @@ static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration rrc->carrier.siblock1, 0, 0, // WIP hardcoded rnti - NULL); + NULL, + 0); } /* set flag to indicate that cell information is configured. This is required @@ -295,7 +286,8 @@ void apply_macrlc_config(gNB_RRC_INST *rrc, NULL, 0, ue_context_pP->ue_context.rnti, - cgc); + cgc, + 0); nr_rrc_rlc_config_asn1_req(ctxt_pP, ue_context_pP->ue_context.SRB_configList, @@ -483,7 +475,8 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( rrc_instance_p->carrier.siblock1, 0, ue_context_pP->ue_context.rnti, - NULL); + NULL, + 0); LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", @@ -735,12 +728,24 @@ rrc_gNB_generate_defaultRRCReconfiguration( NULL, ue_p->masterCellGroup); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); - free(ue_context_pP->ue_context.nas_pdu.buffer); LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + rrc->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + rrc->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_PROCESSING_DELAY_MS); + /* Free all NAS PDUs */ for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { @@ -1005,10 +1010,21 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( &rrc->configuration, NULL, cellGroupConfig); + LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); - - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + rrc->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + rrc->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_PROCESSING_DELAY_MS); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1181,11 +1197,22 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL, NULL, NULL); - - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_PROCESSING_DELAY_MS); + /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_modify_pdusessions; i++) { if (ue_context_pP->ue_context.modify_pdusession[i].param.nas_pdu.buffer != NULL) { @@ -1286,12 +1313,24 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( NULL, NULL); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); - ue_context_pP->ue_context.pdu_session_release_command_flag = 1; LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_PROCESSING_DELAY_MS); + /* Free all NAS PDUs */ if (nas_length > 0) { /* Free the NAS PDU buffer and invalidate it */ @@ -1399,7 +1438,8 @@ rrc_gNB_process_RRCReconfigurationComplete( NULL, 0, ue_context_pP->ue_context.rnti, - ue_context_pP->ue_context.masterCellGroup); + ue_context_pP->ue_context.masterCellGroup, + 0); LOG_D(NR_RRC,"Configuring RLC DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); nr_rrc_rlc_config_asn1_req(ctxt_pP, SRB_configList, // NULL, @@ -1840,10 +1880,22 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( NULL, NULL); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS); - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_PROCESSING_DELAY_MS); + /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { @@ -3459,15 +3511,6 @@ void nr_rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { get_ul_mimo_layers(RC.nrrrc[0],ue_context_p->ue_context.UE_Capability_nr)); } } - - if (ue_context_p->ue_context.nr_rrc_processing_timer > 0) { - ue_context_p->ue_context.nr_rrc_processing_timer++; - if (ue_context_p->ue_context.nr_rrc_processing_timer > ue_context_p->ue_context.nr_rrc_processing_delay) { - ue_context_p->ue_context.nr_rrc_processing_timer = 0; - nr_rrc_mac_schedule_ue_enabled(ctxt_pP->module_id, ue_context_p->ue_context.rnti, true); - } - } - if (ue_context_p->ue_context.ul_failure_timer > 0) { ue_context_p->ue_context.ul_failure_timer++; diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index fb8de46953..2685f3ba35 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -356,7 +356,8 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ NULL, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup); + ue_context_p->ue_context.secondaryCellGroup, + 0); } else { rrc_mac_config_req_gNB(rrc->module_id, rrc->configuration.ssb_SubcarrierOffset, @@ -369,7 +370,8 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ NULL, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup); + ue_context_p->ue_context.secondaryCellGroup, + 0); } if(m == NULL){ From 7b5d8de65ff24e3e1f414336c21241a107f3fac2 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 14 Apr 2022 15:52:54 +0100 Subject: [PATCH 146/233] Remove sib1_tda from configuration files to use the default value --- targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf | 1 - targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf | 1 - .../CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf | 1 - .../GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf | 1 - .../GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf | 1 - .../CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 1 - .../GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf | 1 - 7 files changed, 7 deletions(-) diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf index c07a0db649..92d1ce432c 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf @@ -48,7 +48,6 @@ gNBs = pdsch_AntennaPorts = 1; pusch_AntennaPorts = 1; min_rxtxtime = 6; - sib1_tda = 0; pdcch_ConfigSIB1 = ( { diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf index 256d724f3f..f95bdf91ae 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf @@ -38,7 +38,6 @@ gNBs = ssb_SubcarrierOffset = 0; min_rxtxtime = 6; - sib1_tda = 0; pdcch_ConfigSIB1 = ( { diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf index 726e43d7ad..74f6c34f57 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.tm1.106PRB.usrpn300.gtp-itti.conf @@ -37,7 +37,6 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 0; - sib1_tda = 0; servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf index 7e2db64aaa..d124c5692f 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf @@ -34,7 +34,6 @@ gNBs = ssb_SubcarrierOffset = 0; min_rxtxtime = 6; - sib1_tda = 0; do_SRS = 1; pdcch_ConfigSIB1 = ( diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf index cab801525b..ff64fe718a 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf @@ -36,7 +36,6 @@ gNBs = pdsch_AntennaPorts = 1; pusch_AntennaPorts = 1; min_rxtxtime = 6; - sib1_tda = 0; pdcch_ConfigSIB1 = ( { diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index 950e9c1d38..1bc6de3bfe 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -35,7 +35,6 @@ gNBs = ssb_SubcarrierOffset = 0; pdsch_AntennaPorts_N1 = 2; pusch_AntennaPorts = 2; - sib1_tda = 0; do_SRS = 1; ul_prbblacklist = "51,52,53,54" diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index 1dc903b811..a0cee6f9ca 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -34,7 +34,6 @@ gNBs = ssb_SubcarrierOffset = 0; min_rxtxtime = 6; - sib1_tda = 0; pdcch_ConfigSIB1 = ( { From bd7f837f18aa2b30b28bdeff9f1f7433ac226fcd Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 19 Apr 2022 13:23:17 +0100 Subject: [PATCH 147/233] Fix after merge --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 3 + openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 4 +- openair2/RRC/NR/rrc_gNB.c | 60 +++++++++++++++++-- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index ecc2ef0cff..b82a2b0925 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -349,6 +349,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, // This updates the timers running at MAC layer nr_mac_update_timers(module_idP, frame, slot); + // This schedules the BWP switching + schedule_nr_bwp_switch(module_idP, frame, slot); + // This schedules MIB schedule_nr_mib(module_idP, frame, slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index da3556e675..a75b2b6265 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1992,8 +1992,10 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram NR_BWP_t *genericParameters = get_dl_bwp_genericParameters(sched_ctrl->active_bwp, RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon, sib1); + // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs - sched_ctrl->rrc_processing_timer = (10 << genericParameters->subcarrierSpacing); // RRCSetup 10 ms + uint32_t delay_ms = bwpList ? 16 : 10; + sched_ctrl->rrc_processing_timer = (delay_ms << genericParameters->subcarrierSpacing); } else { LOG_I(NR_MAC, "(ue %i, rnti 0x%04x) RA Procedure failed at Msg4!\n", UE_id, ra->rnti); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 7cb1af60dc..93973d1ca6 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -741,6 +741,12 @@ rrc_gNB_generate_defaultRRCReconfiguration( LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS : NR_RRC_PROCESSING_DELAY_MS; + rrc_mac_config_req_gNB(ctxt_pP->module_id, 0, rrc->configuration.pdsch_AntennaPorts, @@ -753,7 +759,7 @@ rrc_gNB_generate_defaultRRCReconfiguration( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + delay_ms); /* Free all NAS PDUs */ for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1021,6 +1027,12 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( cellGroupConfig); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS : NR_RRC_PROCESSING_DELAY_MS; + rrc_mac_config_req_gNB(ctxt_pP->module_id, 0, rrc->configuration.pdsch_AntennaPorts, @@ -1033,7 +1045,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + delay_ms); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1208,6 +1220,12 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS : NR_RRC_PROCESSING_DELAY_MS; + rrc_mac_config_req_gNB(ctxt_pP->module_id, 0, RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, @@ -1220,7 +1238,7 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + delay_ms); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_modify_pdusessions; i++) { @@ -1326,6 +1344,12 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS : NR_RRC_PROCESSING_DELAY_MS; + rrc_mac_config_req_gNB(ctxt_pP->module_id, 0, RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, @@ -1338,7 +1362,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + delay_ms); /* Free all NAS PDUs */ if (nas_length > 0) { @@ -1891,6 +1915,12 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS : NR_RRC_PROCESSING_DELAY_MS; + rrc_mac_config_req_gNB(ctxt_pP->module_id, 0, RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, @@ -1903,7 +1933,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + delay_ms); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1978,7 +2008,25 @@ int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP NULL, masterCellGroup); - enable_nr_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP, TX_SL_AHEAD_DELAY + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS); + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_PROCESSING_DELAY_MS + NR_RRC_BWP_SWITCH_DELAY_MS : NR_RRC_PROCESSING_DELAY_MS; + + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + delay_ms); nr_rrc_data_req(ctxt_pP, DCCH, From 5c176c454aa48ea601e360707bbee624eac1ab19 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 19 Apr 2022 16:34:10 +0100 Subject: [PATCH 148/233] Fix mcs table update during RRCReconfiguration --- openair2/LAYER2/NR_MAC_gNB/config.c | 2 -- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 6 ++--- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 23 +++++++++++-------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 4f19a313ae..06f1433300 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -673,8 +673,6 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, LOG_I(NR_MAC,"Modified UE_id %d/%x with CellGroup\n",UE_id,rnti); process_CellGroup(CellGroup,&UE_info->UE_sched_ctrl[UE_id]); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - sched_ctrl->update_pdsch_ps = true; - sched_ctrl->update_pusch_ps = true; const NR_PDSCH_ServingCellConfig_t *pdsch = servingCellConfig ? servingCellConfig->pdsch_ServingCellConfig->choice.setup : NULL; if (get_softmodem_params()->sa) { // add all available DL HARQ processes for this UE in SA diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index f72ccaf6e5..0ad56751d3 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -589,7 +589,7 @@ bool allocate_dl_retransmission(module_id_t module_id, } /* check whether we need to switch the TDA allocation since the last * (re-)transmission */ - if (ps->time_domain_allocation != tda || sched_ctrl->update_pdsch_ps) { + if (ps->time_domain_allocation != tda) { nr_set_pdsch_semi_static(sib1, scc, cg, @@ -599,7 +599,6 @@ bool allocate_dl_retransmission(module_id_t module_id, ps->nrOfLayers, sched_ctrl, ps); - sched_ctrl->update_pdsch_ps = false; } } else { /* the retransmission will use a different time domain allocation, check @@ -914,7 +913,6 @@ void pf_dl(module_id_t module_id, layers[UE_id], sched_ctrl, ps); - sched_ctrl->update_pdsch_ps = false; } const uint16_t slbitmap = SL_to_bitmap(ps->startSymbolIndex, ps->nrOfSymbols); @@ -1067,6 +1065,8 @@ void nr_mac_update_timers(module_id_t module_id, sched_ctrl->rrc_processing_timer--; if (sched_ctrl->rrc_processing_timer == 0) { LOG_I(NR_MAC, "(%d.%d) De-activating RRC processing timer for UE %d\n", frame, slot, UE_id); + sched_ctrl->update_pdsch_ps = true; + sched_ctrl->update_pusch_ps = true; } } } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 6f17556c91..e901d0d669 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -579,16 +579,21 @@ void nr_set_pdsch_semi_static(const NR_SIB1_t *sib1, bwpd = (NR_BWP_DownlinkDedicated_t*)bwpd0; } - if (bwpd && - bwpd->pdsch_Config && - bwpd->pdsch_Config->choice.setup && - bwpd->pdsch_Config->choice.setup->mcs_Table) { - if (*bwpd->pdsch_Config->choice.setup->mcs_Table == 0) - ps->mcsTableIdx = 1; - else - ps->mcsTableIdx = 2; + if (sched_ctrl->update_pdsch_ps == true) { + if (bwpd && + bwpd->pdsch_Config && + bwpd->pdsch_Config->choice.setup && + bwpd->pdsch_Config->choice.setup->mcs_Table) { + if (*bwpd->pdsch_Config->choice.setup->mcs_Table == 0) { + ps->mcsTableIdx = 1; + } else { + ps->mcsTableIdx = 2; + } + } else { + ps->mcsTableIdx = 0; + } + sched_ctrl->update_pdsch_ps = false; } - else ps->mcsTableIdx = 0; LOG_D(NR_MAC,"MCS Table Index: %d\n",ps->mcsTableIdx); NR_PDSCH_Config_t *pdsch_Config; From 7508205f907c3ece049e2584830bfb06be73fdc4 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 19 Apr 2022 16:39:49 +0100 Subject: [PATCH 149/233] Move nr_mac_update_timers function to gNB_scheduler_primitives --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 18 ------------------ .../NR_MAC_gNB/gNB_scheduler_primitives.c | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 0ad56751d3..98ec1ae659 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1054,24 +1054,6 @@ nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(module_id_t module_id, int CC_id) return nr_fr1_dlsch_preprocessor; } -void nr_mac_update_timers(module_id_t module_id, - frame_t frame, - sub_frame_t slot) { - NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; - const NR_list_t *UE_list = &UE_info->list; - for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { - NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - if (sched_ctrl->rrc_processing_timer > 0) { - sched_ctrl->rrc_processing_timer--; - if (sched_ctrl->rrc_processing_timer == 0) { - LOG_I(NR_MAC, "(%d.%d) De-activating RRC processing timer for UE %d\n", frame, slot, UE_id); - sched_ctrl->update_pdsch_ps = true; - sched_ctrl->update_pusch_ps = true; - } - } - } -} - void nr_schedule_ue_spec(module_id_t module_id, frame_t frame, sub_frame_t slot) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index e901d0d669..1ae83b0ffd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2796,6 +2796,23 @@ void nr_csirs_scheduling(int Mod_idP, } } +void nr_mac_update_timers(module_id_t module_id, + frame_t frame, + sub_frame_t slot) { + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; + const NR_list_t *UE_list = &UE_info->list; + for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->rrc_processing_timer > 0) { + sched_ctrl->rrc_processing_timer--; + if (sched_ctrl->rrc_processing_timer == 0) { + LOG_I(NR_MAC, "(%d.%d) De-activating RRC processing timer for UE %d\n", frame, slot, UE_id); + sched_ctrl->update_pdsch_ps = true; + sched_ctrl->update_pusch_ps = true; + } + } + } +} /*void fill_nfapi_coresets_and_searchspaces(NR_CellGroupConfig_t *cg, nfapi_nr_coreset_t *coreset, From 72f40e6fb1dee33ecfdab78adcd753c5492a4e5e Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 19 Apr 2022 17:04:05 +0100 Subject: [PATCH 150/233] Add definition for some rrc processing delays --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 2 +- openair2/RRC/NR/nr_rrc_defs.h | 4 +++- openair2/RRC/NR/rrc_gNB.c | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index c76e6d238a..32e1a238f0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1952,7 +1952,7 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon, sib1); // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs - sched_ctrl->rrc_processing_timer = (10 << genericParameters->subcarrierSpacing); // RRCSetup 10 ms + sched_ctrl->rrc_processing_timer = (NR_RRC_SETUP_DELAY_MS << genericParameters->subcarrierSpacing); // RRCSetup 10 ms } else { LOG_I(NR_MAC, "(ue %i, rnti 0x%04x) RA Procedure failed at Msg4!\n", UE_id, ra->rnti); diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 268168750c..5cc3157bce 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -97,7 +97,9 @@ typedef struct nr_uid_linear_allocator_s { #define PROTOCOL_NR_RRC_CTXT_ARGS(CTXT_Pp) PROTOCOL_NR_CTXT_ARGS(CTXT_Pp) // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs -#define NR_RRC_PROCESSING_DELAY_MS 10 +#define NR_RRC_SETUP_DELAY_MS 10 +#define NR_RRC_RECONFIGURATION_DELAY_MS 10 +#define NR_RRC_BWP_SWITCHING_DELAY_MS 6 #define NR_UE_MODULE_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! #define NR_UE_INDEX_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! used to be -1 diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index a7e6e49052..0670765d1b 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -744,7 +744,7 @@ rrc_gNB_generate_defaultRRCReconfiguration( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + NR_RRC_RECONFIGURATION_DELAY_MS); /* Free all NAS PDUs */ for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1024,7 +1024,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + NR_RRC_RECONFIGURATION_DELAY_MS); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1211,7 +1211,7 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + NR_RRC_RECONFIGURATION_DELAY_MS); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_modify_pdusessions; i++) { @@ -1329,7 +1329,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + NR_RRC_RECONFIGURATION_DELAY_MS); /* Free all NAS PDUs */ if (nas_length > 0) { @@ -1894,7 +1894,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( 0, ue_context_pP->ue_context.rnti, NULL, - NR_RRC_PROCESSING_DELAY_MS); + NR_RRC_RECONFIGURATION_DELAY_MS); /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { From 55f1a6b4daed84fd58f044d001672af9d73f6951 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 19 Apr 2022 17:28:48 +0100 Subject: [PATCH 151/233] Stop the scheduling of CSI-RS, of CSI measurement report reception on PUCCH2 and of reception of SRS --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 5 +++++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c | 2 +- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 1ae83b0ffd..d7478474b1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2609,6 +2609,11 @@ void nr_csirs_scheduling(int Mod_idP, for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + + if (sched_ctrl->rrc_processing_timer > 0) { + continue; + } + NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; if (!CellGroup || !CellGroup->spCellConfig || !CellGroup->spCellConfig->spCellConfigDedicated || diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c index bf2af3b388..8680cc25d1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c @@ -125,7 +125,7 @@ void nr_schedule_srs(int module_id, frame_t frame) { sched_ctrl->sched_srs.slot = -1; sched_ctrl->sched_srs.srs_scheduled = false; - if(!UE_info->Msg4_ACKed[UE_id]) { + if(!UE_info->Msg4_ACKed[UE_id] || sched_ctrl->rrc_processing_timer > 0) { continue; } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index e21d1e50f7..e2d9f1035c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -702,7 +702,9 @@ void nr_csi_meas_reporting(int Mod_idP, for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { const NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - if (sched_ctrl->ul_failure==1 && get_softmodem_params()->phy_test==0) continue; + if ((sched_ctrl->rrc_processing_timer > 0) || (sched_ctrl->ul_failure==1 && get_softmodem_params()->phy_test==0)) { + continue; + } if (!CellGroup || !CellGroup->spCellConfig || !CellGroup->spCellConfig->spCellConfigDedicated || !CellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig) continue; const NR_CSI_MeasConfig_t *csi_measconfig = CellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup; From 8e4c513805a12a7757aaed9f8bc278d337f30427 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 19 Apr 2022 18:07:45 +0100 Subject: [PATCH 152/233] Fix RRC processing time when using F1 split --- openair2/RRC/NR/rrc_gNB.c | 140 ++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 65 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 0670765d1b..ab61116924 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -732,19 +732,21 @@ rrc_gNB_generate_defaultRRCReconfiguration( LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - rrc->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - rrc->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - NR_RRC_RECONFIGURATION_DELAY_MS); + if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + rrc->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + rrc->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_RECONFIGURATION_DELAY_MS); + } /* Free all NAS PDUs */ for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1012,19 +1014,21 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( cellGroupConfig); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - rrc->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - rrc->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - NR_RRC_RECONFIGURATION_DELAY_MS); + if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + rrc->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + rrc->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_RECONFIGURATION_DELAY_MS); + } /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { @@ -1199,19 +1203,21 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - NR_RRC_RECONFIGURATION_DELAY_MS); + if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_RECONFIGURATION_DELAY_MS); + } /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_modify_pdusessions; i++) { @@ -1317,19 +1323,21 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - NR_RRC_RECONFIGURATION_DELAY_MS); + if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_RECONFIGURATION_DELAY_MS); + } /* Free all NAS PDUs */ if (nas_length > 0) { @@ -1882,19 +1890,21 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - NR_RRC_RECONFIGURATION_DELAY_MS); + if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + rrc_mac_config_req_gNB(ctxt_pP->module_id, + 0, + RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, + 0, + 0, + 0, + RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, + NULL, + NULL, + 0, + ue_context_pP->ue_context.rnti, + NULL, + NR_RRC_RECONFIGURATION_DELAY_MS); + } /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { From 709db95112d98af9cacc08574fe0b715afaa27e5 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 19 Apr 2022 18:59:56 +0100 Subject: [PATCH 153/233] Fix DL/UL failure due to wrong dedicated BWP when an RA fails on Msg4 and then succeeds --- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index eaf3f6d421..7dd05e29e6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2807,14 +2807,42 @@ void nr_csirs_scheduling(int Mod_idP, void nr_mac_update_timers(module_id_t module_id, frame_t frame, sub_frame_t slot) { + NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; const NR_list_t *UE_list = &UE_info->list; + for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + if (sched_ctrl->rrc_processing_timer > 0) { + sched_ctrl->rrc_processing_timer--; + if (sched_ctrl->rrc_processing_timer == 0) { + LOG_I(NR_MAC, "(%d.%d) De-activating RRC processing timer for UE %d\n", frame, slot, UE_id); + + // If needed, update the Dedicated BWP + const int current_bwp_id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; + const int current_ubwp_id = sched_ctrl->active_ubwp ? sched_ctrl->active_ubwp->bwp_Id : 0; + if(UE_info->CellGroup[UE_id] && + UE_info->CellGroup[UE_id]->spCellConfig && + UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated && + UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList && + UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig && + UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList) { + const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated; + if(*servingCellConfig->firstActiveDownlinkBWP_Id != current_bwp_id) { + sched_ctrl->active_bwp = servingCellConfig->downlinkBWP_ToAddModList->list.array[*servingCellConfig->firstActiveDownlinkBWP_Id - 1]; + LOG_I(NR_MAC, "Changing to DL-BWP %li\n", sched_ctrl->active_bwp->bwp_Id); + } + if(*servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id != current_ubwp_id) { + sched_ctrl->active_ubwp = servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList->list.array[*servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id - 1]; + LOG_I(NR_MAC, "Changing to UL-BWP %li\n", sched_ctrl->active_ubwp->bwp_Id); + } + } + sched_ctrl->update_pdsch_ps = true; sched_ctrl->update_pusch_ps = true; } From f1a38dbc98258cc0d5b7bebfd838388feb9a1cd2 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 20 Apr 2022 13:47:51 +0200 Subject: [PATCH 154/233] CSI-IM harmonization --- openair2/RRC/NR/nr_rrc_config.c | 97 ++++++++++++++++++++++++++++++ openair2/RRC/NR/nr_rrc_config.h | 2 + openair2/RRC/NR/rrc_gNB_reconfig.c | 33 +--------- 3 files changed, 100 insertions(+), 32 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index bb2a180146..be6df0cafc 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -170,6 +170,103 @@ void config_csirs(NR_ServingCellConfigCommon_t *servingcellconfigcommon, csi_MeasConfig->nzp_CSI_RS_ResourceToReleaseList = NULL; } +void set_csiim_offset(struct NR_CSI_ResourcePeriodicityAndOffset *periodicityAndOffset, + struct NR_CSI_ResourcePeriodicityAndOffset *target_periodicityAndOffset) { + + switch(periodicityAndOffset->present) { + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots4: + periodicityAndOffset->choice.slots4 = target_periodicityAndOffset->choice.slots4; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots5: + periodicityAndOffset->choice.slots5 = target_periodicityAndOffset->choice.slots5; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots8: + periodicityAndOffset->choice.slots8 = target_periodicityAndOffset->choice.slots8; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots10: + periodicityAndOffset->choice.slots10 = target_periodicityAndOffset->choice.slots10; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots16: + periodicityAndOffset->choice.slots16 = target_periodicityAndOffset->choice.slots16; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots20: + periodicityAndOffset->choice.slots20 = target_periodicityAndOffset->choice.slots20; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots32: + periodicityAndOffset->choice.slots32 = target_periodicityAndOffset->choice.slots32; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots40: + periodicityAndOffset->choice.slots40 = target_periodicityAndOffset->choice.slots40; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots64: + periodicityAndOffset->choice.slots64 = target_periodicityAndOffset->choice.slots64; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots80: + periodicityAndOffset->choice.slots80 = target_periodicityAndOffset->choice.slots80; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots160: + periodicityAndOffset->choice.slots160 = target_periodicityAndOffset->choice.slots160; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots320: + periodicityAndOffset->choice.slots320 = target_periodicityAndOffset->choice.slots320; + break; + case NR_CSI_ResourcePeriodicityAndOffset_PR_slots640: + periodicityAndOffset->choice.slots640 = target_periodicityAndOffset->choice.slots640; + break; + default: + AssertFatal(1==0,"CSI periodicity not among allowed values\n"); + } + +} + +void config_csiim(int do_csirs, int dl_antenna_ports, int curr_bwp, + NR_CSI_MeasConfig_t *csi_MeasConfig) { + + if (do_csirs && dl_antenna_ports > 1) { + csi_MeasConfig->csi_IM_ResourceToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceToAddModList)); + NR_CSI_IM_Resource_t *imres = calloc(1,sizeof(*imres)); + imres->csi_IM_ResourceId = 0; + NR_NZP_CSI_RS_Resource_t *nzpcsi = NULL; + for (int i=0; inzp_CSI_RS_ResourceToAddModList->list.count; i++){ + nzpcsi = csi_MeasConfig->nzp_CSI_RS_ResourceToAddModList->list.array[i]; + if (nzpcsi->nzp_CSI_RS_ResourceId == imres->csi_IM_ResourceId) + break; + } + AssertFatal(nzpcsi->nzp_CSI_RS_ResourceId == imres->csi_IM_ResourceId, "Couldn't find NZP CSI-RS corresponding to CSI-IM\n"); + imres->csi_IM_ResourceElementPattern = calloc(1,sizeof(*imres->csi_IM_ResourceElementPattern)); + imres->csi_IM_ResourceElementPattern->present = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1; + imres->csi_IM_ResourceElementPattern->choice.pattern1 = calloc(1,sizeof(*imres->csi_IM_ResourceElementPattern->choice.pattern1)); + // starting subcarrier is 4 in the following configuration + // this is ok for current possible CSI-RS configurations (using only the first 4 symbols) + // TODO needs a more dynamic setting if CSI-RS is changed + imres->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern__pattern1__subcarrierLocation_p1_s4; + imres->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1 = nzpcsi->resourceMapping.firstOFDMSymbolInTimeDomain; // same symbol as CSI-RS + imres->freqBand = calloc(1,sizeof(*imres->freqBand)); + imres->freqBand->startingRB = 0; + imres->freqBand->nrofRBs = ((curr_bwp>>2)+(curr_bwp%4>0))<<2; + imres->periodicityAndOffset = calloc(1,sizeof(*imres->periodicityAndOffset)); + // same period and offset of the associated CSI-RS + imres->periodicityAndOffset->present = nzpcsi->periodicityAndOffset->present; + set_csiim_offset(imres->periodicityAndOffset, nzpcsi->periodicityAndOffset); + ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceToAddModList->list,imres); + csi_MeasConfig->csi_IM_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceSetToAddModList)); + NR_CSI_IM_ResourceSet_t *imset = calloc(1,sizeof(*imset)); + imset->csi_IM_ResourceSetId = 0; + NR_CSI_IM_ResourceId_t *res = calloc(1,sizeof(*res)); + *res = imres->csi_IM_ResourceId; + ASN_SEQUENCE_ADD(&imset->csi_IM_Resources,res); + ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceSetToAddModList->list,imset); + } + else { + csi_MeasConfig->csi_IM_ResourceToAddModList = NULL; + csi_MeasConfig->csi_IM_ResourceSetToAddModList = NULL; + } + + csi_MeasConfig->csi_IM_ResourceToReleaseList = NULL; + csi_MeasConfig->csi_IM_ResourceSetToReleaseList = NULL; +} + + void prepare_sim_uecap(NR_UE_NR_Capability_t *cap, NR_ServingCellConfigCommon_t *scc, int numerology, diff --git a/openair2/RRC/NR/nr_rrc_config.h b/openair2/RRC/NR/nr_rrc_config.h index 7a0fe892fd..4401290623 100644 --- a/openair2/RRC/NR/nr_rrc_config.h +++ b/openair2/RRC/NR/nr_rrc_config.h @@ -119,6 +119,8 @@ void config_csirs(NR_ServingCellConfigCommon_t *servingcellconfigcommon, int num_dl_antenna_ports, int curr_bwp, int do_csirs); +void config_csiim(int do_csirs, int dl_antenna_ports, int curr_bwp, + NR_CSI_MeasConfig_t *csi_MeasConfig); void set_dl_mcs_table(int scs, NR_UE_NR_Capability_t *cap, NR_BWP_DownlinkDedicated_t *bwp_Dedicated, NR_ServingCellConfigCommon_t *scc); diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 36113fb7ee..b80708909d 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -1034,39 +1034,8 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco NR_CSI_MeasConfig_t *csi_MeasConfig = calloc(1,sizeof(*csi_MeasConfig)); secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup = csi_MeasConfig; - if (do_csirs && dl_antenna_ports > 1) { - csi_MeasConfig->csi_IM_ResourceToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceToAddModList)); - NR_CSI_IM_Resource_t *imres0 = calloc(1,sizeof(*imres0)); - imres0->csi_IM_ResourceId = 0; - imres0->csi_IM_ResourceElementPattern = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern)); - imres0->csi_IM_ResourceElementPattern->present = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1; - imres0->csi_IM_ResourceElementPattern->choice.pattern1 = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern->choice.pattern1)); - imres0->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern__pattern1__subcarrierLocation_p1_s4; - imres0->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1 = 6; - imres0->freqBand = calloc(1,sizeof(*imres0->freqBand)); - imres0->freqBand->startingRB = 0; - imres0->freqBand->nrofRBs = ((curr_bwp>>2)+(curr_bwp%4>0))<<2; - imres0->periodicityAndOffset = calloc(1,sizeof(*imres0->periodicityAndOffset)); - imres0->periodicityAndOffset->present = NR_CSI_ResourcePeriodicityAndOffset_PR_slots320; - imres0->periodicityAndOffset->choice.slots320 = 0; - ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceToAddModList->list,imres0); - csi_MeasConfig->csi_IM_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceSetToAddModList)); - NR_CSI_IM_ResourceSet_t *imset0 = calloc(1,sizeof(*imset0)); - imset0->csi_IM_ResourceSetId = 0; - NR_CSI_IM_ResourceId_t *res0 = calloc(1,sizeof(*res0)); - *res0 = 0; - ASN_SEQUENCE_ADD(&imset0->csi_IM_Resources,res0); - ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceSetToAddModList->list,imset0); - } - else { - csi_MeasConfig->csi_IM_ResourceToAddModList = NULL; - csi_MeasConfig->csi_IM_ResourceSetToAddModList = NULL; - } - - csi_MeasConfig->csi_IM_ResourceToReleaseList = NULL; - csi_MeasConfig->csi_IM_ResourceSetToReleaseList = NULL; - config_csirs(servingcellconfigcommon, csi_MeasConfig, uid, dl_antenna_ports, curr_bwp, do_csirs); + config_csiim(do_csirs, dl_antenna_ports, curr_bwp, csi_MeasConfig); csi_MeasConfig->csi_SSB_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_SSB_ResourceSetToAddModList)); csi_MeasConfig->csi_SSB_ResourceSetToReleaseList = NULL; From efd99acacc0a5fb9dd7af5753f47de5abade6252 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 20 Apr 2022 15:51:02 +0100 Subject: [PATCH 155/233] Fix read SSpace array index at -1 --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 6 ++++-- openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 6 +++--- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 941efb1b8d..c60b6e153c 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -560,7 +560,8 @@ void configure_ss_coreset(NR_UE_MAC_INST_t *mac, AssertFatal(ss->searchSpaceType != NULL, "ss->searchSpaceType is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot is null\n"); AssertFatal(ss->monitoringSymbolsWithinSlot->buf != NULL, "NR_SearchSpace->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id-1][ss->searchSpaceId - 1] = ss; + AssertFatal(ss->searchSpaceId <= FAPI_NR_MAX_SS, "Invalid searchSpaceId\n"); + mac->SSpace[dl_bwp_id][ss->searchSpaceId - 1] = ss; } struct NR_PDCCH_ConfigCommon__commonSearchSpaceList *commonSearchSpaceList = pdcch_ConfigCommon->choice.setup->commonSearchSpaceList; @@ -575,7 +576,8 @@ void configure_ss_coreset(NR_UE_MAC_INST_t *mac, AssertFatal(css->searchSpaceType != NULL, "css->searchSpaceType is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot != NULL, "css->monitoringSymbolsWithinSlot is null\n"); AssertFatal(css->monitoringSymbolsWithinSlot->buf != NULL, "css->monitoringSymbolsWithinSlot->buf is null\n"); - mac->SSpace[dl_bwp_id-1][css->searchSpaceId - 1] = css; + AssertFatal(css->searchSpaceId <= FAPI_NR_MAX_SS, "Invalid searchSpaceId\n"); + mac->SSpace[dl_bwp_id][css->searchSpaceId - 1] = css; } } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 1ef8a36377..fce6efa103 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -102,7 +102,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t ss_id,mac->ra.ss->searchSpaceId); } else - ss = mac->SSpace[dl_bwp_id-1][ss_id-1]; + ss = mac->SSpace[dl_bwp_id][ss_id-1]; } else ss = mac->search_space_zero; @@ -277,12 +277,12 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl if (bwpd) { for (ss_id = 1; ss_id <= FAPI_NR_MAX_SS; ss_id++){ - if(mac->SSpace[bwp_id-1][ss_id-1]==NULL) { + if(mac->SSpace[bwp_id][ss_id-1]==NULL) { continue; } LOG_D(NR_MAC, "[DCI_CONFIG] ss_id %d\n",ss_id); - NR_SearchSpace_t *ss = mac->SSpace[bwp_id-1][ss_id-1]; + NR_SearchSpace_t *ss = mac->SSpace[bwp_id][ss_id-1]; AssertFatal(ss_id == ss->searchSpaceId,"SS IDs don't correspond\n"); fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15; NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 8424197643..dbf6c3c953 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -2642,7 +2642,7 @@ int get_n_rb(NR_UE_MAC_INST_t *mac, int rnti_type){ case NR_RNTI_P: { if (mac->DLbwp[dl_bwp_id-1]->bwp_Common->pdcch_ConfigCommon->choice.setup->controlResourceSetZero) { uint8_t coreset_id = 0; // assuming controlResourceSetId is 0 for controlResourceSetZero - NR_ControlResourceSet_t *coreset = mac->coreset[dl_bwp_id-1][coreset_id]; + NR_ControlResourceSet_t *coreset = mac->coreset[dl_bwp_id][coreset_id]; get_coreset_rballoc(coreset->frequencyDomainResources.buf,&N_RB,&start_RB); } else { N_RB = NRRIV2BW(mac->scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); From 15777bbf0e8910b36e9ee7b83579ca9dea6311fb Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 20 Apr 2022 17:05:03 +0200 Subject: [PATCH 156/233] fix for ulsim antenna ports --- openair1/SIMULATION/NR_PHY/ulsim.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 1a027a33d5..ade4e2aad4 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -730,7 +730,7 @@ int main(int argc, char **argv) // TODO do a UECAP for phy-sim const gNB_RrcConfigurationReq conf = { - .pdsch_AntennaPorts = { .N1 = n_tx, .N2 = 1, .XP = 1 }, + .pdsch_AntennaPorts = { .N1 = 1, .N2 = 1, .XP = 1 }, .pusch_AntennaPorts = n_rx, .minRXTXTIME = 0, .do_CSIRS = 0, @@ -751,10 +751,10 @@ int main(int argc, char **argv) rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 0, 0, NULL); // UE dedicated configuration rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); - frame_parms->nb_antennas_tx = n_tx; + frame_parms->nb_antennas_tx = 1; frame_parms->nb_antennas_rx = n_rx; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; - cfg->carrier_config.num_tx_ant.value = n_tx; + cfg->carrier_config.num_tx_ant.value = 1; cfg->carrier_config.num_rx_ant.value = n_rx; // nr_phy_config_request_sim(gNB,N_RB_DL,N_RB_DL,mu,0,0x01); phy_init_nr_gNB(gNB,0,1); @@ -771,6 +771,8 @@ int main(int argc, char **argv) PHY_vars_UE_g[0] = malloc(sizeof(PHY_VARS_NR_UE*)); PHY_vars_UE_g[0][0] = UE; memcpy(&UE->frame_parms, frame_parms, sizeof(NR_DL_FRAME_PARMS)); + UE->frame_parms.nb_antennas_tx = n_tx; + frame_parms->nb_antennas_rx = 1; if (init_nr_ue_signal(UE, 1) != 0) { printf("Error at UE NR initialisation\n"); From 588754153857ebe7d58eaa3955c7996cf2b27d65 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 20 Apr 2022 17:16:49 +0100 Subject: [PATCH 157/233] Fix segmentation fault when using multiple Dedicated-BWPs and F1 --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index a954ced6a9..bec1d36fe9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -58,7 +58,7 @@ void calculate_preferred_ul_tda(module_id_t module_id, const NR_BWP_Uplink_t *ub lte_frame_type_t frame_type = nrmac->common_channels->frame_type; const int n = nr_slots_per_frame[*scc->ssbSubcarrierSpacing]; - NR_ServingCellConfigCommonSIB_t *scc_sib1 = get_softmodem_params()->sa ? + NR_ServingCellConfigCommonSIB_t *scc_sib1 = nrmac->common_channels[0].sib1 ? RC.nrmac[module_id]->common_channels[0].sib1->message.choice.c1->choice.systemInformationBlockType1->servingCellConfigCommon : NULL; AssertFatal(scc!=NULL || scc_sib1!=NULL,"We need one serving cell config common\n"); From f49df054566bc7dc1df20a98a3d191f5001e3dd4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 20 Apr 2022 18:55:38 +0200 Subject: [PATCH 158/233] fix for ulschsim antenna ports --- openair1/SIMULATION/NR_PHY/ulschsim.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 3f8062a0ce..d76d438446 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -391,8 +391,6 @@ int main(int argc, char **argv) initTpool(tp_param, gNB->threadPool, true); initNotifiedFIFO(gNB->respDecode); frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH) - frame_parms->nb_antennas_tx = n_tx; - frame_parms->nb_antennas_rx = n_rx; frame_parms->N_RB_DL = N_RB_DL; frame_parms->N_RB_UL = N_RB_UL; frame_parms->Ncp = extended_prefix_flag ? EXTENDED : NORMAL; @@ -400,6 +398,9 @@ int main(int argc, char **argv) memcpy(&gNB->frame_parms, frame_parms, sizeof(NR_DL_FRAME_PARMS)); + gNB->frame_parms.nb_antennas_tx = 1; + gNB->frame_parms.nb_antennas_rx = n_rx; + nr_phy_config_request_sim(gNB, N_RB_UL, N_RB_UL, mu, Nid_cell, SSB_positions); phy_init_nr_gNB(gNB, 0, 1); //lowmem @@ -408,6 +409,9 @@ int main(int argc, char **argv) UE = malloc(sizeof(PHY_VARS_NR_UE)); memcpy(&UE->frame_parms, frame_parms, sizeof(NR_DL_FRAME_PARMS)); + UE->frame_parms.nb_antennas_tx = n_tx; + UE->frame_parms.nb_antennas_rx = 1; + //phy_init_nr_top(frame_parms); if (init_nr_ue_signal(UE, 1) != 0) { printf("Error at UE NR initialisation.\n"); From aeaf4c87b574c22219ad380df63006cfb273e527 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 20 Apr 2022 20:19:56 +0100 Subject: [PATCH 159/233] Fix delay of CU for a very large cellGroupConfig --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index f20e5bf239..c728ebd076 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -2220,9 +2220,7 @@ int16_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, &asn_DEF_NR_CellGroupConfig, //might be added prefix later (void **)&cellGroupConfig, (uint8_t *)masterCellGroup_from_DU->buf, - masterCellGroup_from_DU->size, 0, 0); - - xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)cellGroupConfig); + masterCellGroup_from_DU->size, 0, 0); } else { cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t)); @@ -2249,8 +2247,10 @@ int16_t do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, ue_p->masterCellGroup = cellGroupConfig; if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void*)cellGroupConfig); xer_fprint(stdout, &asn_DEF_NR_DL_CCCH_Message, (void *)&dl_ccch_msg); } + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_CCCH_Message, NULL, (void *)&dl_ccch_msg, From f57f4ce3fedaef00e374db58d0e2e9b41870b2c0 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 22 Apr 2022 09:56:54 +0200 Subject: [PATCH 160/233] bugfix in channel compensation --- openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index b415374dc7..db05e03d4c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -721,7 +721,7 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, ul_ch_mag128[1] = _mm_mulhi_epi16(ul_ch_mag128[1],QAM_amp128); ul_ch_mag128[1] = _mm_slli_epi16(ul_ch_mag128[1],1); - ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128); + ul_ch_mag128b[1] = _mm_mulhi_epi16(ul_ch_mag128b[1],QAM_amp128b); ul_ch_mag128b[1] = _mm_slli_epi16(ul_ch_mag128b[1],1); mmtmpD0 = _mm_madd_epi16(ul_ch128[2],ul_ch128[2]); From 697d72cd01f0742f9d1cb88bc707fbf17ee8f10d Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 22 Apr 2022 18:51:53 +0200 Subject: [PATCH 161/233] adding missing parameter in ulsim --- openair1/SIMULATION/NR_PHY/ulsim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index ade4e2aad4..64ccffabdd 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1171,6 +1171,7 @@ int main(int argc, char **argv) ul_config.ul_config_list[0].pusch_config_pdu.mcs_table = mcs_table; ul_config.ul_config_list[0].pusch_config_pdu.num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; ul_config.ul_config_list[0].pusch_config_pdu.nrOfLayers = precod_nbr_layers; + ul_config.ul_config_list[0].pusch_config_pdu.dmrs_ports = ((1< Date: Tue, 26 Apr 2022 10:40:45 +0200 Subject: [PATCH 162/233] fix(ci): when deploy CN5G, health detection fixed to display the image tag/date in HTML report Signed-off-by: Raphael Defosseux --- ci-scripts/epc.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ci-scripts/epc.py b/ci-scripts/epc.py index 0fc37d4ead..52583bb6d1 100644 --- a/ci-scripts/epc.py +++ b/ci-scripts/epc.py @@ -255,16 +255,19 @@ class EPCManagement(): mySSH.command('mkdir -p ' + self.SourceCodePath + '/scripts', '\$', 5) mySSH.command('cd /opt/oai-cn5g-fed-v1.3/docker-compose', '\$', 5) mySSH.command('python3 ./core-network.py '+self.cfgDeploy, '\$', 60) - time.sleep(2) - mySSH.command('docker-compose -p 5gcn ps -a', '\$', 60) + if re.search('start-mini-as-ue', self.cfgDeploy): + dFile = 'docker-compose-mini-nrf-asue.yaml' + else: + dFile = 'docker-compose-mini-nrf.yaml' + mySSH.command('docker-compose -p 5gcn -f ' + dFile + ' ps -a', '\$', 60) if mySSH.getBefore().count('Up (healthy)') != 6: logging.error('Not all container healthy') else: - logging.debug('OK') - mySSH.command('docker-compose config | grep --colour=never image', '\$', 10) + logging.debug('OK --> all containers are healthy') + mySSH.command('docker-compose -p 5gcn -f ' + dFile + ' config | grep --colour=never image', '\$', 10) listOfImages = mySSH.getBefore() for imageLine in listOfImages.split('\\r\\n'): - res1 = re.search('image: (?P[a-zA-Z0-9\-]+):(?P[a-zA-Z0-9\-]+)', str(imageLine)) + res1 = re.search('image: (?P[a-zA-Z0-9\-/]+):(?P[a-zA-Z0-9\-]+)', str(imageLine)) res2 = re.search('mysql', str(imageLine)) if res1 is not None and res2 is None: html_cell += res1.group('name') + ':' + res1.group('tag') + ' ' From 7e6f8ecfb38514a0490f680c3f3ef2e3ecf9d015 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 26 Apr 2022 10:59:25 +0200 Subject: [PATCH 163/233] fixing bugs and cleaning up ulsim --- openair1/SIMULATION/NR_PHY/ulsim.c | 219 ++++++++++++----------------- 1 file changed, 93 insertions(+), 126 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 64ccffabdd..8b30958131 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -744,13 +744,14 @@ int main(int argc, char **argv) /* RRC parameter validation for secondaryCellGroup */ fix_scd(scd); - AssertFatal((gNB->if_inst = NR_IF_Module_init(0))!=NULL,"Cannot register interface"); + AssertFatal((gNB->if_inst = NR_IF_Module_init(0))!=NULL,"Cannot register interface"); - gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; + gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); + rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 1, + secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); frame_parms->nb_antennas_tx = 1; frame_parms->nb_antennas_rx = n_rx; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; @@ -772,7 +773,7 @@ int main(int argc, char **argv) PHY_vars_UE_g[0][0] = UE; memcpy(&UE->frame_parms, frame_parms, sizeof(NR_DL_FRAME_PARMS)); UE->frame_parms.nb_antennas_tx = n_tx; - frame_parms->nb_antennas_rx = 1; + UE->frame_parms.nb_antennas_rx = 1; if (init_nr_ue_signal(UE, 1) != 0) { printf("Error at UE NR initialisation\n"); @@ -825,7 +826,7 @@ int main(int argc, char **argv) unsigned char *estimated_output_bit; unsigned char *test_input_bit; - uint32_t errors_decoding = 0; + uint32_t errors_decoding = 0; test_input_bit = (unsigned char *) malloc16(sizeof(unsigned char) * 16 * 68 * 384); @@ -858,21 +859,14 @@ int main(int argc, char **argv) /* validate parameters othwerwise default values are used */ /* -U flag can be used to set DMRS parameters*/ - if(modify_dmrs) - { + if(modify_dmrs) { if(dmrs_arg[0] == 0) - { mapping_type = typeA; - } else if (dmrs_arg[0] == 1) - { mapping_type = typeB; - } /* Additional DMRS positions */ if(dmrs_arg[1] >= 0 && dmrs_arg[1] <=3 ) - { add_pos = dmrs_arg[1]; - } } printf("NOTE: DMRS config is modified with Mapping Type %d , Additional Position %d \n", mapping_type, add_pos ); @@ -882,24 +876,22 @@ int main(int argc, char **argv) printf("num dmrs sym %d\n",number_dmrs_symbols); uint8_t nb_re_dmrs = (dmrs_config_type == pusch_dmrs_type1) ? 6 : 4; - if ((frame_parms->nb_antennas_tx==4)&&(precod_nbr_layers==4)) - { + if ((UE->frame_parms.nb_antennas_tx==4)&&(precod_nbr_layers==4)) num_dmrs_cdm_grps_no_data = 2; - } if (transformPrecoder == transformPrecoder_enabled) { AssertFatal(enable_ptrs == 0, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); int8_t index = get_index_for_dmrs_lowpapr_seq((NR_NB_SC_PER_RB/2) * nb_rb); - AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); + AssertFatal(index >= 0, "Num RBs not configured according to 3GPP 38.211 section 6.3.1.4. For PUSCH with transform precoding, num RBs cannot be multiple of any other primenumber other than 2,3,5\n"); dmrs_config_type = pusch_dmrs_type1; printf("[ULSIM]: TRANSFORM PRECODING ENABLED. Num RBs: %d, index for DMRS_SEQ: %d\n", nb_rb, index); } - nb_re_dmrs = nb_re_dmrs * num_dmrs_cdm_grps_no_data; + nb_re_dmrs = nb_re_dmrs * num_dmrs_cdm_grps_no_data; unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, mod_order, precod_nbr_layers); unsigned int TBS = nr_compute_tbs(mod_order, code_rate, nb_rb, nb_symb_sch, nb_re_dmrs * number_dmrs_symbols, 0, 0, precod_nbr_layers); @@ -926,17 +918,12 @@ int main(int argc, char **argv) double ts = 1.0/(frame_parms->subcarrier_spacing * frame_parms->ofdm_symbol_size); /* -T option enable PTRS */ - if(enable_ptrs) - { + if(enable_ptrs) { /* validate parameters othwerwise default values are used */ if(ptrs_arg[0] == 0 || ptrs_arg[0] == 1 || ptrs_arg[0] == 2 ) - { ptrs_time_density = ptrs_arg[0]; - } if(ptrs_arg[1] == 2 || ptrs_arg[1] == 4 ) - { ptrs_freq_density = ptrs_arg[1]; - } pdu_bit_map |= PUSCH_PDU_BITMAP_PUSCH_PTRS; printf("NOTE: PTRS Enabled with L %d, K %d \n", ptrs_time_density, ptrs_freq_density ); } @@ -1052,7 +1039,7 @@ int main(int argc, char **argv) uint8_t round = 0; crc_status = 1; - errors_decoding = 0; + errors_decoding = 0; memset((void*)roundStats,0,50*sizeof(roundStats[0])); while (roundframe_parms, - frame_parms->nb_antennas_tx); + UE->frame_parms.nb_antennas_tx); - if (n_trials==1) - { + if (n_trials==1) { LOG_M("txsig0.m","txs0", &UE->common_vars.txdata[0][slot_offset],slot_length,1,1); LOG_M("txsig0F.m","txs0F", UE->common_vars.txdataF[0],frame_parms->ofdm_symbol_size*14,1,1); - if (precod_nbr_layers > 1) - { + if (precod_nbr_layers > 1) { LOG_M("txsig1.m","txs1", &UE->common_vars.txdata[1][slot_offset],slot_length,1,1); LOG_M("txsig1F.m","txs1F", UE->common_vars.txdataF[1],frame_parms->ofdm_symbol_size*14,1,1); - if (precod_nbr_layers==4) - { + if (precod_nbr_layers==4) { LOG_M("txsig2.m","txs2", &UE->common_vars.txdata[2][slot_offset],slot_length,1,1); LOG_M("txsig3.m","txs3", &UE->common_vars.txdata[3][slot_offset],slot_length,1,1); - LOG_M("txsig2F.m","txs2F", UE->common_vars.txdataF[2],frame_parms->ofdm_symbol_size*14,1,1); LOG_M("txsig3F.m","txs3F", UE->common_vars.txdataF[3],frame_parms->ofdm_symbol_size*14,1,1); } @@ -1241,32 +1224,28 @@ int main(int argc, char **argv) //////////////////////////////////////////////////// tx_offset = frame_parms->get_samples_slot_timestamp(slot,frame_parms,0); txlev_sum = 0; - for (int aa=0; aanb_antennas_tx; aa++) - { + for (int aa=0; aanb_antennas_rx; aa++) { atxlev[aa] = signal_energy(&UE->common_vars.txdata[aa][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples); - txlev_sum += atxlev[aa]; + txlev_sum += atxlev[aa]; if (n_trials==1) printf("txlev[%d] = %d (%f dB) txlev_sum %d\n",aa,atxlev[aa],10*log10((double)atxlev[aa]),txlev_sum); } } else - { n_trials = 1; - } - if (input_fd == NULL ) - { + if (input_fd == NULL) { sigma_dB = 10 * log10((double)txlev_sum * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; sigma = pow(10,sigma_dB/10); if(n_trials==1) printf("sigma %f (%f dB), txlev_sum %f (factor %f)\n",sigma,sigma_dB,10*log10((double)txlev_sum),(double)(double) - frame_parms->ofdm_symbol_size/(12*nb_rb)); + frame_parms->ofdm_symbol_size/(12*nb_rb)); for (i=0; inb_antennas_tx; aa++) { + for (int aa=0; aaframe_parms.nb_antennas_tx; aa++) { s_re[aa][i] = ((double)(((short *)&UE->common_vars.txdata[aa][slot_offset]))[(i<<1)]); s_im[aa][i] = ((double)(((short *)&UE->common_vars.txdata[aa][slot_offset]))[(i<<1)+1]); } @@ -1274,17 +1253,13 @@ int main(int argc, char **argv) if (UE2gNB->max_Doppler == 0) - { multipath_channel(UE2gNB, s_re, s_im, r_re, r_im, slot_length, 0, (n_trials==1)?1:0); - } else { + else multipath_tv_channel(UE2gNB, s_re, s_im, r_re, r_im, 2*slot_length, 0); - } - for (i=0; inb_antennas_rx; ap++) - { - if (channel_model == AWGN) - { + + for (i=0; inb_antennas_rx; ap++) { + if (channel_model == AWGN) { double H_awgn[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 {0.0, 1.0, 0.0, 0.0}, //rx 1 {0.0, 0.0, 1.0, 0.0}, //rx 2 @@ -1299,8 +1274,7 @@ int main(int argc, char **argv) r_re[ap][i] = 0; r_im[ap][i] = 0; - for (int aa=0; aapdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) - { + if(pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { set_ptrs_symb_idx(&ptrsSymPos, pusch_pdu->nr_of_symbols, pusch_pdu->start_symbol_index, @@ -1346,47 +1319,42 @@ int main(int argc, char **argv) ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot); - if (n_trials==1 && round==0) - { - LOG_M("rxsig0.m","rx0",&gNB->common_vars.rxdata[0][slot_offset],slot_length,1,1); - - LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0],14*frame_parms->ofdm_symbol_size,1,1); - if (precod_nbr_layers > 1) - { - LOG_M("rxsig1.m","rx1",&gNB->common_vars.rxdata[1][slot_offset],slot_length,1,1); - LOG_M("rxsigF1.m","rxsF1",gNB->common_vars.rxdataF[1],14*frame_parms->ofdm_symbol_size,1,1); - if (precod_nbr_layers==4) - { - LOG_M("rxsig2.m","rx2",&gNB->common_vars.rxdata[2][slot_offset],slot_length,1,1); - LOG_M("rxsig3.m","rx3",&gNB->common_vars.rxdata[3][slot_offset],slot_length,1,1); - - LOG_M("rxsigF2.m","rxsF2",gNB->common_vars.rxdataF[2],14*frame_parms->ofdm_symbol_size,1,1); - LOG_M("rxsigF3.m","rxsF3",gNB->common_vars.rxdataF[3],14*frame_parms->ofdm_symbol_size,1,1); - } + if (n_trials==1 && round==0) { + LOG_M("rxsig0.m","rx0",&gNB->common_vars.rxdata[0][slot_offset],slot_length,1,1); + LOG_M("rxsigF0.m","rxsF0",gNB->common_vars.rxdataF[0],14*frame_parms->ofdm_symbol_size,1,1); + if (precod_nbr_layers > 1) { + LOG_M("rxsig1.m","rx1",&gNB->common_vars.rxdata[1][slot_offset],slot_length,1,1); + LOG_M("rxsigF1.m","rxsF1",gNB->common_vars.rxdataF[1],14*frame_parms->ofdm_symbol_size,1,1); + if (precod_nbr_layers==4) { + LOG_M("rxsig2.m","rx2",&gNB->common_vars.rxdata[2][slot_offset],slot_length,1,1); + LOG_M("rxsig3.m","rx3",&gNB->common_vars.rxdata[3][slot_offset],slot_length,1,1); + LOG_M("rxsigF2.m","rxsF2",gNB->common_vars.rxdataF[2],14*frame_parms->ofdm_symbol_size,1,1); + LOG_M("rxsigF3.m","rxsF3",gNB->common_vars.rxdataF[3],14*frame_parms->ofdm_symbol_size,1,1); + } } - } + } - if (n_trials == 1 && round==0) { + if (n_trials == 1 && round==0) { #ifdef __AVX2__ - __attribute__((unused)) - int off = ((nb_rb&1) == 1)? 4:0; + __attribute__((unused)) + int off = ((nb_rb&1) == 1)? 4:0; #else - __attribute__((unused)) - int off = 0; + __attribute__((unused)) + int off = 0; #endif - LOG_M("rxsigF0_ext.m","rxsF0_ext", - &gNB->pusch_vars[0]->rxdataF_ext[0][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_M("chestF0.m","chF0", - &gNB->pusch_vars[0]->ul_ch_estimates[0][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); - LOG_M("chestT0.m","chT0", - &gNB->pusch_vars[0]->ul_ch_estimates_time[0][0],frame_parms->ofdm_symbol_size,1,1); - LOG_M("chestF0_ext.m","chF0_ext", - &gNB->pusch_vars[0]->ul_ch_estimates_ext[0][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], - (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); - LOG_M("rxsigF0_comp.m","rxsF0_comp", - &gNB->pusch_vars[0]->rxdataF_comp[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF0_ext.m","rxsF0_ext", + &gNB->pusch_vars[0]->rxdataF_ext[0][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("chestF0.m","chF0", + &gNB->pusch_vars[0]->ul_ch_estimates[0][start_symbol*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); + LOG_M("chestT0.m","chT0", + &gNB->pusch_vars[0]->ul_ch_estimates_time[0][0],frame_parms->ofdm_symbol_size,1,1); + LOG_M("chestF0_ext.m","chF0_ext", + &gNB->pusch_vars[0]->ul_ch_estimates_ext[0][(start_symbol+1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))], + (nb_symb_sch-1)*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); + LOG_M("rxsigF0_comp.m","rxsF0_comp", + &gNB->pusch_vars[0]->rxdataF_comp[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); LOG_M("chmagF0.m","chmF0", &gNB->pusch_vars[0]->ul_ch_mag[0][start_symbol*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size))],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); LOG_M("chmagbF0.m","chmbF0", @@ -1454,48 +1422,47 @@ int main(int argc, char **argv) &gNB->pusch_vars[0]->llr_layers[2][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); LOG_M("rxsigF0_llrlayers3.m","rxsF0_llrlayers3", &gNB->pusch_vars[0]->llr_layers[3][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); - } + } - LOG_M("rxsigF0_llr.m","rxsF0_llr", - &gNB->pusch_vars[0]->llr[0],precod_nbr_layers*(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); - } + LOG_M("rxsigF0_llr.m","rxsF0_llr", + &gNB->pusch_vars[0]->llr[0],precod_nbr_layers*(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); + } //////////////////////////////////////////////////////////// - if ((gNB->ulsch[0]->last_iteration_cnt >= - gNB->ulsch[0]->max_ldpc_iterations+1) || ul_proc_error == 1) { - error_flag = 1; - n_errors[round][snrRun]++; - crc_status = 1; - } else { - crc_status = 0; - } - if(n_trials==1) printf("end of round %d rv_index %d\n",round, rv_index); + if ((gNB->ulsch[0]->last_iteration_cnt >= + gNB->ulsch[0]->max_ldpc_iterations+1) || ul_proc_error == 1) { + error_flag = 1; + n_errors[round][snrRun]++; + crc_status = 1; + } else + crc_status = 0; + if(n_trials==1) printf("end of round %d rv_index %d\n",round, rv_index); - //---------------------------------------------------------- - //----------------- count and print errors ----------------- - //---------------------------------------------------------- + //---------------------------------------------------------- + //----------------- count and print errors ----------------- + //---------------------------------------------------------- - if ((pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) && (SNR==snr0) && (trial==0) && (round==0)) { - ptrs_symbols = 0; - for (int i = pusch_pdu->start_symbol_index; i < pusch_pdu->start_symbol_index + pusch_pdu->nr_of_symbols; i++){ - ptrs_symbols += ((gNB->pusch_vars[UE_id]->ptrs_symbols) >> i) & 1; - } - /* 2*5*(50/2), for RB = 50,K = 2 for 5 OFDM PTRS symbols */ - available_bits -= 2 * ptrs_symbols * ((nb_rb + ptrs_freq_density - 1) /ptrs_freq_density); - printf("[ULSIM][PTRS] Available bits are: %5u, removed PTRS bits are: %5d \n",available_bits, (ptrsSymbPerSlot * ptrsRePerSymb * 2) ); - } + if ((pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) && (SNR==snr0) && (trial==0) && (round==0)) { + ptrs_symbols = 0; + for (int i = pusch_pdu->start_symbol_index; i < pusch_pdu->start_symbol_index + pusch_pdu->nr_of_symbols; i++) + ptrs_symbols += ((gNB->pusch_vars[UE_id]->ptrs_symbols) >> i) & 1; - for (i = 0; i < available_bits; i++) { - - if(((ulsch_ue->harq_processes[harq_pid]->f[i] == 0) && (gNB->pusch_vars[UE_id]->llr[i] <= 0)) || - ((ulsch_ue->harq_processes[harq_pid]->f[i] == 1) && (gNB->pusch_vars[UE_id]->llr[i] >= 0))) - { - /*if(errors_scrambling == 0) - printf("\x1B[34m" "[frame %d][trial %d]\t1st bit in error in unscrambling = %d\n" "\x1B[0m", frame, trial, i);*/ - errors_scrambling[round][snrRun]++; - } - } - round++; + /* 2*5*(50/2), for RB = 50,K = 2 for 5 OFDM PTRS symbols */ + available_bits -= 2 * ptrs_symbols * ((nb_rb + ptrs_freq_density - 1) /ptrs_freq_density); + printf("[ULSIM][PTRS] Available bits are: %5u, removed PTRS bits are: %5d \n",available_bits, (ptrsSymbPerSlot * ptrsRePerSymb * 2) ); + } + + for (i = 0; i < available_bits; i++) { + + if(((ulsch_ue->harq_processes[harq_pid]->f[i] == 0) && (gNB->pusch_vars[UE_id]->llr[i] <= 0)) || + ((ulsch_ue->harq_processes[harq_pid]->f[i] == 1) && (gNB->pusch_vars[UE_id]->llr[i] >= 0))) { + + /*if(errors_scrambling == 0) + printf("\x1B[34m" "[frame %d][trial %d]\t1st bit in error in unscrambling = %d\n" "\x1B[0m", frame, trial, i);*/ + errors_scrambling[round][snrRun]++; + } + } + round++; } // round From 8d2e5fd7aad1b35f8d07714da5bfc499d8e9b4c8 Mon Sep 17 00:00:00 2001 From: Raphael Defosseux Date: Tue, 26 Apr 2022 11:53:32 +0200 Subject: [PATCH 164/233] fix(ci): CN5G does not keep PCAP in artifacts Signed-off-by: Raphael Defosseux --- ci-scripts/epc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci-scripts/epc.py b/ci-scripts/epc.py index 52583bb6d1..0e0afefaaf 100644 --- a/ci-scripts/epc.py +++ b/ci-scripts/epc.py @@ -539,7 +539,7 @@ class EPCManagement(): mySSH.command('python3 ./core-network.py '+self.cfgUnDeploy, '\$', 60) mySSH.command('docker volume prune --force || true', '\$', 60) time.sleep(2) - mySSH.command('tshark -r /tmp/oai-cn5g.pcap | egrep --colour=never "Tracking area update" ','\$', 30) + mySSH.command('tshark -r /tmp/oai-cn5g-v1.3.pcap | egrep --colour=never "Tracking area update" ','\$', 30) result = re.search('Tracking area update request', mySSH.getBefore()) if result is not None: message = 'UE requested ' + str(mySSH.getBefore().count('Tracking area update request')) + 'Tracking area update request(s)' @@ -833,8 +833,8 @@ class EPCManagement(): mySSH.command('zip mme.log.zip mme_check_run.*', '\$', 60) elif re.match('OAICN5G', self.Type, re.IGNORECASE): mySSH.command('cd ' + self.SourceCodePath + '/logs','\$', 5) - mySSH.command('cp -f /tmp/oai-cn5g.pcap .','\$', 30) - mySSH.command('zip mme.log.zip oai-amf.log oai-nrf.log oai-cn5g.pcap','\$', 30) + mySSH.command('cp -f /tmp/oai-cn5g-v1.3.pcap .','\$', 30) + mySSH.command('zip mme.log.zip oai-amf.log oai-nrf.log oai-cn5g*.pcap','\$', 30) mySSH.command('mv mme.log.zip ' + self.SourceCodePath + '/scripts','\$', 30) elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE): mySSH.command('zip mme.log.zip mme*.log', '\$', 60) From 5761f24eb064259316b6e7538d1de337421e7454 Mon Sep 17 00:00:00 2001 From: Raphael Defosseux Date: Tue, 26 Apr 2022 13:58:39 +0200 Subject: [PATCH 165/233] fix(ci): 1st step to migrate the formatting / license check to the new pipeline env Signed-off-by: Raphael Defosseux --- ci-scripts/checkCodingFormattingRules.sh | 4 +- ci-scripts/cls_static_code_analysis.py | 50 +++++++++++++++++++ .../docker/Dockerfile.formatting.bionic | 25 ++++++++++ ci-scripts/main.py | 4 +- ci-scripts/xml_class_list.yml | 1 + ci-scripts/xml_files/formatting_check.xml | 37 ++++++++++++++ 6 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 ci-scripts/docker/Dockerfile.formatting.bionic create mode 100644 ci-scripts/xml_files/formatting_check.xml diff --git a/ci-scripts/checkCodingFormattingRules.sh b/ci-scripts/checkCodingFormattingRules.sh index a805f765df..c29941cade 100755 --- a/ci-scripts/checkCodingFormattingRules.sh +++ b/ci-scripts/checkCodingFormattingRules.sh @@ -59,7 +59,7 @@ if [ $# -eq 0 ] then echo " ---- Checking the whole repository ----" echo "" - NB_FILES_TO_FORMAT=`astyle --dry-run --options=ci-scripts/astyle-options.txt --recursive *.c *.h | grep -c Formatted || true` + NB_FILES_TO_FORMAT=`astyle --dry-run --options=ci-scripts/astyle-options.txt --recursive --exclude=ci-scripts --exclude=cmake_targets *.c *.h | grep -c Formatted || true` echo "Nb Files that do NOT follow OAI rules: $NB_FILES_TO_FORMAT" echo $NB_FILES_TO_FORMAT > ./oai_rules_result.txt @@ -136,7 +136,7 @@ fi # Merge request scenario MERGE_COMMMIT=`git log -n1 --pretty=format:%H` -TARGET_INIT_COMMIT=`cat .git/refs/remotes/origin/$TARGET_BRANCH` +TARGET_INIT_COMMIT=`git log -n1 --pretty=format:%H origin/$TARGET_BRANCH` echo " ---- Checking the modified files by the merge request ----" echo "" diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py index d6796ad414..feea106190 100644 --- a/ci-scripts/cls_static_code_analysis.py +++ b/ci-scripts/cls_static_code_analysis.py @@ -237,3 +237,53 @@ class StaticCodeAnalysis(): return 0 + def LicenceAndFormattingCheck(self, HTML): + if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + lIpAddr = self.eNBIPAddress + lUserName = self.eNBUserName + lPassWord = self.eNBPassword + lSourcePath = self.eNBSourceCodePath + + if lIpAddr == '' or lUserName == '' or lPassWord == '' or lSourcePath == '': + HELP.GenericHelp(CONST.Version) + sys.exit('Insufficient Parameter') + logging.debug('Building on server: ' + lIpAddr) + mySSH = SSH.SSHConnection() + mySSH.open(lIpAddr, lUserName, lPassWord) + + self.testCase_id = HTML.testCase_id + + # on RedHat/CentOS .git extension is mandatory + result = re.search('([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository) + if result is not None: + full_ran_repo_name = self.ranRepository.replace('git/', 'git') + else: + full_ran_repo_name = self.ranRepository + '.git' + mySSH.command('mkdir -p ' + lSourcePath, '\$', 5) + mySSH.command('cd ' + lSourcePath, '\$', 5) + mySSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone ' + full_ran_repo_name + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600) + # Raphael: here add a check if git clone or git fetch went smoothly + mySSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5) + mySSH.command('git config user.name "OAI Jenkins"', '\$', 5) + + mySSH.command('echo ' + lPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30) + mySSH.command('mkdir -p cmake_targets/log', '\$', 5) + # if the commit ID is provided use it to point to it + if self.ranCommitID != '': + mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30) + # if the branch is not develop, then it is a merge request and we need to do + # the potential merge. Note that merge conflicts should already been checked earlier + if (self.ranAllowMerge): + if self.ranTargetBranch == '': + if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'): + mySSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5) + else: + logging.debug('Merging with the target branch: ' + self.ranTargetBranch) + mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5) + + mySSH.command('docker image rm oai-formatting-check:latest || true', '\$', 60) + mySSH.command('docker build --target oai-formatting-check --tag oai-formatting-check:latest --file ci-scripts/docker/Dockerfile.formatting.bionic . > cmake_targets/log/oai-formatting-check.txt 2>&1', '\$', 600) + + return 0 diff --git a/ci-scripts/docker/Dockerfile.formatting.bionic b/ci-scripts/docker/Dockerfile.formatting.bionic new file mode 100644 index 0000000000..0f29c8b833 --- /dev/null +++ b/ci-scripts/docker/Dockerfile.formatting.bionic @@ -0,0 +1,25 @@ +FROM ubuntu:bionic AS oai-formatting-check + +ARG MERGE_REQUEST +ARG SRC_BRANCH +ARG TARGET_BRANCH +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \ + DEBIAN_FRONTEND=noninteractive apt-get install --yes \ + astyle \ + gawk \ + git + +WORKDIR /oai-ran +COPY . . + +RUN /bin/bash -c "if [[ -v MERGE_REQUEST ]]; then echo 'Source Branch = $SRC_BRANCH'; echo 'Target Branch = $TARGET_BRANCH'; else echo 'Push to develop'; fi" +RUN /bin/bash -c "if [[ -v MERGE_REQUEST ]]; then ./ci-scripts/checkCodingFormattingRules.sh --src-branch $SRC_BRANCH --target-branch $TARGET_BRANCH; else ./ci-scripts/checkCodingFormattingRules.sh; fi" && \ + ls -lst *.txt && \ + cat *.txt + +#RUN ./ci-scripts/checkCodingFormattingRules.sh && \ +# ls -lst *.txt +# diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 03947bdfc9..41577b0aa3 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -799,7 +799,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re HTML.startTime=int(round(time.time() * 1000)) while CiTestObj.FailReportCnt < CiTestObj.repeatCounts[0] and RAN.prematureExit: RAN.prematureExit=False - # At every iteratin of the retry loop, a separator will be added + # At every iteration of the retry loop, a separator will be added # pass CiTestObj.FailReportCnt as parameter of HTML.CreateHtmlRetrySeparator HTML.CreateHtmlRetrySeparator(CiTestObj.FailReportCnt) for test_case_id in todo_tests: @@ -923,6 +923,8 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re CONTAINERS.UndeployObject(HTML, RAN) elif action == 'Cppcheck_Analysis': SCA.CppCheckAnalysis(HTML) + elif action == 'LicenceAndFormattingCheck': + SCA.LicenceAndFormattingCheck(HTML) elif action == 'Deploy_Run_PhySim': PHYSIM.Deploy_PhySim(HTML, RAN) elif action == 'DeployGenObject': diff --git a/ci-scripts/xml_class_list.yml b/ci-scripts/xml_class_list.yml index d6f75191a8..b305aff424 100755 --- a/ci-scripts/xml_class_list.yml +++ b/ci-scripts/xml_class_list.yml @@ -47,3 +47,4 @@ - PingFromContainer - IperfFromContainer - StatsFromGenObject + - LicenceAndFormattingCheck diff --git a/ci-scripts/xml_files/formatting_check.xml b/ci-scripts/xml_files/formatting_check.xml new file mode 100644 index 0000000000..bdf47af65d --- /dev/null +++ b/ci-scripts/xml_files/formatting_check.xml @@ -0,0 +1,37 @@ + + + formatting-tab + License and Formatting Checks + wrench + + 000002 + + + + + LicenceAndFormattingCheck + License and Formatting Checks + + + From 275522eae3ba0a3bcedc83023537c109721e4103 Mon Sep 17 00:00:00 2001 From: Raphael Defosseux Date: Tue, 26 Apr 2022 16:54:42 +0200 Subject: [PATCH 166/233] feat(ci): reporting in HTML of formatting and licences checks Signed-off-by: Raphael Defosseux --- ci-scripts/cls_static_code_analysis.py | 157 +++++++++++++++++- .../docker/Dockerfile.formatting.bionic | 18 +- ci-scripts/main.py | 4 +- 3 files changed, 170 insertions(+), 9 deletions(-) diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py index feea106190..5b45b48ba6 100644 --- a/ci-scripts/cls_static_code_analysis.py +++ b/ci-scripts/cls_static_code_analysis.py @@ -37,6 +37,7 @@ import logging import os from pathlib import Path import time +from multiprocessing import Process, Lock, SimpleQueue #----------------------------------------------------------- # OAI Testing modules @@ -275,15 +276,167 @@ class StaticCodeAnalysis(): mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 30) # if the branch is not develop, then it is a merge request and we need to do # the potential merge. Note that merge conflicts should already been checked earlier + argToPass = '' if (self.ranAllowMerge): + argToPass = '--build-arg MERGE_REQUEST=true --build-arg SRC_BRANCH=' + self.ranBranch if self.ranTargetBranch == '': if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'): mySSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5) + argToPass += ' --build-arg TARGET_BRANCH=develop ' else: logging.debug('Merging with the target branch: ' + self.ranTargetBranch) mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5) + argToPass += ' --build-arg TARGET_BRANCH=' + self.ranTargetBranch + ' ' mySSH.command('docker image rm oai-formatting-check:latest || true', '\$', 60) - mySSH.command('docker build --target oai-formatting-check --tag oai-formatting-check:latest --file ci-scripts/docker/Dockerfile.formatting.bionic . > cmake_targets/log/oai-formatting-check.txt 2>&1', '\$', 600) + mySSH.command('docker build --target oai-formatting-check --tag oai-formatting-check:latest ' + argToPass + '--file ci-scripts/docker/Dockerfile.formatting.bionic . > cmake_targets/log/oai-formatting-check.txt 2>&1', '\$', 600) - return 0 + mySSH.command('docker image rm oai-formatting-check:latest || true', '\$', 60) + mySSH.command('docker image prune --force', '\$', 60) + mySSH.command('docker volume prune --force', '\$', 60) + + # Analyzing the logs + mySSH.command('cd ' + lSourcePath + '/cmake_targets', '\$', 5) + mySSH.command('mkdir -p build_log_' + self.testCase_id, '\$', 5) + mySSH.command('mv log/* ' + 'build_log_' + self.testCase_id, '\$', 5) + mySSH.close() + + mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/build_log_' + self.testCase_id + '/*', '.') + + finalStatus = 0 + if (os.path.isfile('./oai-formatting-check.txt')): + analyzed = False + nbFilesNotFormatted = 0 + listFiles = False + listFilesNotFormatted = [] + circularHeaderDependency = False + circularHeaderDependencyFiles = [] + gnuGplLicence = False + gnuGplLicenceFiles = [] + suspectLicence = False + suspectLicenceFiles = [] + with open('./oai-formatting-check.txt', 'r') as logfile: + for line in logfile: + ret = re.search('./ci-scripts/checkCodingFormattingRules.sh', str(line)) + if ret is not None: + analyzed = True + if analyzed: + ret = re.search('Nb Files that do NOT follow OAI rules: (?P[0-9\.]+)', str(line)) + if ret is not None: + nbFilesNotFormatted = int(ret.group('nb_errors')) + + if re.search('=== Files not properly formatted ===', str(line)) is not None: + listFiles = True + if listFiles: + if re.search('Removing intermediate container', str(line)) is not None: + listFiles = False + elif re.search('Running in|Files not properly formatted', str(line)) is not None: + pass + else: + listFilesNotFormatted.append(str(line).strip()) + + if re.search('=== Files with incorrect define protection ===', str(line)) is not None: + circularHeaderDependency = True + if circularHeaderDependency: + if re.search('Removing intermediate container', str(line)) is not None: + circularHeaderDependency = False + elif re.search('Running in|Files with incorrect define protection', str(line)) is not None: + pass + else: + circularHeaderDependencyFiles.append(str(line).strip()) + + if re.search('=== Files with a GNU GPL licence Banner ===', str(line)) is not None: + gnuGplLicence = True + if gnuGplLicence: + if re.search('Removing intermediate container', str(line)) is not None: + gnuGplLicence = False + elif re.search('Running in|Files with a GNU GPL licence Banner', str(line)) is not None: + pass + else: + gnuGplLicenceFiles.append(str(line).strip()) + + if re.search('=== Files with a suspect Banner ===', str(line)) is not None: + suspectLicence = True + if suspectLicence: + if re.search('Removing intermediate container', str(line)) is not None: + suspectLicence = False + elif re.search('Running in|Files with a suspect Banner', str(line)) is not None: + pass + else: + suspectLicenceFiles.append(str(line).strip()) + + logfile.close() + if analyzed: + logging.debug('files not formatted properly: ' + str(nbFilesNotFormatted)) + if nbFilesNotFormatted == 0: + HTML.CreateHtmlTestRow('File(s) Format', 'OK', CONST.ALL_PROCESSES_OK) + else: + html_queue = SimpleQueue() + html_cell = '

\n'
+					html_cell += 'Number of files not following OAI Rules: ' + str(nbFilesNotFormatted) + '\n'
+					for nFile in listFilesNotFormatted:
+						html_cell += str(nFile).strip() + '\n'
+					html_cell += '
' + html_queue.put(html_cell) + HTML.CreateHtmlTestRowQueue('File(s) Format', 'KO', 1, html_queue) + del(html_cell) + del(html_queue) + + logging.debug('header files not respecting the circular dependency protection: ' + str(len(circularHeaderDependencyFiles))) + if len(circularHeaderDependencyFiles) == 0: + HTML.CreateHtmlTestRow('Header Circular Dependency', 'OK', CONST.ALL_PROCESSES_OK) + else: + html_queue = SimpleQueue() + html_cell = '
\n'
+					html_cell += 'Number of files not respecting: ' + str(len(circularHeaderDependencyFiles)) + '\n'
+					for nFile in circularHeaderDependencyFiles:
+						html_cell += str(nFile).strip() + '\n'
+					html_cell += '
' + html_queue.put(html_cell) + HTML.CreateHtmlTestRowQueue('Header Circular Dependency', 'KO', 1, html_queue) + del(html_cell) + del(html_queue) + finalStatus = -1 + + logging.debug('files with a GNU GPL license: ' + str(len(gnuGplLicenceFiles))) + if len(gnuGplLicenceFiles) == 0: + HTML.CreateHtmlTestRow('Files w/ GNU GPL License', 'OK', CONST.ALL_PROCESSES_OK) + else: + html_queue = SimpleQueue() + html_cell = '
\n'
+					html_cell += 'Number of files not respecting: ' + str(len(gnuGplLicenceFiles)) + '\n'
+					for nFile in gnuGplLicenceFiles:
+						html_cell += str(nFile).strip() + '\n'
+					html_cell += '
' + html_queue.put(html_cell) + HTML.CreateHtmlTestRowQueue('Files w/ GNU GPL License', 'KO', 1, html_queue) + del(html_cell) + del(html_queue) + finalStatus = -1 + + logging.debug('files with a suspect license: ' + str(len(suspectLicenceFiles))) + if len(suspectLicenceFiles) == 0: + HTML.CreateHtmlTestRow('Files with suspect license', 'OK', CONST.ALL_PROCESSES_OK) + else: + html_queue = SimpleQueue() + html_cell = '
\n'
+					html_cell += 'Number of files not respecting: ' + str(len(suspectLicenceFiles)) + '\n'
+					for nFile in suspectLicenceFiles:
+						html_cell += str(nFile).strip() + '\n'
+					html_cell += '
' + html_queue.put(html_cell) + HTML.CreateHtmlTestRowQueue('Files with suspect license', 'KO', 1, html_queue) + del(html_cell) + del(html_queue) + finalStatus = -1 + + else: + finalStatus = -1 + HTML.htmleNBFailureMsg = 'Could not fully analyze oai-formatting-check.txt file' + HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE) + else: + finalStatus = -1 + HTML.htmleNBFailureMsg = 'Could not access oai-formatting-check.txt file' + HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE) + + return finalStatus diff --git a/ci-scripts/docker/Dockerfile.formatting.bionic b/ci-scripts/docker/Dockerfile.formatting.bionic index 0f29c8b833..01417a3bdb 100644 --- a/ci-scripts/docker/Dockerfile.formatting.bionic +++ b/ci-scripts/docker/Dockerfile.formatting.bionic @@ -16,10 +16,16 @@ WORKDIR /oai-ran COPY . . RUN /bin/bash -c "if [[ -v MERGE_REQUEST ]]; then echo 'Source Branch = $SRC_BRANCH'; echo 'Target Branch = $TARGET_BRANCH'; else echo 'Push to develop'; fi" -RUN /bin/bash -c "if [[ -v MERGE_REQUEST ]]; then ./ci-scripts/checkCodingFormattingRules.sh --src-branch $SRC_BRANCH --target-branch $TARGET_BRANCH; else ./ci-scripts/checkCodingFormattingRules.sh; fi" && \ - ls -lst *.txt && \ - cat *.txt +RUN /bin/bash -c "if [[ -v MERGE_REQUEST ]]; then ./ci-scripts/checkCodingFormattingRules.sh --src-branch $SRC_BRANCH --target-branch $TARGET_BRANCH; else ./ci-scripts/checkCodingFormattingRules.sh; fi" -#RUN ./ci-scripts/checkCodingFormattingRules.sh && \ -# ls -lst *.txt -# +RUN echo "=== Files not properly formatted ===" && \ + /bin/bash -c "if [[ -f oai_rules_result_list.txt ]]; then cat oai_rules_result_list.txt; fi" + +RUN echo "=== Files with incorrect define protection ===" && \ + /bin/bash -c "if [[ -f header-files-w-incorrect-define.txt ]]; then cat header-files-w-incorrect-define.txt; fi" + +RUN echo "=== Files with a GNU GPL licence Banner ===" && \ + /bin/bash -c "if [[ -f files-w-gnu-gpl-license-banner.txt ]]; then cat files-w-gnu-gpl-license-banner.txt; fi" + +RUN echo "=== Files with a suspect Banner ===" && \ + /bin/bash -c "if [[ -f files-w-suspect-banner.txt ]]; then cat files-w-suspect-banner.txt; fi" diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 41577b0aa3..adef885942 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -924,7 +924,9 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re elif action == 'Cppcheck_Analysis': SCA.CppCheckAnalysis(HTML) elif action == 'LicenceAndFormattingCheck': - SCA.LicenceAndFormattingCheck(HTML) + ret = SCA.LicenceAndFormattingCheck(HTML) + if ret != 0: + RAN.prematureExit = True elif action == 'Deploy_Run_PhySim': PHYSIM.Deploy_PhySim(HTML, RAN) elif action == 'DeployGenObject': From cb456a23cc1f60de459d4184725dd64688ea12f0 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Tue, 26 Apr 2022 20:25:53 +0100 Subject: [PATCH 167/233] Fix in atxlev computation in ulsim (the loop should be over nb_antennas_tx and not nb_antennas_rx) and remove wrong sigma_dB computation --- openair1/SIMULATION/NR_PHY/ulsim.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 8b30958131..3d16eeaa2f 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1224,7 +1224,7 @@ int main(int argc, char **argv) //////////////////////////////////////////////////// tx_offset = frame_parms->get_samples_slot_timestamp(slot,frame_parms,0); txlev_sum = 0; - for (int aa=0; aanb_antennas_rx; aa++) { + for (int aa=0; aanb_antennas_tx; aa++) { atxlev[aa] = signal_energy(&UE->common_vars.txdata[aa][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples); @@ -1279,9 +1279,6 @@ int main(int argc, char **argv) r_im[ap][i] += s_im[aa][i]*H_awgn[ap][aa]; } } - - sigma_dB = 10 * log10((double)atxlev[ap] * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; - sigma = pow(10,sigma_dB/10); ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); // convert to fixed point ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); From e168c0f33bbeafd3675561486adb9c73ad6454c8 Mon Sep 17 00:00:00 2001 From: Manish Date: Wed, 27 Apr 2022 05:31:12 +0000 Subject: [PATCH 168/233] help menu updated in document for native architecture. Compilation steps corrected on Ubuntu 20. Signed-off-by: Manish --- doc/BUILD.md | 1 + doc/TESTING_5GSA_setup.md | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/doc/BUILD.md b/doc/BUILD.md index a52ce98cb5..70d69f5afd 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -193,6 +193,7 @@ Using the help option of the build script you can get the list of available opti | --UE | maintained and tested in CI | build `lte-uesoftmodem` the LTE UE | | --gNB | maintained and tested in CI | build `nr-softmodem` the 5G gNodeB | | --nrUE | maintained and tested in CI | build `nr-uesoftmodem` the 5G UE | +| --arch-native | maintained | build with native architecture optimization | | --usrp-recplay | deprecated | use the USRP configuration parameters to use the record player. | | --build-lib | maintained | build optional shared library(ies), which can then be loaded at run time via command line option. Use the --help option to get the list of supported optional libraries. `all` can be used to build all available optional libraries. | | --UE-conf-nvram | maintained | Specifies the path to the input file used by the conf2uedata utility. defaults to [openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf](../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf) | diff --git a/doc/TESTING_5GSA_setup.md b/doc/TESTING_5GSA_setup.md index 9b27549df4..4b86e12297 100644 --- a/doc/TESTING_5GSA_setup.md +++ b/doc/TESTING_5GSA_setup.md @@ -82,15 +82,7 @@ To get the code and build the gNB executable: git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git git checkout develop - # Install dependencies in Ubuntu 20.04 - cd - cd openairinterface5g/ - source oaienv - cd cmake_targets/ - ./install_external_packages.ubuntu20 - # Build OAI gNB - cd cd openairinterface5g/ source oaienv cd cmake_targets/ From 7e172e348bc279a2fc26474a3c8d984b7f82937f Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Wed, 27 Apr 2022 11:51:29 +0100 Subject: [PATCH 169/233] Fix from frame_parms->nb_antennas_tx to UE->frame_parms.nb_antennas_tx in ulsim --- openair1/SIMULATION/NR_PHY/ulsim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 3d16eeaa2f..6521324bec 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1224,7 +1224,7 @@ int main(int argc, char **argv) //////////////////////////////////////////////////// tx_offset = frame_parms->get_samples_slot_timestamp(slot,frame_parms,0); txlev_sum = 0; - for (int aa=0; aanb_antennas_tx; aa++) { + for (int aa=0; aaframe_parms.nb_antennas_tx; aa++) { atxlev[aa] = signal_energy(&UE->common_vars.txdata[aa][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples); From 264599a071d69a3f653fb2573e466c4c93619f55 Mon Sep 17 00:00:00 2001 From: hardy Date: Thu, 28 Apr 2022 11:16:19 +0200 Subject: [PATCH 170/233] update AS UE IP address after IT migration --- ci-scripts/ci_ueinfra.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-scripts/ci_ueinfra.yaml b/ci-scripts/ci_ueinfra.yaml index b888f6bb59..51fc4a264e 100644 --- a/ci-scripts/ci_ueinfra.yaml +++ b/ci-scripts/ci_ueinfra.yaml @@ -55,7 +55,7 @@ amarisoft_ue_1: Duration : 60 Ping : /tmp/test_ue1.log UELog : /tmp/ue1.log - HostIPAddress : 192.168.18.89 + HostIPAddress : 172.21.16.144 HostUsername : root HostPassword : toor HostSourceCodePath : /tmp From 6af3d0a3641907961b0676262fd99407deeec208 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 28 Apr 2022 11:27:27 +0200 Subject: [PATCH 171/233] change in ulsim test to show degradation in performances for 2rx scenario --- cmake_targets/autotests/test_case_list.xml | 4 ++-- openair1/SIMULATION/NR_PHY/ulsim.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml index 5687810e69..4f564b9dd8 100755 --- a/cmake_targets/autotests/test_case_list.xml +++ b/cmake_targets/autotests/test_case_list.xml @@ -1306,7 +1306,7 @@ (Test12: SC-FDMA, 216 PRBs), (Test13: SC-FDMA, 273 PRBs), (Test14: SC-FDMA, 3 DMRS), - (Test15: MCS 16 50 PRBs 2 RX_Antenna) + (Test15: MCS 19 50 PRBs 2 RX_Antenna) (Test16: MCS 9 106 PRBs MIMO 2 layers) (Test17: MCS 9 106 PRBs MIMO 4 layers) @@ -1329,7 +1329,7 @@ -n50 -s5 -Z -r216 -R217 -n50 -s5 -Z -r270 -R273 -n100 -s5 -Z -U 2 0 2 - -n100 -m16 -s10 -z2 + -n100 -m19 -s10 -S15 -z2 -n100 -m9 -r106 -s10 -W2 -y2 -z2 -n100 -m9 -r106 -s13 -W4 -y4 -z4 diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 3d16eeaa2f..6521324bec 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1224,7 +1224,7 @@ int main(int argc, char **argv) //////////////////////////////////////////////////// tx_offset = frame_parms->get_samples_slot_timestamp(slot,frame_parms,0); txlev_sum = 0; - for (int aa=0; aanb_antennas_tx; aa++) { + for (int aa=0; aaframe_parms.nb_antennas_tx; aa++) { atxlev[aa] = signal_energy(&UE->common_vars.txdata[aa][tx_offset + 5*frame_parms->ofdm_symbol_size + 4*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0], frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples); From 99fde7f7cb09c2300e52da699725dff1830dfb4e Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Thu, 28 Apr 2022 11:50:27 +0100 Subject: [PATCH 172/233] Fix some needed debug logs considering the multi-layer case --- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index db05e03d4c..16c58210e4 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -616,22 +616,21 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, #ifdef DEBUG_CH_COMP int16_t *rxF, *ul_ch; int prnt_idx; + for (int nl=0; nlnb_antennas_rx; aarx++) { + rxF = (int16_t *) &rxdataF_ext[aarx][symbol * (off + (nb_rb * 12))]; + ul_ch = (int16_t *) &ul_ch_estimates_ext[nl * frame_parms->nb_antennas_rx + aarx][symbol * (off + (nb_rb * 12))]; - for (int ant=0; antnb_antennas_rx; ant++) { - rxF = (int16_t *)&rxdataF_ext[ant][symbol*(off+(nb_rb*12))]; - ul_ch = (int16_t *)&ul_ch_estimates_ext[ant][symbol*(off+(nb_rb*12))]; - - printf("--------------------symbol = %d, mod_order = %d, output_shift = %d-----------------------\n", symbol, mod_order, output_shift); - printf("----------------Before compensation------------------\n"); - - for (prnt_idx=0;prnt_idx<12*5*2;prnt_idx+=2){ - - printf("rxF[%d] = (%d,%d)\n", prnt_idx>>1, rxF[prnt_idx],rxF[prnt_idx+1]); - printf("ul_ch[%d] = (%d,%d)\n", prnt_idx>>1, ul_ch[prnt_idx],ul_ch[prnt_idx+1]); + printf("--------symbol = %d, mod_order = %d, output_shift = %d, layer %i, antenna rx = %d -----------\n", + symbol, mod_order, output_shift, nl, aarx); + printf("----------------Before compensation------------------\n"); + for (prnt_idx = 0; prnt_idx < 12 * 5 * 2; prnt_idx += 2) { + printf("rxF[%d] = (%d,%d)\n", prnt_idx >> 1, rxF[prnt_idx], rxF[prnt_idx + 1]); + printf("ul_ch[%d] = (%d,%d)\n", prnt_idx >> 1, ul_ch[prnt_idx], ul_ch[prnt_idx + 1]); + } } } - #endif #ifdef DEBUG_CH_MAG @@ -1137,19 +1136,17 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, #ifdef DEBUG_CH_COMP + for (int nl2=0; nl2nb_antennas_rx; aarx2++) { + rxF = (int16_t *)&rxdataF_comp[nl2*frame_parms->nb_antennas_rx+aarx2][(symbol*(off+(nb_rb*12)))]; - for (int ant=0; antnb_antennas_rx; ant++) { - rxF = (int16_t *)&rxdataF_comp[ant][(symbol*(off+(nb_rb*12)))]; - - printf("----------------After compansation------------------\n"); - - for (prnt_idx=0;prnt_idx<12*5*2;prnt_idx+=2){ - - printf("rxF[%d] = (%d,%d)\n", prnt_idx>>1, rxF[prnt_idx],rxF[prnt_idx+1]); + printf("--------After compansation, layer %i, antenna rx %i----------\n", nl2, aarx2); + for (prnt_idx=0;prnt_idx<12*5*2;prnt_idx+=2){ + printf("rxF[%d] = (%d,%d)\n", prnt_idx>>1, rxF[prnt_idx],rxF[prnt_idx+1]); + } } } - #endif #ifdef DEBUG_CH_MAG From 65f09fecb8e6daf48ecba36a80a7a61612ff734b Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Thu, 28 Apr 2022 14:18:05 +0100 Subject: [PATCH 173/233] First code simplification in UL channel estimation --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index c3360f692e..f2871eef10 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -321,43 +321,27 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; ul_ch+=8; - for (pilot_cnt=3; pilot_cnt<(6*nb_rb_pusch-3); pilot_cnt += 2) { + for (pilot_cnt=3; pilot_cnt<(6*nb_rb_pusch-3); pilot_cnt++) { ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - #ifdef DEBUG_PUSCH - printf("pilot %u : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); - printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); - #endif - multadd_real_vector_complex_scalar(fm, - ch, - ul_ch, - 8); - pil += 2; - re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; - //printf("ul_ch addr %p\n",ul_ch); +#ifdef DEBUG_PUSCH + printf("pilot %4u: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d)\n", + pilot_cnt, pil[0], pil[1], rxF[0], rxF[1], ch[0], ch[1]); + //printf("data %4u: rxF -> (%4d,%4d) (%2d)\n",pilot_cnt, rxF[2], rxF[3], dBc(rxF[2], rxF[3])); +#endif - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - - #ifdef DEBUG_PUSCH - printf("pilot %u : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt+1,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); - printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt+1,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); - #endif - multadd_real_vector_complex_scalar(fmm, - ch, - ul_ch, - 8); - - //for (int i= 0; i<16; i++) - //printf("ul_ch addr %p %d\n", ul_ch+i, *(ul_ch+i)); + if (pilot_cnt%2 == 0) { + multadd_real_vector_complex_scalar(fmm, ch, ul_ch, 8); + ul_ch+=8; + } else { + multadd_real_vector_complex_scalar(fm, ch, ul_ch, 8); + } pil += 2; re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol_offset+nushift+re_offset)]; - ul_ch+=8; + rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; } // Treat first 2 pilots specially (right edge) From 4ecb0401e036a48cca294d449681cee49aeb7782 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Thu, 28 Apr 2022 14:34:43 +0100 Subject: [PATCH 174/233] Second code simplification in UL channel estimation --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 101 ++---------------- 1 file changed, 7 insertions(+), 94 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index f2871eef10..acf69fbba2 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -227,101 +227,10 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, //if ((gNB->frame_parms.N_RB_UL&1)==0) { if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1 && gNB->prb_interpolation == 0){ + LOG_D(PHY,"PUSCH estimation DMRS type 1, Freq-domain interpolation"); - // Treat first 2 pilots specially (left edge) - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); -#ifdef DEBUG_PUSCH - LOG_I(PHY, "In %s ch 0 %d\n", __FUNCTION__, ((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])); - LOG_I(PHY, "In %s pilot 0 : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n", - __FUNCTION__, - rxF[0], - rxF[1], - dBc(rxF[0],rxF[1]), - ch[0], - ch[1], - dBc(ch[0],ch[1]), - pil[0], - pil[1]); - LOG_I(PHY, "In %s data 0 : rxF - > (%d,%d) (%d)\n", __FUNCTION__, rxF[2], rxF[3], dBc(rxF[2],rxF[3])); -#endif - - multadd_real_vector_complex_scalar(fl, - ch, - ul_ch, - 8); - pil += 2; - re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; - //for (int i= 0; i<8; i++) - //printf("ul_ch addr %p %d\n", ul_ch+i, *(ul_ch+i)); - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - -#ifdef DEBUG_PUSCH - LOG_I(PHY, "In %s pilot 1 : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n", - __FUNCTION__, - rxF[0], - rxF[1], - dBc(rxF[0],rxF[1]), - ch[0], - ch[1], - dBc(ch[0],ch[1]), - pil[0], - pil[1]); - LOG_I(PHY, "In %s data 1 : rxF - > (%d,%d) (%d)\n", - __FUNCTION__, - rxF[2], - rxF[3], - dBc(rxF[2],rxF[3])); -#endif - - multadd_real_vector_complex_scalar(fml, - ch, - ul_ch, - 8); - pil += 2; - re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; - //printf("ul_ch addr %p\n",ul_ch); - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - -#ifdef DEBUG_PUSCH - LOG_I(PHY, "In %s pilot 2 : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n", - __FUNCTION__, - rxF[0], - rxF[1], - dBc(rxF[0],rxF[1]), - ch[0], - ch[1], - dBc(ch[0],ch[1]), - pil[0], - pil[1]); - LOG_I(PHY, "In %s data 2 : rxF - > (%d,%d) (%d)\n", - __FUNCTION__, - rxF[2], - rxF[3], - dBc(rxF[2],rxF[3])); -#endif - - multadd_real_vector_complex_scalar(fmm, - ch, - ul_ch, - 8); - - //for (int i= 0; i<16; i++) - //printf("ul_ch addr %p %d\n", ul_ch+i, *(ul_ch+i)); - - pil += 2; - re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; - ul_ch+=8; - - for (pilot_cnt=3; pilot_cnt<(6*nb_rb_pusch-3); pilot_cnt++) { + for (pilot_cnt = 0; pilot_cnt < (6*nb_rb_pusch-3); pilot_cnt++) { ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); @@ -332,7 +241,11 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, //printf("data %4u: rxF -> (%4d,%4d) (%2d)\n",pilot_cnt, rxF[2], rxF[3], dBc(rxF[2], rxF[3])); #endif - if (pilot_cnt%2 == 0) { + if (pilot_cnt == 0) { + multadd_real_vector_complex_scalar(fl, ch, ul_ch, 8); + } else if (pilot_cnt == 1) { + multadd_real_vector_complex_scalar(fml, ch, ul_ch, 8); + } else if (pilot_cnt%2 == 0) { multadd_real_vector_complex_scalar(fmm, ch, ul_ch, 8); ul_ch+=8; } else { From 735b51a06b59228ef41c26ca8ea7e1d1ce133d2a Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Thu, 28 Apr 2022 15:03:26 +0100 Subject: [PATCH 175/233] Third code simplification in UL channel estimation --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 81 ++++--------------- 1 file changed, 14 insertions(+), 67 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index acf69fbba2..2e3d9d8c56 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -230,7 +230,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, LOG_D(PHY,"PUSCH estimation DMRS type 1, Freq-domain interpolation"); - for (pilot_cnt = 0; pilot_cnt < (6*nb_rb_pusch-3); pilot_cnt++) { + for (pilot_cnt = 0; pilot_cnt < 6*nb_rb_pusch; pilot_cnt++) { ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); @@ -245,6 +245,11 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, multadd_real_vector_complex_scalar(fl, ch, ul_ch, 8); } else if (pilot_cnt == 1) { multadd_real_vector_complex_scalar(fml, ch, ul_ch, 8); + } else if (pilot_cnt == (6*nb_rb_pusch-2)) { + multadd_real_vector_complex_scalar(fmr, ch, ul_ch, 8); + ul_ch+=8; + } else if (pilot_cnt == (6*nb_rb_pusch-1)) { + multadd_real_vector_complex_scalar(fr, ch, ul_ch, 8); } else if (pilot_cnt%2 == 0) { multadd_real_vector_complex_scalar(fmm, ch, ul_ch, 8); ul_ch+=8; @@ -256,54 +261,6 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; } - - // Treat first 2 pilots specially (right edge) - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); -#ifdef DEBUG_PUSCH - printf("pilot %u : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); - printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); -#endif - multadd_real_vector_complex_scalar(fm, - ch, - ul_ch, - 8); - - //for (int i= 0; i<8; i++) - //printf("ul_ch addr %p %d\n", ul_ch+i, *(ul_ch+i)); - - pil += 2; - re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol_offset+nushift+re_offset)]; - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); -#ifdef DEBUG_PUSCH - printf("ch 0 %d\n",((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])); - printf("pilot %u : rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt+1,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); - printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt+1,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); -#endif - multadd_real_vector_complex_scalar(fmr, - ch, - ul_ch, - 8); - - pil += 2; - re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol_offset+nushift+re_offset)]; - ul_ch+=8; - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); -#ifdef DEBUG_PUSCH - printf("pilot %u: rxF - > (%d,%d) (%d) ch -> (%d,%d) (%d), pil -> (%d,%d) \n",pilot_cnt+2,rxF[0],rxF[1],dBc(rxF[0],rxF[1]),ch[0],ch[1],dBc(ch[0],ch[1]),pil[0],pil[1]); - printf("data %u : rxF - > (%d,%d) (%d)\n",pilot_cnt+2,rxF[2],rxF[3],dBc(rxF[2],rxF[3])); -#endif - multadd_real_vector_complex_scalar(fr, - ch, - ul_ch, - 8); - // check if PRB crosses DC and improve estimates around DC if ((bwp_start_subcarrier < gNB->frame_parms.ofdm_symbol_size) && (bwp_start_subcarrier+nb_rb_pusch*12 >= gNB->frame_parms.ofdm_symbol_size)) { @@ -323,10 +280,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, // for proper allignment of SIMD vectors if((gNB->frame_parms.N_RB_UL&1)==0) { - multadd_real_vector_complex_scalar(fdcl, - ch, - ul_ch-4, - 8); + multadd_real_vector_complex_scalar(fdcl, ch, ul_ch-4, 8); pil += 4; re_offset = (re_offset+4) % gNB->frame_parms.ofdm_symbol_size; @@ -334,16 +288,11 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - multadd_real_vector_complex_scalar(fdcr, - ch, - ul_ch-4, - 8); - } - else { - multadd_real_vector_complex_scalar(fdclh, - ch, - ul_ch, - 8); + multadd_real_vector_complex_scalar(fdcr, ch, ul_ch-4, 8); + + } else { + + multadd_real_vector_complex_scalar(fdclh, ch, ul_ch, 8); pil += 4; re_offset = (re_offset+4) % gNB->frame_parms.ofdm_symbol_size; @@ -351,12 +300,10 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - multadd_real_vector_complex_scalar(fdcrh, - ch, - ul_ch, - 8); + multadd_real_vector_complex_scalar(fdcrh, ch, ul_ch, 8); } } + #ifdef DEBUG_PUSCH ul_ch = (int16_t *)&ul_ch_estimates[p*gNB->frame_parms.nb_antennas_rx+aarx][ch_offset]; for(uint16_t idxP=0; idxP Date: Thu, 28 Apr 2022 16:24:21 +0100 Subject: [PATCH 176/233] Improve debug log --- openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 2e3d9d8c56..b55a9694d8 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -307,10 +307,11 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, #ifdef DEBUG_PUSCH ul_ch = (int16_t *)&ul_ch_estimates[p*gNB->frame_parms.nb_antennas_rx+aarx][ch_offset]; for(uint16_t idxP=0; idxP Date: Thu, 28 Apr 2022 21:04:41 +0100 Subject: [PATCH 177/233] UL channel estimation with a formulation closer to 3GPP, because this will be useful next --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 106 ++++++++++++------ openair1/PHY/NR_REFSIG/nr_dmrs_rx.c | 2 +- 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index b55a9694d8..5a196a4030 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -76,6 +76,10 @@ void freq2time(uint16_t ofdm_symbol_size, } } +// Table 6.4.1.1.3-1/2 from TS 38.211 +const uint16_t delta1[8] = {0, 0, 1, 1, 0, 0, 1, 1}; +const uint16_t delta2[12] = {0, 0, 2, 2, 4, 4, 0, 0, 2, 2, 4, 4}; + int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, unsigned char Ns, unsigned short p, @@ -86,7 +90,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, int pilot[3280] __attribute__((aligned(16))); unsigned char aarx; - unsigned short k; + unsigned short k0; unsigned int pilot_cnt,re_cnt; int16_t ch[2],ch_r[2],ch_l[2],*pil,*rxF,*ul_ch; int16_t *fl,*fm,*fr,*fml,*fmr,*fmm,*fdcl,*fdcr,*fdclh,*fdcrh; @@ -112,18 +116,18 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, symbol_offset = gNB->frame_parms.ofdm_symbol_size*symbol; - k = bwp_start_subcarrier; + k0 = bwp_start_subcarrier; int re_offset; uint16_t nb_rb_pusch = pusch_pdu->rb_size; - LOG_D(PHY, "In %s: ch_offset %d, soffset %d, symbol_offset %d OFDM size %d, Ns = %d, k = %d symbol %d\n", + LOG_D(PHY, "In %s: ch_offset %d, soffset %d, symbol_offset %d, OFDM size %d, Ns = %d, k0 = %d, symbol %d\n", __FUNCTION__, ch_offset, soffset, symbol_offset, gNB->frame_parms.ofdm_symbol_size, Ns, - k, + k0, symbol); switch (nushift) { @@ -206,60 +210,92 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, } #endif + // Based on Table 6.4.1.1.3-1/2 from TS 38.211 + int delta = 0; + //int deltas[3] = {0}; + //int n_delta = 0; + if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1) { + delta = delta1[p]; + /*if (pusch_pdu->nrOfLayers <= 2) { + n_delta = 1; + } else { + n_delta = 2; + deltas[1] = 1; + }*/ + } else { + delta = delta2[p]; + /*if (pusch_pdu->nrOfLayers <= 2) { + n_delta = 1; + } else if (pusch_pdu->nrOfLayers <= 4) { + n_delta = 2; + deltas[1] = 2; + } else { + n_delta = 3; + deltas[1] = 2; + deltas[2] = 4; + }*/ + } + for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { - re_offset = k; /* Initializing the Resource element offset for each Rx antenna */ - pil = (int16_t *)&pilot[0]; - rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+k+nushift)]; + rxF = (int16_t *)&rxdataF[aarx][(soffset + symbol_offset + k0 + nushift)]; ul_ch = (int16_t *)&ul_ch_estimates[p*gNB->frame_parms.nb_antennas_rx+aarx][ch_offset]; - re_offset = k; + re_offset = k0; memset(ul_ch,0,4*(gNB->frame_parms.ofdm_symbol_size)); #ifdef DEBUG_PUSCH LOG_I(PHY, "In %s symbol_offset %d, nushift %d\n", __FUNCTION__, symbol_offset, nushift); LOG_I(PHY, "In %s ch est pilot addr %p, N_RB_UL %d\n", __FUNCTION__, &pilot[0], gNB->frame_parms.N_RB_UL); - LOG_I(PHY, "In %s bwp_start_subcarrier %d, k %d, first_carrier %d, nb_rb_pusch %d\n", __FUNCTION__, bwp_start_subcarrier, k, gNB->frame_parms.first_carrier_offset, nb_rb_pusch); + LOG_I(PHY, "In %s bwp_start_subcarrier %d, k0 %d, first_carrier %d, nb_rb_pusch %d\n", __FUNCTION__, bwp_start_subcarrier, k0, gNB->frame_parms.first_carrier_offset, nb_rb_pusch); LOG_I(PHY, "In %s rxF addr %p p %d\n", __FUNCTION__, rxF, p); LOG_I(PHY, "In %s ul_ch addr %p nushift %d\n", __FUNCTION__, ul_ch, nushift); #endif - //if ((gNB->frame_parms.N_RB_UL&1)==0) { if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1 && gNB->prb_interpolation == 0){ LOG_D(PHY,"PUSCH estimation DMRS type 1, Freq-domain interpolation"); - for (pilot_cnt = 0; pilot_cnt < 6*nb_rb_pusch; pilot_cnt++) { + // For configuration type 1: k = 4*n + 2*k' + delta, + // where k' is 0 or 1, and delta is in Table 6.4.1.1.3-1 from TS 38.211 - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); + pilot_cnt = 0; + for (int n = 0; n < 3*nb_rb_pusch; n++) { + for (int k_line = 0; k_line <= 1; k_line++) { + + pilot_cnt++; + + re_offset = (k0 + (n<<2) + (k_line<<1) + delta) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *)&rxdataF[aarx][(soffset + symbol_offset + re_offset)]; + + ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); + ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); #ifdef DEBUG_PUSCH - printf("pilot %4u: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d)\n", - pilot_cnt, pil[0], pil[1], rxF[0], rxF[1], ch[0], ch[1]); - //printf("data %4u: rxF -> (%4d,%4d) (%2d)\n",pilot_cnt, rxF[2], rxF[3], dBc(rxF[2], rxF[3])); + printf("pilot %4u: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d)\n", + pilot_cnt, pil[0], pil[1], rxF[0], rxF[1], ch[0], ch[1]); + //printf("data %4u: rxF -> (%4d,%4d) (%2d)\n",pilot_cnt, rxF[2], rxF[3], dBc(rxF[2], rxF[3])); #endif - if (pilot_cnt == 0) { - multadd_real_vector_complex_scalar(fl, ch, ul_ch, 8); - } else if (pilot_cnt == 1) { - multadd_real_vector_complex_scalar(fml, ch, ul_ch, 8); - } else if (pilot_cnt == (6*nb_rb_pusch-2)) { - multadd_real_vector_complex_scalar(fmr, ch, ul_ch, 8); - ul_ch+=8; - } else if (pilot_cnt == (6*nb_rb_pusch-1)) { - multadd_real_vector_complex_scalar(fr, ch, ul_ch, 8); - } else if (pilot_cnt%2 == 0) { - multadd_real_vector_complex_scalar(fmm, ch, ul_ch, 8); - ul_ch+=8; - } else { - multadd_real_vector_complex_scalar(fm, ch, ul_ch, 8); - } + if (pilot_cnt == 0) { + multadd_real_vector_complex_scalar(fl, ch, ul_ch, 8); + } else if (pilot_cnt == 1) { + multadd_real_vector_complex_scalar(fml, ch, ul_ch, 8); + } else if (pilot_cnt == (6*nb_rb_pusch-2)) { + multadd_real_vector_complex_scalar(fmr, ch, ul_ch, 8); + ul_ch+=8; + } else if (pilot_cnt == (6*nb_rb_pusch-1)) { + multadd_real_vector_complex_scalar(fr, ch, ul_ch, 8); + } else if (pilot_cnt%2 == 0) { + multadd_real_vector_complex_scalar(fmm, ch, ul_ch, 8); + ul_ch+=8; + } else { + multadd_real_vector_complex_scalar(fm, ch, ul_ch, 8); + } - pil += 2; - re_offset = (re_offset+2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(soffset+symbol_offset+nushift+re_offset)]; + pil += 2; + } } // check if PRB crosses DC and improve estimates around DC @@ -267,7 +303,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, ul_ch = (int16_t *)&ul_ch_estimates[p*gNB->frame_parms.nb_antennas_rx+aarx][ch_offset]; uint16_t idxDC = 2*(gNB->frame_parms.ofdm_symbol_size - bwp_start_subcarrier); uint16_t idxPil = idxDC/2; - re_offset = k; + re_offset = k0; pil = (int16_t *)&pilot[0]; pil += (idxPil-2); ul_ch += (idxDC-4); diff --git a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c index 920b78eac2..4a8aaab4cb 100644 --- a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c +++ b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c @@ -39,7 +39,7 @@ #include "nr_refsig.h" #include "PHY/defs_gNB.h" -/*Table 7.4.1.1.2-1/2 from 38.211 */ +// Table 6.4.1.1.3-1/2 from TS 38.211 int wf1[8][2] = {{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1}}; int wt1[8][2] = {{1,1},{1,1},{1,1},{1,1},{1,-1},{1,-1},{1,-1},{1,-1}}; int wf2[12][2] = {{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1}}; From 724e155a3a7d9e7f7467921cd285e1007cb61039 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Thu, 28 Apr 2022 23:57:19 +0100 Subject: [PATCH 178/233] Move UL delta computation for a function --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 23 ++++++++----------- openair1/PHY/NR_REFSIG/nr_dmrs_rx.c | 9 ++++++++ openair1/PHY/NR_REFSIG/nr_refsig.h | 2 ++ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 5a196a4030..633d453494 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -76,10 +76,6 @@ void freq2time(uint16_t ofdm_symbol_size, } } -// Table 6.4.1.1.3-1/2 from TS 38.211 -const uint16_t delta1[8] = {0, 0, 1, 1, 0, 0, 1, 1}; -const uint16_t delta2[12] = {0, 0, 2, 2, 4, 4, 0, 0, 2, 2, 4, 4}; - int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, unsigned char Ns, unsigned short p, @@ -211,20 +207,17 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, #endif // Based on Table 6.4.1.1.3-1/2 from TS 38.211 - int delta = 0; - //int deltas[3] = {0}; - //int n_delta = 0; + /*int deltas[3] = {0}; + int n_delta = 0; if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1) { - delta = delta1[p]; - /*if (pusch_pdu->nrOfLayers <= 2) { + if (pusch_pdu->nrOfLayers <= 2) { n_delta = 1; } else { n_delta = 2; deltas[1] = 1; - }*/ + } } else { - delta = delta2[p]; - /*if (pusch_pdu->nrOfLayers <= 2) { + if (pusch_pdu->nrOfLayers <= 2) { n_delta = 1; } else if (pusch_pdu->nrOfLayers <= 4) { n_delta = 2; @@ -233,8 +226,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, n_delta = 3; deltas[1] = 2; deltas[2] = 4; - }*/ - } + } + }*/ for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { @@ -260,6 +253,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, // For configuration type 1: k = 4*n + 2*k' + delta, // where k' is 0 or 1, and delta is in Table 6.4.1.1.3-1 from TS 38.211 + int delta = nr_pusch_dmrs_delta(pusch_dmrs_type1, p); + pilot_cnt = 0; for (int n = 0; n < 3*nb_rb_pusch; n++) { for (int k_line = 0; k_line <= 1; k_line++) { diff --git a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c index 4a8aaab4cb..4f089562f6 100644 --- a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c +++ b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c @@ -40,8 +40,10 @@ #include "PHY/defs_gNB.h" // Table 6.4.1.1.3-1/2 from TS 38.211 +int delta1[8] = {0, 0, 1, 1, 0, 0, 1, 1}; int wf1[8][2] = {{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1}}; int wt1[8][2] = {{1,1},{1,1},{1,1},{1,1},{1,-1},{1,-1},{1,-1},{1,-1}}; +int delta2[12] = {0, 0, 2, 2, 4, 4, 0, 0, 2, 2, 4, 4}; int wf2[12][2] = {{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1},{1,1},{1,-1}}; int wt2[12][2] = {{1,1},{1,1},{1,1},{1,1},{1,1},{1,1},{1,-1},{1,-1},{1,-1},{1,-1},{1,-1},{1,-1}}; @@ -49,6 +51,13 @@ int wt2[12][2] = {{1,1},{1,1},{1,1},{1,1},{1,1},{1,1},{1,-1},{1,-1},{1,-1},{1,-1 short nr_rx_mod_table[14] = {0,0,23170,-23170,-23170,23170,23170,-23170,23170,23170,-23170,-23170,-23170,23170}; short nr_rx_nmod_table[14] = {0,0,-23170,23170,23170,-23170,-23170,23170,-23170,-23170,23170,23170,23170,-23170}; +int nr_pusch_dmrs_delta(uint8_t dmrs_config_type, unsigned short p) { + if (dmrs_config_type == pusch_dmrs_type1) { + return delta1[p]; + } else { + return delta2[p]; + } +} int nr_pusch_dmrs_rx(PHY_VARS_gNB *gNB, unsigned int Ns, diff --git a/openair1/PHY/NR_REFSIG/nr_refsig.h b/openair1/PHY/NR_REFSIG/nr_refsig.h index df3dad1e59..ad632431eb 100644 --- a/openair1/PHY/NR_REFSIG/nr_refsig.h +++ b/openair1/PHY/NR_REFSIG/nr_refsig.h @@ -42,6 +42,8 @@ void nr_init_csi_rs(PHY_VARS_gNB* gNB, uint32_t Nid); void nr_gold_pusch(PHY_VARS_gNB* gNB, int nscid, uint32_t nid); +int nr_pusch_dmrs_delta(uint8_t dmrs_config_type, unsigned short p); + int nr_pusch_dmrs_rx(PHY_VARS_gNB *gNB, unsigned int Ns, unsigned int *nr_gold_pusch, From 719d7fb8be19a584454334a5d6ff2013dd3feb28 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Fri, 29 Apr 2022 00:51:55 +0100 Subject: [PATCH 179/233] Improvements in UL channel estimation to consider multiple antennas for dmrs type 1 --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 633d453494..9bc93938ce 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -246,33 +246,41 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, LOG_I(PHY, "In %s ul_ch addr %p nushift %d\n", __FUNCTION__, ul_ch, nushift); #endif - if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1 && gNB->prb_interpolation == 0){ + if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1 && gNB->prb_interpolation == 0) { LOG_D(PHY,"PUSCH estimation DMRS type 1, Freq-domain interpolation"); // For configuration type 1: k = 4*n + 2*k' + delta, // where k' is 0 or 1, and delta is in Table 6.4.1.1.3-1 from TS 38.211 - int delta = nr_pusch_dmrs_delta(pusch_dmrs_type1, p); - pilot_cnt = 0; + for (int n = 0; n < 3*nb_rb_pusch; n++) { + + // LS estimation + ch[0] = 0; + ch[1] = 0; + for (int p2 = 0; p2nrOfLayers; p2+=2) { + int delta2 = nr_pusch_dmrs_delta(pusch_dmrs_type1, p2); + for (int k_line = 0; k_line <= 1; k_line++) { + re_offset = (k0 + (n << 2) + (k_line << 1) + delta2) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *) &rxdataF[aarx][(soffset + symbol_offset + re_offset)]; + ch[0] += (int16_t) (((int32_t) pil[0] * rxF[0] - (int32_t) pil[1] * rxF[1]) >> 15); + ch[1] += (int16_t) (((int32_t) pil[0] * rxF[1] + (int32_t) pil[1] * rxF[0]) >> 15); + pil += 2; + } + } + + // Channel interpolation for (int k_line = 0; k_line <= 1; k_line++) { - pilot_cnt++; - - re_offset = (k0 + (n<<2) + (k_line<<1) + delta) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *)&rxdataF[aarx][(soffset + symbol_offset + re_offset)]; - - ch[0] = (int16_t)(((int32_t)pil[0]*rxF[0] - (int32_t)pil[1]*rxF[1])>>15); - ch[1] = (int16_t)(((int32_t)pil[0]*rxF[1] + (int32_t)pil[1]*rxF[0])>>15); - #ifdef DEBUG_PUSCH + re_offset = (k0 + (n << 2) + (k_line << 1)) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *) &rxdataF[aarx][(soffset + symbol_offset + re_offset)]; printf("pilot %4u: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d)\n", pilot_cnt, pil[0], pil[1], rxF[0], rxF[1], ch[0], ch[1]); //printf("data %4u: rxF -> (%4d,%4d) (%2d)\n",pilot_cnt, rxF[2], rxF[3], dBc(rxF[2], rxF[3])); #endif - if (pilot_cnt == 0) { multadd_real_vector_complex_scalar(fl, ch, ul_ch, 8); } else if (pilot_cnt == 1) { @@ -288,8 +296,6 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, } else { multadd_real_vector_complex_scalar(fm, ch, ul_ch, 8); } - - pil += 2; } } From c128a5d9e298d17e13dd24e63b9fc5e433b1e9c3 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Fri, 29 Apr 2022 01:11:27 +0100 Subject: [PATCH 180/233] Remove SUPPORT_PMI_MATRIC flag --- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 16c58210e4..c22fb011f1 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -359,25 +359,15 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, memcpy1((void*)&rxF_ext[2*neg_length],(void*)rxF,pos_length*sizeof(int32_t)); } - #ifdef SUPPORT_PMI_MATRIC - for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) - #else - aatx = aarx; - #endif - { + for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) { ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; - memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); } } else { - #ifdef SUPPORT_PMI_MATRIC - for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) - #else - aatx = aarx; - #endif - { + + for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) { ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; @@ -2098,11 +2088,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->ul_ch_mag0, gNB->pusch_vars[ulsch_id]->ul_ch_magb0, gNB->pusch_vars[ulsch_id]->rxdataF_comp, - #ifdef SUPPORT_PMI_MATRIC (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, - #else - NULL, - #endif frame_parms, symbol, nb_re_pusch, @@ -2118,11 +2104,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, gNB->pusch_vars[ulsch_id]->rxdataF_comp, gNB->pusch_vars[ulsch_id]->ul_ch_mag0, gNB->pusch_vars[ulsch_id]->ul_ch_magb0, - #ifdef SUPPORT_PMI_MATRIC (rel15_ul->nrOfLayers>1) ? gNB->pusch_vars[ulsch_id]->rho : NULL, - #else - NULL, - #endif rel15_ul->nrOfLayers, symbol, rel15_ul->rb_size, From e23679431063b42a091f515cec3da3f381b5277b Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Fri, 29 Apr 2022 01:22:33 +0100 Subject: [PATCH 181/233] Put in ulsim a channel matrix H_awgn_mimo different from identity (it is a matrix equal to matrix used in dlsim) --- openair1/SIMULATION/NR_PHY/ulsim.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 6521324bec..b64b4c22c5 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1260,23 +1260,19 @@ int main(int argc, char **argv) for (i=0; inb_antennas_rx; ap++) { if (channel_model == AWGN) { - double H_awgn[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 - {0.0, 1.0, 0.0, 0.0}, //rx 1 - {0.0, 0.0, 1.0, 0.0}, //rx 2 - {0.0, 0.0, 0.0, 1.0}};//rx 3 - #if 0 - double H_awgn_mimo[4][4] ={{1.0, 0.5, 0.25, 0.125},//rx 0 - {0.5, 1.0, 0.5, 0.25}, //rx 1 - {0.25, 0.5, 1.0, 0.5}, //rx 2 - {0.125, 0.25, 0.5, 1.0}};//rx 3 - #endif + + double H_awgn_mimo[4][4] ={{1.0, 0.2, 0.1, 0.05}, //rx 0 + {0.2, 1.0, 0.2, 0.1}, //rx 1 + {0.1, 0.2, 1.0, 0.2}, //rx 2 + {0.05, 0.1, 0.2, 1.0}}; //rx 3 + // sum up signals from different Tx antennas r_re[ap][i] = 0; r_im[ap][i] = 0; for (int aa=0; aa Date: Fri, 29 Apr 2022 11:00:40 +0100 Subject: [PATCH 182/233] Fix high MCS test fail for UL (it do not fix the degradation yet) --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 9bc93938ce..e180be6604 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -206,28 +206,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, } #endif - // Based on Table 6.4.1.1.3-1/2 from TS 38.211 - /*int deltas[3] = {0}; - int n_delta = 0; - if (pusch_pdu->dmrs_config_type == pusch_dmrs_type1) { - if (pusch_pdu->nrOfLayers <= 2) { - n_delta = 1; - } else { - n_delta = 2; - deltas[1] = 1; - } - } else { - if (pusch_pdu->nrOfLayers <= 2) { - n_delta = 1; - } else if (pusch_pdu->nrOfLayers <= 4) { - n_delta = 2; - deltas[1] = 2; - } else { - n_delta = 3; - deltas[1] = 2; - deltas[2] = 4; - } - }*/ + uint8_t b_shift = pusch_pdu->nrOfLayers < 4 ? 1 : log2_approx(pusch_pdu->nrOfLayers>>1); for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { @@ -265,8 +244,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, for (int k_line = 0; k_line <= 1; k_line++) { re_offset = (k0 + (n << 2) + (k_line << 1) + delta2) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *) &rxdataF[aarx][(soffset + symbol_offset + re_offset)]; - ch[0] += (int16_t) (((int32_t) pil[0] * rxF[0] - (int32_t) pil[1] * rxF[1]) >> 15); - ch[1] += (int16_t) (((int32_t) pil[0] * rxF[1] + (int32_t) pil[1] * rxF[0]) >> 15); + ch[0] += (int16_t) (((int32_t) pil[0] * rxF[0] - (int32_t) pil[1] * rxF[1]) >> (15+b_shift)); + ch[1] += (int16_t) (((int32_t) pil[0] * rxF[1] + (int32_t) pil[1] * rxF[0]) >> (15+b_shift)); pil += 2; } } From 523f8f2139797682971a94a6a1df44bde0e6b4f0 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Fri, 29 Apr 2022 18:19:49 +0100 Subject: [PATCH 183/233] Put pilot_cnt++ in the right place --- openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index e180be6604..720a1e9b50 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -252,7 +252,6 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, // Channel interpolation for (int k_line = 0; k_line <= 1; k_line++) { - pilot_cnt++; #ifdef DEBUG_PUSCH re_offset = (k0 + (n << 2) + (k_line << 1)) % gNB->frame_parms.ofdm_symbol_size; rxF = (int16_t *) &rxdataF[aarx][(soffset + symbol_offset + re_offset)]; @@ -275,6 +274,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, } else { multadd_real_vector_complex_scalar(fm, ch, ul_ch, 8); } + pilot_cnt++; } } From 1694e1b061867837e950c19ae70c54944cc8326d Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Fri, 29 Apr 2022 19:08:41 +0100 Subject: [PATCH 184/233] Fix b_shift --- openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 720a1e9b50..038f1473f9 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -206,7 +206,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, } #endif - uint8_t b_shift = pusch_pdu->nrOfLayers < 4 ? 1 : log2_approx(pusch_pdu->nrOfLayers>>1); + uint8_t b_shift = pusch_pdu->nrOfLayers == 1; for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { From 0e2a00655e79b48743b361e7124a2e6ea738d84f Mon Sep 17 00:00:00 2001 From: rmagueta Date: Sat, 30 Apr 2022 17:07:14 +0100 Subject: [PATCH 185/233] Fix build after merge --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 205fb90017..b3aab64bd8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1648,8 +1648,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->R = 0; ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->F = 1; ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->LCID = DL_SCH_LCID_CCCH; - ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->L1 = (mac_sdu_length >> 8) & 0xff; - ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->L2 = mac_sdu_length & 0xff; + ((NR_MAC_SUBHEADER_LONG *) &buf[mac_pdu_length])->L = htons(mac_sdu_length); ra->mac_pdu_length = mac_pdu_length + mac_sdu_length + sizeof(NR_MAC_SUBHEADER_LONG); } LOG_I(NR_MAC,"Encoded RRCSetup Piggyback (%d + %d bytes), mac_pdu_length %d\n", mac_sdu_length, mac_subheader_len, ra->mac_pdu_length); From 4f2854f92cd4c834592b21f9248ad5ae37d9753b Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Mon, 2 May 2022 01:55:03 +0100 Subject: [PATCH 186/233] Fix UL channel estimation for 4 layers --- cmake_targets/autotests/test_case_list.xml | 2 +- .../PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml index f372e7230a..14daa644df 100755 --- a/cmake_targets/autotests/test_case_list.xml +++ b/cmake_targets/autotests/test_case_list.xml @@ -1331,7 +1331,7 @@ -n100 -s5 -Z -U 2 0 2 -n100 -m19 -s10 -S15 -z2 -n100 -m9 -r106 -s10 -W2 -y2 -z2 - -n100 -m9 -r106 -s13 -W4 -y4 -z4 + -n100 -m9 -r106 -s20 -W4 -y4 -z4 nr_ulsim.test1 nr_ulsim.test2 nr_ulsim.test3 nr_ulsim.test4 nr_ulsim.test5 nr_ulsim.test6 nr_ulsim.test7 nr_ulsim.test8 nr_ulsim.test9 nr_ulsim.test10 nr_ulsim.test11 nr_ulsim.test12 nr_ulsim.test13 nr_ulsim.test14 nr_ulsim.test15 nr_ulsim.test16 nr_ulsim.test17 PUSCH test OK diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 038f1473f9..20575b45d1 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -233,21 +233,19 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, // where k' is 0 or 1, and delta is in Table 6.4.1.1.3-1 from TS 38.211 pilot_cnt = 0; + int delta = nr_pusch_dmrs_delta(pusch_dmrs_type1, p); for (int n = 0; n < 3*nb_rb_pusch; n++) { // LS estimation ch[0] = 0; ch[1] = 0; - for (int p2 = 0; p2nrOfLayers; p2+=2) { - int delta2 = nr_pusch_dmrs_delta(pusch_dmrs_type1, p2); - for (int k_line = 0; k_line <= 1; k_line++) { - re_offset = (k0 + (n << 2) + (k_line << 1) + delta2) % gNB->frame_parms.ofdm_symbol_size; - rxF = (int16_t *) &rxdataF[aarx][(soffset + symbol_offset + re_offset)]; - ch[0] += (int16_t) (((int32_t) pil[0] * rxF[0] - (int32_t) pil[1] * rxF[1]) >> (15+b_shift)); - ch[1] += (int16_t) (((int32_t) pil[0] * rxF[1] + (int32_t) pil[1] * rxF[0]) >> (15+b_shift)); - pil += 2; - } + for (int k_line = 0; k_line <= 1; k_line++) { + re_offset = (k0 + (n << 2) + (k_line << 1) + delta) % gNB->frame_parms.ofdm_symbol_size; + rxF = (int16_t *) &rxdataF[aarx][(soffset + symbol_offset + re_offset)]; + ch[0] += (int16_t) (((int32_t) pil[0] * rxF[0] - (int32_t) pil[1] * rxF[1]) >> (15+b_shift)); + ch[1] += (int16_t) (((int32_t) pil[0] * rxF[1] + (int32_t) pil[1] * rxF[0]) >> (15+b_shift)); + pil += 2; } // Channel interpolation From 6b6528cc944be640b19ef0c83fa0522e9ff48f10 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 2 May 2022 10:41:42 +0200 Subject: [PATCH 187/233] revert H_awgn_mimo in simulator --- targets/ARCH/rfsimulator/simulator.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/targets/ARCH/rfsimulator/simulator.c b/targets/ARCH/rfsimulator/simulator.c index 38902984b6..127b2975d5 100644 --- a/targets/ARCH/rfsimulator/simulator.c +++ b/targets/ARCH/rfsimulator/simulator.c @@ -705,17 +705,10 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest } else { // no channel modeling - #if 0 double H_awgn_mimo[4][4] ={{1.0, 0.2, 0.1, 0.05}, //rx 0 {0.2, 1.0, 0.2, 0.1}, //rx 1 {0.1, 0.2, 1.0, 0.2}, //rx 2 {0.05, 0.1, 0.2, 1.0}};//rx 3 - #else - double H_awgn_mimo[4][4] ={{1.0, 0.0, 0.0, 0.0},//rx 0 - {0.0, 1.0, 0.0, 0.0},//rx 1 - {0.0, 0.0, 1.0, 0.0},//rx 2 - {0.0, 0.0, 0.0, 1.0}};//rx 3 - #endif sample_t *out=(sample_t *)samplesVoid[a]; int nbAnt_tx = ptr->th.nbAnt;//number of Tx antennas From 5de85671d82fac4aefc666ed9c0c8232f112e228 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Mon, 2 May 2022 16:58:08 +0100 Subject: [PATCH 188/233] Fix heap buffer overflow --- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index c22fb011f1..b11423b083 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -807,21 +807,17 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, int avg_rho_re[frame_parms->nb_antennas_rx][nrOfLayers*nrOfLayers]; int avg_rho_im[frame_parms->nb_antennas_rx][nrOfLayers*nrOfLayers]; - for (aarx=0; aarxnb_antennas_rx; aarx++) - { - for (aatx=0; aatx < nrOfLayers; aatx++) - { - ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - - for (int atx=0; atx< nrOfLayers; atx++) - { + for (aarx=0; aarx < frame_parms->nb_antennas_rx; aarx++) { + for (aatx=0; aatx < nrOfLayers; aatx++) { + for (int atx=0; atx< nrOfLayers; atx++) { + avg_rho_re[aarx][aatx*nrOfLayers+atx] = 0; avg_rho_im[aarx][aatx*nrOfLayers+atx] = 0; rho128 = (__m128i *)&rho[aarx][aatx*nrOfLayers+atx][symbol*(off+(nb_rb*12))]; + ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; ul_ch128_2 = (__m128i *)&ul_ch_estimates_ext[atx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - for (rb=0; rb Date: Mon, 2 May 2022 19:07:07 +0100 Subject: [PATCH 189/233] Fix logs in help of ulsim --- openair1/SIMULATION/NR_PHY/ulsim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index b64b4c22c5..e537277f46 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -605,8 +605,8 @@ int main(int argc, char **argv) printf("-u Set the numerology\n"); printf("-w Start PRB for PUSCH\n"); //printf("-x Transmission mode (1,2,6 for the moment)\n"); - printf("-y Number of TX antennas used in eNB\n"); - printf("-z Number of RX antennas used in UE\n"); + printf("-y Number of TX antennas used at UE\n"); + printf("-z Number of RX antennas used at gNB\n"); printf("-A Interpolation_filname Run with Abstraction to generate Scatter plot using interpolation polynomial in file\n"); //printf("-C Generate Calibration information for Abstraction (effective SNR adjustment to remove Pe bias w.r.t. AWGN)\n"); printf("-F Input filename (.txt format) for RX conformance testing\n"); From b6a018089b8be24a793a620a0e4b7ff212edc3ae Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Mon, 2 May 2022 23:53:13 +0100 Subject: [PATCH 190/233] Fix in nr_ulsch_extract_rbs --- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index b11423b083..6975eafee1 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -308,31 +308,26 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, unsigned char symbol, uint8_t is_dmrs_symbol, nfapi_nr_pusch_pdu_t *pusch_pdu, - NR_DL_FRAME_PARMS *frame_parms) -{ + NR_DL_FRAME_PARMS *frame_parms) { unsigned short start_re, re, nb_re_pusch; unsigned char aarx, aatx; uint32_t rxF_ext_index = 0; uint32_t ul_ch0_ext_index = 0; uint32_t ul_ch0_index = 0; - //uint8_t k_prime; - //uint16_t n; int16_t *rxF,*rxF_ext; int *ul_ch0,*ul_ch0_ext; - //uint8_t delta = 0; int soffset = (slot&3)*frame_parms->symbols_per_slot*frame_parms->ofdm_symbol_size; #ifdef DEBUG_RB_EXT - printf("--------------------symbol = %d-----------------------\n", symbol); printf("--------------------ch_ext_index = %d-----------------------\n", symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size); - #endif uint8_t is_data_re; start_re = (frame_parms->first_carrier_offset + (pusch_pdu->rb_start + pusch_pdu->bwp_start) * NR_NB_SC_PER_RB)%frame_parms->ofdm_symbol_size; nb_re_pusch = NR_NB_SC_PER_RB * pusch_pdu->rb_size; + #ifdef __AVX2__ int nb_re_pusch2 = nb_re_pusch + (nb_re_pusch&7); #else @@ -341,18 +336,13 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol * frame_parms->ofdm_symbol_size)]; - rxF_ext = (int16_t *)&pusch_vars->rxdataF_ext[aarx][symbol * nb_re_pusch2]; // [hna] rxdataF_ext isn't contiguous in order to solve an alignment problem ib llr computation in case of mod_order = 4, 6 + rxF = (int16_t *)&rxdataF[aarx][soffset+(symbol * frame_parms->ofdm_symbol_size)]; + rxF_ext = (int16_t *)&pusch_vars->rxdataF_ext[aarx][symbol * nb_re_pusch2]; // [hna] rxdataF_ext isn't contiguous in order to solve an alignment problem ib llr computation in case of mod_order = 4, 6 if (is_dmrs_symbol == 0) { - // - //rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); if (start_re + nb_re_pusch <= frame_parms->ofdm_symbol_size) { - memcpy1((void*)rxF_ext, - (void*)&rxF[start_re*2], - nb_re_pusch*sizeof(int32_t)); - } - else { + memcpy1((void*)rxF_ext, (void*)&rxF[start_re*2], nb_re_pusch*sizeof(int32_t)); + } else { int neg_length = frame_parms->ofdm_symbol_size-start_re; int pos_length = nb_re_pusch-neg_length; memcpy1((void*)rxF_ext,(void*)&rxF[start_re*2],neg_length*sizeof(int32_t)); @@ -360,28 +350,23 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, } for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) { - ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available + ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; memcpy1((void*)ul_ch0_ext,(void*)ul_ch0,nb_re_pusch*sizeof(int32_t)); } - } - else { + + } else { for (aatx = 0; aatx < pusch_pdu->nrOfLayers; aatx++) { - ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available + ul_ch0 = &pusch_vars->ul_ch_estimates[aatx*frame_parms->nb_antennas_rx+aarx][pusch_vars->dmrs_symbol*frame_parms->ofdm_symbol_size]; // update channel estimates if new dmrs symbol are available ul_ch0_ext = &pusch_vars->ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*nb_re_pusch2]; - //n = 0; - //k_prime = 0; rxF_ext_index = 0; ul_ch0_ext_index = 0; - ul_ch0_index = 0; - + ul_ch0_index = 0; for (re = 0; re < nb_re_pusch; re++) { - uint16_t k= start_re+re; - + uint16_t k = start_re + re; is_data_re = allowed_xlsch_re_in_dmrs_symbol(k, start_re, frame_parms->ofdm_symbol_size, pusch_pdu->num_dmrs_cdm_grps_no_data, pusch_pdu->dmrs_config_type); - if (++k >= frame_parms->ofdm_symbol_size) { k -= frame_parms->ofdm_symbol_size; } @@ -390,9 +375,9 @@ void nr_ulsch_extract_rbs(int32_t **rxdataF, printf("re = %d, is_dmrs_symbol = %d, symbol = %d\n", re, is_dmrs_symbol, symbol); #endif - /* save only data and respective channel estimates */ + // save only data and respective channel estimates if (is_data_re == 1) { - if (aatx == aarx) { + if (aatx == 0) { rxF_ext[rxF_ext_index] = (rxF[ ((start_re + re)*2) % (frame_parms->ofdm_symbol_size*2)]); rxF_ext[rxF_ext_index + 1] = (rxF[(((start_re + re)*2) + 1) % (frame_parms->ofdm_symbol_size*2)]); rxF_ext_index +=2; From e3fd9f7f8cdf907ef14a758e7cddb1e091160e5d Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Tue, 3 May 2022 00:29:44 +0100 Subject: [PATCH 191/233] To use the channel generated by multipath_channel() for single-layer case in ulsim --- openair1/SIMULATION/NR_PHY/ulsim.c | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index e537277f46..fc4f916242 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1251,34 +1251,36 @@ int main(int argc, char **argv) } } - - if (UE2gNB->max_Doppler == 0) - multipath_channel(UE2gNB, s_re, s_im, r_re, r_im, slot_length, 0, (n_trials==1)?1:0); - else - multipath_tv_channel(UE2gNB, s_re, s_im, r_re, r_im, 2*slot_length, 0); - - for (i=0; inb_antennas_rx; ap++) { - if (channel_model == AWGN) { - - double H_awgn_mimo[4][4] ={{1.0, 0.2, 0.1, 0.05}, //rx 0 - {0.2, 1.0, 0.2, 0.1}, //rx 1 - {0.1, 0.2, 1.0, 0.2}, //rx 2 - {0.05, 0.1, 0.2, 1.0}}; //rx 3 - + // The multipath_channel() function calculates a channel matrix with only 1's. So the channel rank is 1, and we + // cannot use multi-layer. To solve this issue, for now we use the H_awgn_mimo matrix for multi-layer. + if (precod_nbr_layers == 1) { + if (UE2gNB->max_Doppler == 0) { + multipath_channel(UE2gNB, s_re, s_im, r_re, r_im, slot_length, 0, (n_trials==1)?1:0); + } else { + multipath_tv_channel(UE2gNB, s_re, s_im, r_re, r_im, 2*slot_length, 0); + } + } else { + double H_awgn_mimo[4][4] ={{1.0, 0.2, 0.1, 0.05}, //rx 0 + {0.2, 1.0, 0.2, 0.1}, //rx 1 + {0.1, 0.2, 1.0, 0.2}, //rx 2 + {0.05, 0.1, 0.2, 1.0}}; //rx 3 + for (i=0; inb_antennas_rx; ap++) { // sum up signals from different Tx antennas r_re[ap][i] = 0; r_im[ap][i] = 0; - for (int aa=0; aanb_antennas_rx; ap++) { ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i) + (delay*2)] = (int16_t)((r_re[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); // convert to fixed point ((int16_t*) &gNB->common_vars.rxdata[ap][slot_offset])[(2*i)+1 + (delay*2)] = (int16_t)((r_im[ap][i]) + (sqrt(sigma/2)*gaussdouble(0.0,1.0))); - /* Add phase noise if enabled */ if (pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { phase_noise(ts, &((int16_t*)&gNB->common_vars.rxdata[ap][slot_offset])[(2*i)], From 2162ba6d82b365afc617f74c82f30ca6cde45323 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Tue, 3 May 2022 10:03:56 +0100 Subject: [PATCH 192/233] Fix sigma in ulsim --- openair1/SIMULATION/NR_PHY/ulsim.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index fc4f916242..c22ff82be6 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1237,7 +1237,9 @@ int main(int argc, char **argv) n_trials = 1; if (input_fd == NULL) { - sigma_dB = 10 * log10((double)txlev_sum * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; + // Justification of division by precod_nbr_layers: + // When the channel is the identity matrix, the results in terms of SNR should be almost equal for 2x2 and 4x4. + sigma_dB = 10 * log10((double)txlev_sum/precod_nbr_layers * ((double)frame_parms->ofdm_symbol_size/(12*nb_rb))) - SNR;; sigma = pow(10,sigma_dB/10); From cfa37e721329932bdcb864ab940f3871342b35f6 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 3 May 2022 12:21:47 +0200 Subject: [PATCH 193/233] cosmetic changes --- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 75 +++++++------------ openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 6 +- openair1/SIMULATION/NR_PHY/ulschsim.c | 2 - openair1/SIMULATION/NR_PHY/ulsim.c | 6 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 16 ++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 2 - .../NR_MAC_gNB/gNB_scheduler_primitives.c | 4 - 7 files changed, 35 insertions(+), 76 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 6975eafee1..5066316e62 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -478,17 +478,14 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, int off = 0; #endif - for (aatx = 0; aatx < nrOfLayers; aatx++) - { - for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) - { + for (aatx = 0; aatx < nrOfLayers; aatx++) { + for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { //clear average level avg128U = _mm_setzero_si128(); ul_ch128=(__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - for (rb = 0; rb < nb_rb_0; rb++) - { + for (rb = 0; rb < nb_rb_0; rb++) { avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[0], ul_ch128[0]), x)); avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[1], ul_ch128[1]), x)); avg128U = _mm_add_epi32(avg128U, _mm_srai_epi32(_mm_madd_epi16(ul_ch128[2], ul_ch128[2]), x)); @@ -499,10 +496,9 @@ void nr_ulsch_channel_level(int **ul_ch_estimates_ext, ((int32_t*)&avg128U)[1] + ((int32_t*)&avg128U)[2] + ((int32_t*)&avg128U)[3]) / y; - } } - + _mm_empty(); _m_empty(); @@ -637,23 +633,19 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, QAM_amp128b = _mm_setzero_si128(); uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); - for (aatx=0; aatxnb_antennas_rx; aarx++) - { + for (aarx=0; aarxnb_antennas_rx; aarx++) { ul_ch128 = (__m128i *)&ul_ch_estimates_ext[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; ul_ch_mag128 = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; ul_ch_mag128b = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; @@ -661,10 +653,8 @@ void nr_ulsch_channel_compensation(int **rxdataF_ext, rxdataF_comp128 = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aarx][symbol*(off+(nb_rb*12))]; - for (rb=0; rb2) - { + for (rb=0; rb2) { // get channel amplitude if not QPSK //print_shorts("ch:",(int16_t*)&ul_ch128[0]); @@ -1164,8 +1154,7 @@ void nr_ulsch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, int off = 0; #endif - if (n_rx > 1) - { + if (n_rx > 1) { #if defined(__x86_64__) || defined(__i386__) for (int aatx=0; aatxnb_antennas_rx][(symbol*(nb_re + off))]; ul_ch_mag128b[0] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx][(symbol*(nb_re + off))]; - for (int aa=1;aa < n_rx;aa++) - { + for (int aa=1;aa < n_rx;aa++) { rxdataF_comp128[1] = (__m128i *)&rxdataF_comp[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; ul_ch_mag128[1] = (__m128i *)&ul_ch_mag[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; ul_ch_mag128b[1] = (__m128i *)&ul_ch_magb[aatx*frame_parms->nb_antennas_rx+aa][(symbol*(nb_re + off))]; // MRC on each re of rb, both on MF output and magnitude (for 16QAM/64QAM llr computation) - for (i=0; iulsch_channel_estimation_stats); - for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) - { + for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; LOG_D(PHY, "symbol %d, dmrs_symbol_flag :%d\n", symbol, dmrs_symbol_flag); - if (dmrs_symbol_flag == 1) - { + if (dmrs_symbol_flag == 1) { if (gNB->pusch_vars[ulsch_id]->dmrs_symbol == INVALID_VALUE) gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; - for (int nl=0; nlnrOfLayers; nl++) - { + for (int nl=0; nlnrOfLayers; nl++) { nr_pusch_channel_estimation(gNB, slot, @@ -1984,29 +1968,24 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, #endif uint32_t rxdataF_ext_offset = 0; - for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) - { + for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; - if (dmrs_symbol_flag == 1) - { + if (dmrs_symbol_flag == 1) { if ((rel15_ul->ul_dmrs_symb_pos >> ((symbol + 1) % frame_parms->symbols_per_slot)) & 0x01) AssertFatal(1==0,"Double DMRS configuration is not yet supported\n"); gNB->pusch_vars[ulsch_id]->dmrs_symbol = symbol; - if (rel15_ul->dmrs_config_type == 0) - { + if (rel15_ul->dmrs_config_type == 0) { // if no data in dmrs cdm group is 1 only even REs have no data // if no data in dmrs cdm group is 2 both odd and even REs have no data nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*6)); } - else - { + else { nb_re_pusch = rel15_ul->rb_size *(12 - (rel15_ul->num_dmrs_cdm_grps_no_data*4)); } } - else - { + else { nb_re_pusch = rel15_ul->rb_size * NR_NB_SC_PER_RB; } @@ -2016,8 +1995,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, //---------------------------------------------------------- //--------------------- RBs extraction --------------------- //---------------------------------------------------------- - if (nb_re_pusch > 0) - { + if (nb_re_pusch > 0) { start_meas(&gNB->ulsch_rbs_extraction_stats); nr_ulsch_extract_rbs(gNB->common_vars.rxdataF, gNB->pusch_vars[ulsch_id], @@ -2104,8 +2082,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, nb_re_pusch); stop_meas(&gNB->ulsch_mrc_stats); - if (rel15_ul->transformPrecoder == transformPrecoder_enabled) - { + if (rel15_ul->transformPrecoder == transformPrecoder_enabled) { #ifdef __AVX2__ // For odd number of resource blocks need byte alignment to multiple of 8 int nb_re_pusch2 = nb_re_pusch + (nb_re_pusch&7); @@ -2123,8 +2100,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, //---------------------------------------------------------- /* In case PTRS is enabled then LLR will be calculated after PTRS symbols are processed * * otherwise LLR are calculated for each symbol based upon DMRS channel estimates only. */ - if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) - { + if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { start_meas(&gNB->ulsch_ptrs_processing_stats); nr_pusch_ptrs_processing(gNB, frame_parms, @@ -2143,8 +2119,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, /*-------------------- LLRs computation -------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------------------------*/ start_meas(&gNB->ulsch_llr_stats); - for (aatx=0; aatx < rel15_ul->nrOfLayers; aatx++) - { + for (aatx=0; aatx < rel15_ul->nrOfLayers; aatx++) { nr_ulsch_compute_llr(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[aatx*frame_parms->nb_antennas_rx][symbol * (off + rel15_ul->rb_size * NR_NB_SC_PER_RB)], gNB->pusch_vars[ulsch_id]->ul_ch_mag0[aatx*frame_parms->nb_antennas_rx], gNB->pusch_vars[ulsch_id]->ul_ch_magb0[aatx*frame_parms->nb_antennas_rx], diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 114d1a424b..8243d8b0c9 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -283,12 +283,10 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, AssertFatal(ue->CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX,VCD_FUNCTION_IN); - + for(int i=0; i< ue->frame_parms.nb_antennas_tx; ++i) - { memset(ue->common_vars.txdataF[i], 0, sizeof(int)*14*ue->frame_parms.ofdm_symbol_size); - } - + LOG_D(PHY,"****** start TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx); start_meas(&ue->phy_proc_tx); diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index d76d438446..1eaeb6139b 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -444,9 +444,7 @@ int main(int argc, char **argv) NR_UE_ULSCH_t *ulsch_ue = UE->ulsch[0][0]; if ((Nl==4)||(Nl==3)) - { nb_re_dmrs = nb_re_dmrs*2; - } mod_order = nr_get_Qm_ul(Imcs, 0); code_rate = nr_get_code_rate_ul(Imcs, 0); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index c22ff82be6..4d271b4d1d 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1359,8 +1359,7 @@ int main(int argc, char **argv) LOG_M("rxsigF0_llrlayers0.m","rxsF0_llrlayers0", &gNB->pusch_vars[0]->llr_layers[0][0],(nb_symb_sch-1)*NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,1,0); - if (precod_nbr_layers==2) - { + if (precod_nbr_layers==2) { LOG_M("rxsigF1_ext.m","rxsF1_ext", &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); @@ -1379,8 +1378,7 @@ int main(int argc, char **argv) } - if (precod_nbr_layers==4) - { + if (precod_nbr_layers==4) { LOG_M("rxsigF1_ext.m","rxsF1_ext", &gNB->pusch_vars[0]->rxdataF_ext[1][start_symbol*NR_NB_SC_PER_RB * pusch_pdu->rb_size],nb_symb_sch*(off+(NR_NB_SC_PER_RB * pusch_pdu->rb_size)),1,1); LOG_M("rxsigF2_ext.m","rxsF2_ext", diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index c70044ddeb..3201b51d30 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -231,8 +231,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con if (n_antenna_port == 1); // 1 antenna port and the higher layer parameter txConfig = codebook 0 bits - if (n_antenna_port == 4) - { // 4 antenna port and the higher layer parameter txConfig = codebook + if (n_antenna_port == 4) { // 4 antenna port and the higher layer parameter txConfig = codebook // Table 7.3.1.1.2-2: transformPrecoder=disabled and maxRank = 2 or 3 or 4 if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) @@ -259,8 +258,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con // Table 7.3.1.1.2-3: transformPrecoder= enabled, or transformPrecoder=disabled and maxRank = 1 if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) - && (*pusch_Config->maxRank == 1)) - { + && (*pusch_Config->maxRank == 1)) { if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][6]; @@ -279,11 +277,10 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con } } - if (n_antenna_port == 2) - { // 2 antenna port and the higher layer parameter txConfig = codebook + if (n_antenna_port == 2) { + // 2 antenna port and the higher layer parameter txConfig = codebook // Table 7.3.1.1.2-4: transformPrecoder=disabled and maxRank = 2 - if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)) - { + if ((transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) && (*pusch_Config->maxRank == 2)) { if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][12]; pusch_config_pdu->Tpmi = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13]; @@ -299,8 +296,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con // Table 7.3.1.1.2-5: transformPrecoder= enabled, or transformPrecoder= disabled and maxRank = 1 if (((transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) || (transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled)) - && (*pusch_Config->maxRank == 1)) - { + && (*pusch_Config->maxRank == 1)) { if (*pusch_Config->codebookSubset == NR_PUSCH_Config__codebookSubset_fullyAndPartialAndNonCoherent) { pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][16]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 55bcec0de6..9df3a5e397 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -459,9 +459,7 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0; uint8_t num_dmrs_cdm_grps_no_data = 1; if ((target_ul_Nl==4)||(target_ul_Nl==3)) - { num_dmrs_cdm_grps_no_data = 2; - } /* we want to avoid a lengthy deduction of DMRS and other parameters in * every TTI if we can save it, so check whether dci_format, TDA, or diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index c40caa7316..d426bc69c7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1011,13 +1011,9 @@ void config_uldci(const NR_SIB1_t *sib1, } dci_pdu_rel15->precoding_information.val= 0; if (pusch_pdu->nrOfLayers == 2) - { dci_pdu_rel15->precoding_information.val = 4; - } else if (pusch_pdu->nrOfLayers == 4) - { dci_pdu_rel15->precoding_information.val = 11; - } // antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4 // Antenna Ports From 1e38cead3977fd34133d009883e16e7df0471253 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 6 Apr 2022 23:33:09 +0100 Subject: [PATCH 194/233] Increase MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER to allow gNB to work with 273PRBs and 256QAM at MCS 27 --- openair1/PHY/defs_nr_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index b24abd7de3..40c49cee17 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -85,7 +85,7 @@ #define NR_MAX_PDSCH_TBS 3824 #define NR_MAX_SIB_LENGTH 2976 // 3GPP TS 38.331 section 5.2.1 - The physical layer imposes a limit to the maximum size a SIB can take. The maximum SIB1 or SI message size is 2976 bits. -#define MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER 34 +#define MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER 36 #define MAX_NUM_NR_ULSCH_SEGMENTS 34 #define MAX_NR_ULSCH_PAYLOAD_BYTES (MAX_NUM_NR_ULSCH_SEGMENTS*1056) From 8641d54f4d91a927b03e0ae38d47b86e10cdf567 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 3 May 2022 15:56:18 +0100 Subject: [PATCH 195/233] Fix build after merge --- openair1/SIMULATION/NR_PHY/dlsim.c | 4 +- openair1/SIMULATION/NR_PHY/ulsim.c | 4 +- openair2/LAYER2/NR_MAC_gNB/config.c | 18 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 14 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 3 +- openair2/RRC/NR/rrc_gNB.c | 203 +++++------------- openair2/RRC/NR/rrc_gNB_nsa.c | 6 +- 7 files changed, 70 insertions(+), 182 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 57c702a409..3d510529ee 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -827,9 +827,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 0, 0, NULL, 0); + rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, 0); + rrc_mac_config_req_gNB(0,0, pdsch_AntennaPorts, n_tx, 0, 6, scc, NULL, NULL, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup); // reset preprocessor to the one of DLSIM after it has been set during // rrc_mac_config_req_gNB gNB_mac->pre_processor_dl = nr_dlsim_preprocessor; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index e4c693d602..866fe8d486 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -751,9 +751,9 @@ int main(int argc, char **argv) gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; // common configuration - rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 0, 0, NULL, 0); + rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 0, 0, NULL); // UE dedicated configuration - rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup, 0); + rrc_mac_config_req_gNB(0,0, conf.pdsch_AntennaPorts, n_rx, 0, 6, scc, &rrc.carrier.mib, rrc.carrier.siblock1, 1, secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity, secondaryCellGroup); frame_parms->nb_antennas_tx = n_tx; frame_parms->nb_antennas_rx = n_rx; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 892237f3e0..9f7493e581 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -481,23 +481,7 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_BCCH_DL_SCH_Message_t *sib1, int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup, - uint32_t rrc_reconfiguration_delay) { - - if (scc && rrc_reconfiguration_delay > 0) { - const int UE_id = find_nr_UE_id(Mod_idP,rnti); - if (UE_id >= 0) { - NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info; - NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - if (sched_ctrl->rrc_processing_timer == 0) { - const uint16_t sf_ahead = (uint16_t) ceil((float)6/(0x01<<(*scc->ssbSubcarrierSpacing))); - const uint16_t sl_ahead = sf_ahead * (0x01<<(*scc->ssbSubcarrierSpacing)); - sched_ctrl->rrc_processing_timer = (rrc_reconfiguration_delay << (*scc->ssbSubcarrierSpacing)) + sl_ahead; - LOG_I(NR_MAC, "Activating RRC processing timer for UE %d\n", UE_id); - } - } - return 0; - } + NR_CellGroupConfig_t *CellGroup) { if (scc != NULL ) { AssertFatal((scc->ssb_PositionsInBurst->present > 0) && (scc->ssb_PositionsInBurst->present < 4), "SSB Bitmap type %d is not valid\n",scc->ssb_PositionsInBurst->present); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 0a5e7bc2dd..d15a4af9fd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2847,13 +2847,16 @@ void nr_mac_update_timers(module_id_t module_id, NR_BWP_DownlinkDedicated_t *bwpd = spCellConfigDedicated ? spCellConfigDedicated->initialDownlinkBWP : NULL; const uint8_t layers = set_dl_nrOfLayers(sched_ctrl); - const int tda = RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0][slot]; + const int tda = sched_ctrl->active_bwp && RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] >= 0 ? + RC.nrmac[module_id]->preferred_dl_tda[sched_ctrl->active_bwp->bwp_Id][slot] : + (sched_ctrl->pdsch_semi_static.time_domain_allocation >= 0 ? sched_ctrl->pdsch_semi_static.time_domain_allocation : 0); + nr_set_pdsch_semi_static(sib1, scc, UE_info->CellGroup[UE_id], sched_ctrl->active_bwp, bwpd, - tda >= 0 ? tda : sched_ctrl->pdsch_semi_static.time_domain_allocation, + tda, layers, sched_ctrl, &sched_ctrl->pdsch_semi_static); @@ -2864,13 +2867,16 @@ void nr_mac_update_timers(module_id_t module_id, const uint8_t num_dmrs_cdm_grps_no_data = (sched_ctrl->active_ubwp || ubwpd) ? 1 : 2; int dci_format = get_dci_format(sched_ctrl); - const int utda = sched_ctrl->active_ubwp ? RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : 0; + const int utda = sched_ctrl->active_ubwp && RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] >= 0 ? + RC.nrmac[module_id]->preferred_ul_tda[sched_ctrl->active_ubwp->bwp_Id][slot] : + (sched_ctrl->pusch_semi_static.time_domain_allocation >= 0 ? sched_ctrl->pusch_semi_static.time_domain_allocation : 0); + nr_set_pusch_semi_static(sib1, scc, sched_ctrl->active_ubwp, ubwpd, dci_format, - utda >= 0 ? utda : sched_ctrl->pusch_semi_static.time_domain_allocation, + utda, num_dmrs_cdm_grps_no_data, &sched_ctrl->pusch_semi_static); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 4b46cb9a49..d400942680 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -62,8 +62,7 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP, NR_BCCH_DL_SCH_Message_t *sib1, int add_ue, uint32_t rnti, - NR_CellGroupConfig_t *CellGroup, - uint32_t rrc_reconfiguration_delay); + NR_CellGroupConfig_t *CellGroup); void clear_nr_nfapi_information(gNB_MAC_INST * gNB, int CC_idP, diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index c44b48ff83..1c480d2616 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -176,8 +176,7 @@ static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration rrc->carrier.siblock1, 0, 0, // WIP hardcoded rnti - NULL, - 0); + NULL); } /* set flag to indicate that cell information is configured. This is required @@ -287,8 +286,7 @@ void apply_macrlc_config(gNB_RRC_INST *rrc, NULL, 0, ue_context_pP->ue_context.rnti, - cgc, - 0); + cgc); nr_rrc_rlc_config_asn1_req(ctxt_pP, ue_context_pP->ue_context.SRB_configList, @@ -484,8 +482,7 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( rrc_instance_p->carrier.siblock1, 0, ue_context_pP->ue_context.rnti, - NULL, - 0); + NULL); LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel DL-CCCH, Generating RRCSetup (bytes %d)\n", @@ -741,29 +738,6 @@ rrc_gNB_generate_defaultRRCReconfiguration( LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); - if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { - - uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && - ue_context_pP->ue_context.masterCellGroup->spCellConfig && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? - NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; - - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - rrc->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - rrc->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - delay_ms); - } - /* Free all NAS PDUs */ for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { @@ -834,10 +808,16 @@ rrc_gNB_generate_defaultRRCReconfiguration( } if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; + nr_mac_enable_ue_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP->ue_context.rnti, *rrc->carrier.servingcellconfigcommon->ssbSubcarrierSpacing, - NR_RRC_RECONFIGURATION_DELAY_MS); + delay_ms); } } @@ -1037,29 +1017,6 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( cellGroupConfig); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); - if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { - - uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && - ue_context_pP->ue_context.masterCellGroup->spCellConfig && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? - NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; - - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - rrc->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - rrc->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - delay_ms); - } - /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { @@ -1098,10 +1055,16 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( #endif if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; + nr_mac_enable_ue_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP->ue_context.rnti, *rrc->carrier.servingcellconfigcommon->ssbSubcarrierSpacing, - NR_RRC_RECONFIGURATION_DELAY_MS); + delay_ms); } } @@ -1240,29 +1203,6 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( NULL); LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); - if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { - - uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && - ue_context_pP->ue_context.masterCellGroup->spCellConfig && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? - NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; - - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - delay_ms); - } - /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_modify_pdusessions; i++) { if (ue_context_pP->ue_context.modify_pdusession[i].param.nas_pdu.buffer != NULL) { @@ -1301,10 +1241,16 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( #endif if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; + nr_mac_enable_ue_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP->ue_context.rnti, *RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->ssbSubcarrierSpacing, - NR_RRC_RECONFIGURATION_DELAY_MS); + delay_ms); } } @@ -1374,29 +1320,6 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); - if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { - - uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && - ue_context_pP->ue_context.masterCellGroup->spCellConfig && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? - NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; - - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - delay_ms); - } - /* Free all NAS PDUs */ if (nas_length > 0) { /* Free the NAS PDU buffer and invalidate it */ @@ -1431,10 +1354,16 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( #endif if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; + nr_mac_enable_ue_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP->ue_context.rnti, *RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->ssbSubcarrierSpacing, - NR_RRC_RECONFIGURATION_DELAY_MS); + delay_ms); } } @@ -1511,8 +1440,7 @@ rrc_gNB_process_RRCReconfigurationComplete( NULL, 0, ue_context_pP->ue_context.rnti, - ue_context_pP->ue_context.masterCellGroup, - 0); + ue_context_pP->ue_context.masterCellGroup); LOG_D(NR_RRC,"Configuring RLC DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); nr_rrc_rlc_config_asn1_req(ctxt_pP, SRB_configList, // NULL, @@ -1955,29 +1883,6 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); - if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { - - uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && - ue_context_pP->ue_context.masterCellGroup->spCellConfig && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? - NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; - - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - delay_ms); - } - /* Free all NAS PDUs */ for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { @@ -2020,10 +1925,16 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( } if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; + nr_mac_enable_ue_rrc_processing_timer(ctxt_pP->module_id, ue_context_pP->ue_context.rnti, *RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->ssbSubcarrierSpacing, - NR_RRC_RECONFIGURATION_DELAY_MS); + delay_ms); } } //----------------------------------------------------------------------------- @@ -2058,29 +1969,6 @@ int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP NULL, masterCellGroup); - if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { - - uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && - ue_context_pP->ue_context.masterCellGroup->spCellConfig && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && - ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? - NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; - - rrc_mac_config_req_gNB(ctxt_pP->module_id, - 0, - RC.nrrrc[ctxt_pP->module_id]->configuration.pdsch_AntennaPorts, - 0, - 0, - 0, - RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon, - NULL, - NULL, - 0, - ue_context_pP->ue_context.rnti, - NULL, - delay_ms); - } - nr_rrc_data_req(ctxt_pP, DCCH, rrc_gNB_mui++, @@ -2089,6 +1977,19 @@ int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP buffer, PDCP_TRANSMISSION_MODE_CONTROL); + if (NODE_IS_DU(RC.nrrrc[ctxt_pP->module_id]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[ctxt_pP->module_id]->node_type)) { + uint32_t delay_ms = ue_context_pP->ue_context.masterCellGroup && + ue_context_pP->ue_context.masterCellGroup->spCellConfig && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated && + ue_context_pP->ue_context.masterCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList ? + NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_RECONFIGURATION_DELAY_MS; + + nr_mac_enable_ue_rrc_processing_timer(ctxt_pP->module_id, + ue_context_pP->ue_context.rnti, + *RC.nrrrc[ctxt_pP->module_id]->carrier.servingcellconfigcommon->ssbSubcarrierSpacing, + delay_ms); + } + return 0; } diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 4f4ebc7048..22d74faa98 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -359,8 +359,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ NULL, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup, - 0); + ue_context_p->ue_context.secondaryCellGroup); } else { rrc_mac_config_req_gNB(rrc->module_id, rrc->configuration.ssb_SubcarrierOffset, @@ -373,8 +372,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_ NULL, 1, // add_ue flag ue_context_p->ue_id_rnti, - ue_context_p->ue_context.secondaryCellGroup, - 0); + ue_context_p->ue_context.secondaryCellGroup); } if(m == NULL){ From d059c717549e9e2e6d12b115fe441a3a8dce8a37 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Tue, 3 May 2022 16:08:18 +0100 Subject: [PATCH 196/233] Clean up and minor improvements in configuration files --- ...gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf | 19 ++++++------ ...gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf | 31 +++++-------------- ...gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf | 31 +++++-------------- 3 files changed, 26 insertions(+), 55 deletions(-) diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf index c190e63baf..e3ecf87ca0 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf @@ -21,17 +21,18 @@ gNBs = sd = 0x1; // 0 false, else true } ); - }); nr_cellid = 12345678L; ////////// Physical parameters: - ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts_N1 = 1; - pusch_AntennaPorts = 2; - ul_prbblacklist = "79,80,81,82" + ssb_SubcarrierOffset = 0; + pdsch_AntennaPorts_XP = 1; + pusch_AntennaPorts = 2; + do_CSIRS = 0; + ul_prbblacklist = "79,80,81,82" + pdcch_ConfigSIB1 = ( { controlResourceSetZero = 12; @@ -214,16 +215,16 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; prach_dtx_threshold = 120; - pucch0_dtx_threshold = 100; + pucch0_dtx_threshold = 80; } ); RUs = ( { local_rf = "yes" - nb_tx = 2 - nb_rx = 2 - att_tx = 0 + nb_tx = 2; + nb_rx = 2; + att_tx = 0; att_rx = 0; bands = [78]; max_pdschReferenceSignalPower = -27; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf index 264ab137c0..763396998d 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf @@ -19,13 +19,8 @@ gNBs = { sst = 1; sd = 0x1; // 0 false, else true - }, - { - sst = 1; - sd = 0x112233; // 0 false, else true } ); - }); nr_cellid = 12345678L; @@ -33,10 +28,9 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts = 1; + pdsch_AntennaPorts_XP = 1; pusch_AntennaPorts = 2; -# do_CSIRS = 1; - min_rxtxtime = 2; + do_CSIRS = 0; ul_prbblacklist = "107,108,109,110" pdcch_ConfigSIB1 = ( @@ -244,8 +238,9 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "local_RRC"; - pusch_TargetSNRx10 = 200; + pusch_TargetSNRx10 = 150; pucch_TargetSNRx10 = 200; + pusch_FailureThres = 1000; ulsch_max_frame_inactivity = 0; } ); @@ -255,21 +250,20 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; prach_dtx_threshold = 120; - pucch0_dtx_threshold = 120; + pucch0_dtx_threshold = 80; } ); RUs = ( { local_rf = "yes" - nb_tx = 2 - nb_rx = 2 - att_tx = 0 + nb_tx = 2; + nb_rx = 2; + att_tx = 0; att_rx = 0; bands = [78]; max_pdschReferenceSignalPower = -27; max_rxgain = 75; - sf_extension = 0; eNB_instances = [0]; ##beamforming 1x2 matrix: 1 layer x 2 antennas bf_weights = [0x00007fff, 0x0000]; @@ -316,21 +310,12 @@ security = { log_config : { global_log_level ="info"; - global_log_verbosity ="medium"; hw_log_level ="info"; - hw_log_verbosity ="medium"; phy_log_level ="info"; - phy_log_verbosity ="medium"; mac_log_level ="info"; - mac_log_verbosity ="high"; rlc_log_level ="info"; - rlc_log_verbosity ="medium"; pdcp_log_level ="info"; - pdcp_log_verbosity ="medium"; rrc_log_level ="info"; - rrc_log_verbosity ="medium"; ngap_log_level ="debug"; - ngap_log_verbosity ="medium"; f1ap_log_level ="debug"; - f1ap_log_verbosity ="medium"; }; \ No newline at end of file diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf index 7a1d087715..2a84743a0c 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf @@ -19,13 +19,8 @@ gNBs = { sst = 1; sd = 0x1; // 0 false, else true - }, - { - sst = 1; - sd = 0x112233; // 0 false, else true } ); - }); nr_cellid = 12345678L; @@ -33,10 +28,9 @@ gNBs = ////////// Physical parameters: ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts = 1; + pdsch_AntennaPorts_XP = 1; pusch_AntennaPorts = 2; -# do_CSIRS = 1; - min_rxtxtime = 2; + do_CSIRS = 0; ul_prbblacklist = "135,136,137,138" pdcch_ConfigSIB1 = ( @@ -244,8 +238,9 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "local_RRC"; - pusch_TargetSNRx10 = 200; + pusch_TargetSNRx10 = 150; pucch_TargetSNRx10 = 200; + pusch_FailureThres = 1000; ulsch_max_frame_inactivity = 0; } ); @@ -255,21 +250,20 @@ L1s = ( num_cc = 1; tr_n_preference = "local_mac"; prach_dtx_threshold = 120; - pucch0_dtx_threshold = 120; + pucch0_dtx_threshold = 80; } ); RUs = ( { local_rf = "yes" - nb_tx = 2 - nb_rx = 2 - att_tx = 0 + nb_tx = 2; + nb_rx = 2; + att_tx = 0; att_rx = 0; bands = [78]; max_pdschReferenceSignalPower = -27; max_rxgain = 75; - sf_extension = 0; eNB_instances = [0]; ##beamforming 1x2 matrix: 1 layer x 2 antennas bf_weights = [0x00007fff, 0x0000]; @@ -316,21 +310,12 @@ security = { log_config : { global_log_level ="info"; - global_log_verbosity ="medium"; hw_log_level ="info"; - hw_log_verbosity ="medium"; phy_log_level ="info"; - phy_log_verbosity ="medium"; mac_log_level ="info"; - mac_log_verbosity ="high"; rlc_log_level ="info"; - rlc_log_verbosity ="medium"; pdcp_log_level ="info"; - pdcp_log_verbosity ="medium"; rrc_log_level ="info"; - rrc_log_verbosity ="medium"; ngap_log_level ="debug"; - ngap_log_verbosity ="medium"; f1ap_log_level ="debug"; - f1ap_log_verbosity ="medium"; }; \ No newline at end of file From 7aa3bfaa54bc9e9c1d8d8d3033918c2d60a9e8d6 Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Tue, 3 May 2022 18:25:57 +0100 Subject: [PATCH 197/233] Revert deleted rfdevice.trx_end_func() --- executables/nr-softmodem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index a9214535d3..7ec316ccd1 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -848,6 +848,9 @@ int main( int argc, char **argv ) { // *** Handle per CC_id openair0 for(ru_id=0; ru_idrfdevice.trx_end_func) + RC.ru[ru_id]->rfdevice.trx_end_func(&RC.ru[ru_id]->rfdevice); + if (RC.ru[ru_id]->ifdevice.trx_end_func) RC.ru[ru_id]->ifdevice.trx_end_func(&RC.ru[ru_id]->ifdevice); } From f2974b431b12f78274c175da04e090c07a13c89f Mon Sep 17 00:00:00 2001 From: Roberto Louro Magueta Date: Tue, 3 May 2022 19:25:14 +0100 Subject: [PATCH 198/233] Revert transformPrecoder to transform_precoding --- .../nfapi/public_inc/fapi_nr_ue_interface.h | 2 +- .../nfapi/public_inc/nfapi_nr_interface_scf.h | 2 +- nfapi/open-nFAPI/nfapi/src/nfapi_p7.c | 4 ++-- .../PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 2 +- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 2 +- openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c | 10 +++++----- openair1/SIMULATION/NR_PHY/ulsim.c | 16 ++++++++-------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 16 ++++++++-------- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 4 ++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 6 +++--- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 6 +++--- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index 01f3847bb2..fcc243f5cd 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -305,7 +305,7 @@ typedef struct uint8_t qam_mod_order; uint8_t mcs_index; uint8_t mcs_table; - uint8_t transformPrecoder; + uint8_t transform_precoding; uint16_t data_scrambling_id; uint8_t nrOfLayers; uint8_t Tpmi; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index cfa8022526..c3a173758c 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -1210,7 +1210,7 @@ typedef struct uint8_t qam_mod_order; uint8_t mcs_index; uint8_t mcs_table; - uint8_t transformPrecoder; + uint8_t transform_precoding; uint16_t data_scrambling_id; uint8_t nrOfLayers; //DMRS diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c index 4d479da1b0..3adde23e7d 100644 --- a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c +++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c @@ -947,7 +947,7 @@ static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, ui push8(pusch_pdu->qam_mod_order, ppWritePackedMsg, end) && push8(pusch_pdu->mcs_index, ppWritePackedMsg, end) && push8(pusch_pdu->mcs_table, ppWritePackedMsg, end) && - push8(pusch_pdu->transformPrecoder, ppWritePackedMsg, end) && + push8(pusch_pdu->transform_precoding, ppWritePackedMsg, end) && push16(pusch_pdu->data_scrambling_id, ppWritePackedMsg, end) && push8(pusch_pdu->nrOfLayers, ppWritePackedMsg, end) && push16(pusch_pdu->ul_dmrs_symb_pos, ppWritePackedMsg, end) && @@ -4481,7 +4481,7 @@ static uint8_t unpack_ul_tti_request_pusch_pdu(void *tlv, uint8_t **ppReadPacked pull8(ppReadPackedMsg, &pusch_pdu->qam_mod_order, end) && pull8(ppReadPackedMsg, &pusch_pdu->mcs_index, end) && pull8(ppReadPackedMsg, &pusch_pdu->mcs_table, end) && - pull8(ppReadPackedMsg, &pusch_pdu->transformPrecoder, end) && + pull8(ppReadPackedMsg, &pusch_pdu->transform_precoding, end) && pull16(ppReadPackedMsg, &pusch_pdu->data_scrambling_id, end) && pull8(ppReadPackedMsg, &pusch_pdu->nrOfLayers, end) && pull16(ppReadPackedMsg, &pusch_pdu->ul_dmrs_symb_pos, end) && diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 20575b45d1..9e2b3d2d4e 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -169,7 +169,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, gNB->pusch_gold_init[pusch_pdu->scid] = pusch_pdu->ul_dmrs_scrambling_id; nr_gold_pusch(gNB, pusch_pdu->scid, pusch_pdu->ul_dmrs_scrambling_id); } - if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { + if (pusch_pdu->transform_precoding == transformPrecoder_disabled) { nr_pusch_dmrs_rx(gNB, Ns, gNB->nr_gold_pusch_dmrs[pusch_pdu->scid][Ns][symbol], &pilot[0], (1000+p), 0, nb_rb_pusch, (pusch_pdu->bwp_start + pusch_pdu->rb_start)*NR_NB_SC_PER_RB, pusch_pdu->dmrs_config_type); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 5066316e62..e81ef00cac 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -2082,7 +2082,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB, nb_re_pusch); stop_meas(&gNB->ulsch_mrc_stats); - if (rel15_ul->transformPrecoder == transformPrecoder_enabled) { + if (rel15_ul->transform_precoding == transformPrecoder_enabled) { #ifdef __AVX2__ // For odd number of resource blocks need byte alignment to multiple of 8 int nb_re_pusch2 = nb_re_pusch + (nb_re_pusch&7); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index e4b6dbcc1d..450805a26c 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -282,7 +282,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, /// Transform-coded "y"-sequences (for definition see 38-211 V15.3.0 2018-09, subsection 6.3.1.4) int32_t y[max_num_re] __attribute__ ((aligned(16))); - if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { + if (pusch_pdu->transform_precoding == transformPrecoder_enabled) { uint32_t nb_re_pusch=nb_rb * NR_NB_SC_PER_RB; uint32_t y_offset = 0; @@ -377,7 +377,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, if ((ul_dmrs_symb_pos >> l) & 0x01) { is_dmrs_sym = 1; - if (pusch_pdu->transformPrecoder == transformPrecoder_disabled){ + if (pusch_pdu->transform_precoding == transformPrecoder_disabled){ if (dmrs_type == pusch_dmrs_type1) dmrs_idx = (pusch_pdu->bwp_start + start_rb)*6; @@ -393,7 +393,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, } } else if (pusch_pdu->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) { - AssertFatal(pusch_pdu->transformPrecoder == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); + AssertFatal(pusch_pdu->transform_precoding == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); if(is_ptrs_symbol(l, ulsch_ue->ptrs_symbols)) { is_ptrs_sym = 1; @@ -424,7 +424,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, if (is_dmrs == 1) { // if transform precoding is enabled - if (pusch_pdu->transformPrecoder == transformPrecoder_enabled) { + if (pusch_pdu->transform_precoding == transformPrecoder_enabled) { ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[2*dmrs_idx]) >> 15; ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*AMP*dmrs_seq[(2*dmrs_idx) + 1]) >> 15; } else { @@ -448,7 +448,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = (beta_ptrs*AMP*mod_ptrs[(ptrs_idx<<1) + 1]) >> 15; ptrs_idx++; } else if (!is_dmrs_sym || allowed_xlsch_re_in_dmrs_symbol(k, start_sc, frame_parms->ofdm_symbol_size, cdm_grps_no_data, dmrs_type)) { - if (pusch_pdu->transformPrecoder == transformPrecoder_disabled) { + if (pusch_pdu->transform_precoding == transformPrecoder_disabled) { ((int16_t*)tx_precoding[nl])[(sample_offsetF)<<1] = ((int16_t *)tx_layers[nl])[m<<1]; ((int16_t*)tx_precoding[nl])[((sample_offsetF)<<1) + 1] = ((int16_t *)tx_layers[nl])[(m<<1) + 1]; } diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 4d271b4d1d..fc44e993bb 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -323,7 +323,7 @@ int main(int argc, char **argv) uint16_t ptrsSymbPerSlot = 0; uint16_t ptrsRePerSymb = 0; - uint8_t transformPrecoder = transformPrecoder_disabled; // 0 - ENABLE, 1 - DISABLE + uint8_t transform_precoding = transformPrecoder_disabled; // 0 - ENABLE, 1 - DISABLE uint8_t num_dmrs_cdm_grps_no_data = 1; uint8_t mcs_table = 0; @@ -581,10 +581,10 @@ int main(int argc, char **argv) break; case 'Z': - transformPrecoder = transformPrecoder_enabled; + transform_precoding = transformPrecoder_enabled; num_dmrs_cdm_grps_no_data = 2; mcs_table = 3; - printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transformPrecoder); + printf("NOTE: TRANSFORM PRECODING (SC-FDMA) is ENABLED in UPLINK (0 - ENABLE, 1 - DISABLE) : %d \n", transform_precoding); break; default: @@ -879,7 +879,7 @@ int main(int argc, char **argv) if ((UE->frame_parms.nb_antennas_tx==4)&&(precod_nbr_layers==4)) num_dmrs_cdm_grps_no_data = 2; - if (transformPrecoder == transformPrecoder_enabled) { + if (transform_precoding == transformPrecoder_enabled) { AssertFatal(enable_ptrs == 0, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n"); @@ -1084,7 +1084,7 @@ int main(int argc, char **argv) pusch_pdu->mcs_table = mcs_table; pusch_pdu->target_code_rate = code_rate; pusch_pdu->qam_mod_order = mod_order; - pusch_pdu->transformPrecoder = transformPrecoder; + pusch_pdu->transform_precoding = transform_precoding; pusch_pdu->data_scrambling_id = *scc->physCellId; pusch_pdu->nrOfLayers = precod_nbr_layers; pusch_pdu->ul_dmrs_symb_pos = l_prime_mask; @@ -1111,7 +1111,7 @@ int main(int argc, char **argv) pusch_pdu->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; // if transform precoding is enabled - if (transformPrecoder == transformPrecoder_enabled) { + if (transform_precoding == transformPrecoder_enabled) { pusch_pdu->dfts_ofdm.low_papr_group_number = *scc->physCellId % 30; // U as defined in 38.211 section 6.4.1.1.1.2 pusch_pdu->dfts_ofdm.low_papr_sequence_number = 0; // V as defined in 38.211 section 6.4.1.1.1.2 @@ -1171,10 +1171,10 @@ int main(int argc, char **argv) ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list = (nfapi_nr_ue_ptrs_ports_t *) malloc(2*sizeof(nfapi_nr_ue_ptrs_ports_t)); ul_config.ul_config_list[0].pusch_config_pdu.pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset = 0; - ul_config.ul_config_list[0].pusch_config_pdu.transformPrecoder = transformPrecoder; + ul_config.ul_config_list[0].pusch_config_pdu.transform_precoding = transform_precoding; // if transform precoding is enabled - if (transformPrecoder == transformPrecoder_enabled) { + if (transform_precoding == transformPrecoder_enabled) { ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_group_number = *scc->physCellId % 30;// U as defined in 38.211 section 6.4.1.1.1.2 ul_config.ul_config_list[0].pusch_config_pdu.dfts_ofdm.low_papr_sequence_number = 0;// V as defined in 38.211 section 6.4.1.1.1.2 diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 3201b51d30..868db25463 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -216,7 +216,7 @@ void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_con else transformPrecoder = NR_PUSCH_Config__transformPrecoder_disabled; } - pusch_config_pdu->transformPrecoder = transformPrecoder; + pusch_config_pdu->transform_precoding = transformPrecoder; /* PRECOD_NBR_LAYERS */ if ((*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_nonCodebook)); @@ -656,7 +656,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->scid = 0; // Transform precoding according to 6.1.3 UE procedure for applying transform precoding on PUSCH in 38.214 - pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out + pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, NULL, NR_RNTI_TC, 0); // TBR fix rnti and take out // Resource allocation in frequency domain according to 6.1.2.2 in TS 38.214 pusch_config_pdu->resource_alloc = (mac->cg) ? pusch_Config->resourceAllocation : 1; @@ -702,7 +702,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* Transform precoding */ if (rnti_type != NR_RNTI_CS || (rnti_type == NR_RNTI_CS && dci->ndi == 1)) { - pusch_config_pdu->transformPrecoder = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); + pusch_config_pdu->transform_precoding = get_transformPrecoding(initialUplinkBWP, pusch_Config, NULL, dci_format, rnti_type, 0); } /*DCI format-related configuration*/ @@ -743,7 +743,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* TRANSFORM PRECODING ------------------------------------------------------------------------------------------*/ - if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled) { + if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled) { pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; @@ -796,7 +796,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->mcs_index = dci->mcs; /* MCS TABLE */ - if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { + if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_Table : NULL, 0, *dci_format, rnti_type, target_ss, false); } else { pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config ? pusch_Config->mcs_TableTransformPrecoder : NULL, 1, *dci_format, rnti_type, target_ss, false); @@ -831,12 +831,12 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, mappingtype, add_pos, dmrslength, pusch_config_pdu->start_symbol_index, mac->scc ? mac->scc->dmrs_TypeA_Position : mac->mib->dmrs_TypeA_Position); - if (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { + if (mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { if (*dci_format != NR_UL_DCI_FORMAT_0_1) { pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1; } } - else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled)) + else if (*dci_format == NR_UL_DCI_FORMAT_0_0 || (mac->ULbwp[0] && pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled)) pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; // Num PRB Overhead from PUSCH-ServingCellConfig @@ -857,7 +857,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup && mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB && mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { - if (pusch_config_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_disabled) { + if (pusch_config_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_disabled) { nfapi_nr_ue_ptrs_ports_t ptrs_ports_list; pusch_config_pdu->pusch_ptrs.ptrs_ports_list = &ptrs_ports_list; valid_ptrs_setup = set_ul_ptrs_values(mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index c9a22b92c2..9a651293b2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1058,9 +1058,9 @@ void fill_msg3_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, pusch_pdu->cyclic_prefix = 0; pusch_pdu->mcs_table = 0; if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL) - pusch_pdu->transformPrecoder = 1; + pusch_pdu->transform_precoding = 1; else - pusch_pdu->transformPrecoder = 0; + pusch_pdu->transform_precoding = 0; pusch_pdu->data_scrambling_id = *scc->physCellId; pusch_pdu->nrOfLayers = 1; pusch_pdu->ul_dmrs_symb_pos = get_l_prime(nr_of_symbols,mappingtype,pusch_dmrs_pos2,pusch_len1,start_symbol_index, scc->dmrs_TypeA_Position); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index d426bc69c7..728948ba53 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -687,11 +687,11 @@ void nr_set_pusch_semi_static(const NR_SIB1_t *sib1, ps->pusch_Config = ubwp && ubwp->bwp_Dedicated && ubwp->bwp_Dedicated->pusch_Config ? ubwp->bwp_Dedicated->pusch_Config->choice.setup : (ubwpd ? ubwpd->pusch_Config->choice.setup : NULL); if (ps->pusch_Config == NULL || !ps->pusch_Config->transformPrecoder) - ps->transformPrecoder = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; + ps->transform_precoding = !scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder; else - ps->transformPrecoder = *ps->pusch_Config->transformPrecoder; + ps->transform_precoding = *ps->pusch_Config->transformPrecoder; const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific; - if (ps->transformPrecoder) + if (ps->transform_precoding) ps->mcs_table = get_pusch_mcs_table(ps->pusch_Config ? ps->pusch_Config->mcs_Table : NULL, 0, ps->dci_format, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 4061590f6e..4fba22a0c0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1794,7 +1794,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) pusch_pdu->qam_mod_order = sched_pusch->Qm; pusch_pdu->mcs_index = sched_pusch->mcs; pusch_pdu->mcs_table = ps->mcs_table; - pusch_pdu->transformPrecoder = ps->transformPrecoder; + pusch_pdu->transform_precoding = ps->transform_precoding; if (ps->pusch_Config && ps->pusch_Config->dataScramblingIdentityPUSCH) pusch_pdu->data_scrambling_id = *ps->pusch_Config->dataScramblingIdentityPUSCH; else @@ -1805,7 +1805,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) /* FAPI: DMRS */ pusch_pdu->ul_dmrs_symb_pos = ps->ul_dmrs_symb_pos; pusch_pdu->dmrs_config_type = ps->dmrs_config_type; - if (pusch_pdu->transformPrecoder) { // transform precoding disabled + if (pusch_pdu->transform_precoding) { // transform precoding disabled long *scramblingid=NULL; if (ps->NR_DMRS_UplinkConfig && pusch_pdu->scid == 0) scramblingid = ps->NR_DMRS_UplinkConfig->transformPrecodingDisabled->scramblingID0; @@ -1852,7 +1852,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) LOG_D(NR_MAC,"PUSCH PDU : data_scrambling_identity %x, dmrs_scrambling_id %x\n",pusch_pdu->data_scrambling_id,pusch_pdu->ul_dmrs_scrambling_id); /* TRANSFORM PRECODING --------------------------------------------------------*/ - if (pusch_pdu->transformPrecoder == NR_PUSCH_Config__transformPrecoder_enabled){ + if (pusch_pdu->transform_precoding == NR_PUSCH_Config__transformPrecoder_enabled){ // U as specified in section 6.4.1.1.1.2 in 38.211, if sequence hopping and group hopping are disabled pusch_pdu->dfts_ofdm.low_papr_group_number = pusch_pdu->pusch_identity % 30; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 4b55c70d1e..051f5927ec 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -350,7 +350,7 @@ typedef struct NR_pusch_semi_static_t { int nrOfSymbols; NR_PUSCH_Config_t *pusch_Config; - uint8_t transformPrecoder; + uint8_t transform_precoding; uint8_t mcs_table; long mapping_type; From 400b52b46f4dca7249acac482fe798ed04fd2237 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 4 May 2022 10:00:48 +0200 Subject: [PATCH 199/233] Do not separately install python for UHD, is done in general packages --- cmake_targets/tools/build_helper | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index 554f3ccfe5..0d2c82d62e 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -410,7 +410,7 @@ check_install_usrp_uhd_driver(){ x=$((x + 1)) done $SUDO apt-get update - $SUDO apt-get -y install python python-tk libboost-all-dev libusb-1.0-0-dev + $SUDO apt-get -y install python-tk libboost-all-dev libusb-1.0-0-dev if [[ "$distribution" == "ubuntu16.04" ]]; then $SUDO apt-get -y install libuhd-dev libuhd3.15.0 uhd-host fi From 65263a09ac1f6e714bcb47dd5a28dec6f1060898 Mon Sep 17 00:00:00 2001 From: Manish Date: Wed, 27 Apr 2022 05:31:12 +0000 Subject: [PATCH 200/233] help menu updated in document for native architecture. Compilation steps corrected on Ubuntu 20. Signed-off-by: Manish --- doc/BUILD.md | 1 + doc/TESTING_5GSA_setup.md | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/doc/BUILD.md b/doc/BUILD.md index a52ce98cb5..70d69f5afd 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -193,6 +193,7 @@ Using the help option of the build script you can get the list of available opti | --UE | maintained and tested in CI | build `lte-uesoftmodem` the LTE UE | | --gNB | maintained and tested in CI | build `nr-softmodem` the 5G gNodeB | | --nrUE | maintained and tested in CI | build `nr-uesoftmodem` the 5G UE | +| --arch-native | maintained | build with native architecture optimization | | --usrp-recplay | deprecated | use the USRP configuration parameters to use the record player. | | --build-lib | maintained | build optional shared library(ies), which can then be loaded at run time via command line option. Use the --help option to get the list of supported optional libraries. `all` can be used to build all available optional libraries. | | --UE-conf-nvram | maintained | Specifies the path to the input file used by the conf2uedata utility. defaults to [openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf](../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf) | diff --git a/doc/TESTING_5GSA_setup.md b/doc/TESTING_5GSA_setup.md index 9b27549df4..4b86e12297 100644 --- a/doc/TESTING_5GSA_setup.md +++ b/doc/TESTING_5GSA_setup.md @@ -82,15 +82,7 @@ To get the code and build the gNB executable: git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git git checkout develop - # Install dependencies in Ubuntu 20.04 - cd - cd openairinterface5g/ - source oaienv - cd cmake_targets/ - ./install_external_packages.ubuntu20 - # Build OAI gNB - cd cd openairinterface5g/ source oaienv cd cmake_targets/ From eef8cf510b39ccc092b41679bfd0e00d1e6cf304 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 6 May 2022 11:17:49 +0200 Subject: [PATCH 201/233] Improve RFsim documentation --- targets/ARCH/rfsimulator/README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/targets/ARCH/rfsimulator/README.md b/targets/ARCH/rfsimulator/README.md index 4a8d5ff79a..feb7f015b9 100644 --- a/targets/ARCH/rfsimulator/README.md +++ b/targets/ARCH/rfsimulator/README.md @@ -61,6 +61,8 @@ Setting the env variable RFSIMULATOR can be used instead of using the serveraddr ## How to use the RF simulator options +To define and use a channel model, the configuration file needs to include a channel configuration file. To do this, add `@include "channelmod_rfsimu.conf"` to the end of the configuration file, and place the channel configuration file in the same directory. An example channel configuration file `channelmod_rfsimu.conf` is in `ci-scripts/conf_files`. + Add the following options to the command line to enable the channel model and the IQ samples saving for future replay: ```bash --rfsimulator.options chanmod,saviq @@ -82,6 +84,8 @@ Example run: sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test --rfsimulator.options chanmod --rfsimulator.modelname AWGN ``` +where `@include "channelmod_rfsimu.conf"` has been added at the end of the file, and `ci-scripts/conf_files/channelmod_rfsimu.conf` copied to `targets/PROJECTS/GENERIC-LTE-EPC/CONF/`. + ## 4G case For the UE, it should be set to the IP address of the eNB. For example: @@ -102,9 +106,7 @@ $OPENAIR_DIR/targets/bin/conf2uedata -c $OPENAIR_DIR/openair3/NAS/TOOLS/ue_eurec ## 5G case -The 5G RF simulator will be aligned with the 4G as the efforts for merging the 5G specific branches into the develop make progresses. - -After regular build, add the simulation driver (do not use ./build_oai -w SIMU until 4G and 5G branches are merged). +If `build_oai` has not been run with `-w SIMU`, you need to build the `rfsimulator` manually. To do so: ```bash cd ran_build/build make rfsimulator @@ -113,7 +115,7 @@ make rfsimulator ### Launch gNB in one window ```bash -sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test +sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test --nokrnmod 1 ``` ### Launch UE in another window @@ -124,15 +126,17 @@ sudo RFSIMULATOR= ./nr-uesoftmodem --rfsim --phy-t Notes: -1. can be 127.0.0.1 if both gNB and nrUE executables run on the same host, OR the IP interface address of the remote host running the gNB executable, if the gNB and nrUE run on separate hosts -2. the --rrc_config_path parameter SHALL specify where the 2 RAW files are located (`rbconfig.raw` and `reconfig.raw`). +1. This starts the gNB and UE in the `phy-test` UP-only mode where the gNB is started as if a UE had already connected, and a configurable scheduler is used instead of the default one. The options `-m`, `-l`, `-t`, `-M`, `-T`, `-D`, and `-U` can be used to configure this scheduler. +2. can be 127.0.0.1 if both gNB and nrUE executables run on the same host, OR the IP interface address of the remote host running the gNB executable, if the gNB and nrUE run on separate hosts. +3. The --rrc_config_path parameter SHALL specify where the 2 RAW files are located (`rbconfig.raw` and `reconfig.raw`). - If you are running on the same machine and launched the 2 executables (`nr-softmodem` and `nr-uesoftmodem`) from the same directory, nothing has to be done. - If you launched the 2 executables from 2 different folders, just point to the location where you launched the `nr-softmodem`: * `sudo RFSIMULATOR= ./nr-uesoftmodem --rfsim --phy-test --rrc_config_path /the/path/where/you/launched/nr-softmodem` - If you are not running on the same machine or launched the 2 executables from 2 different folders, you need to **COPY** the 2 raw files * `scp usera@machineA:/the/path/where/you/launched/nr-softmodem/r*config.raw userb@machineB:/the/path/where/you/will/launch/nr-uesoftmodem/` * Obviously this operation SHALL be done before launching the `nr-uesoftmodem` executable. -3. to enable the noS1 mode --noS1 and --nokrnmod 1 options should be added to the command line +4. To enable the noS1 mode, `--noS1` option should be added to the command line. +5. To operate the gNB/UE with a 5GC, start them using the `--sa` option. More information can be found [here](../../../doc/TESTING_5GSA_setup.md#2-sa-setup-with-oai-nr-ue-softmodem). In the UE, you can add `-d` option to get the softscope. From d0de678f5613e8dcc09dc2108fa1be782bd3dc89 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 25 Apr 2022 17:23:05 +0200 Subject: [PATCH 202/233] Update CI documentation --- doc/TESTBenches.md | 229 ++++++++++++++++-- doc/testbenches_doc_resources/indoor_live.jpg | Bin 254133 -> 0 bytes doc/testbenches_doc_resources/legacy1.jpg | Bin 107882 -> 0 bytes doc/testbenches_doc_resources/legacy2.jpg | Bin 122055 -> 0 bytes doc/testbenches_doc_resources/next_ci.jpg | Bin 157592 -> 0 bytes doc/testbenches_doc_resources/next_dev.jpg | Bin 185772 -> 0 bytes .../outdoor_live.jpg | Bin 167702 -> 0 bytes 7 files changed, 204 insertions(+), 25 deletions(-) delete mode 100755 doc/testbenches_doc_resources/indoor_live.jpg delete mode 100755 doc/testbenches_doc_resources/legacy1.jpg delete mode 100755 doc/testbenches_doc_resources/legacy2.jpg delete mode 100755 doc/testbenches_doc_resources/next_ci.jpg delete mode 100755 doc/testbenches_doc_resources/next_dev.jpg delete mode 100755 doc/testbenches_doc_resources/outdoor_live.jpg diff --git a/doc/TESTBenches.md b/doc/TESTBenches.md index 7365bc0f4c..fc05299110 100644 --- a/doc/TESTBenches.md +++ b/doc/TESTBenches.md @@ -1,45 +1,224 @@ ## Table of Contents ## -1. [Legacy 1 Bench](#legacy-1-bench) -2. [Legacy 2 Bench](#legacy-2-bench) -3. [Next Bench for DEV](#next-bench-for-dev) -4. [Next Bench for CI](#next-bench-for-ci) -5. [Indoor Live Network Bench](#indoor-live-network-bench) -6. [Outdoor Live Network Bench](#outdoor-live-network-bench) +1. [Machines](#machines) +2. [Networked devices](#networked-devices) +3. [Testbenches](#testbenches) +4. [Pipelines](#pipelines) +## Machines -## Legacy 1 Bench +| Machine | IP address | Lockable Resource | Function | Connected devices | +| ------------- | --------------- | --------------------- | ------------------ | ----------------------------------------------------- | +| asterix | 172.21.16.127 | CI-Asterix-Usage | gNB | 173.21.19.14 | +| obelix | 172.21.16.128 | CI-Obelix-Usage | eNB, UE (5G) | 172.21.19.13, X300 (192.168.60.2), B200mini (30C51EB) | +| porcepix | 172.21.16.136 | CI-NSA-MiniBench | Executor, EPC, 5GC | -- | +| nrmodule2 | 172.21.16.139 | CI-NSA-MiniBench | Quectel | Quectel module | +| nepes | 172.21.16.137 | CI-NSA-MiniBench | gNB | B200mini (30C51D4) | +| caracal | 172.21.16.132 | CI-Caracal | gNB/phytest | N300 (192.168.10.2) | +| idefix | 172.21.16.135 | CI-NSA-MiniBench | Quectel | Quectel module | +| amariue | 172.21.16.144 | CI-Amarisoft-UE-Usage | TBD | Amarisoft UE simulator | +| bellatrix | 192.168.117.115 | CI-RAN-VM-Deployment | Executor | -- | +| nano | 192.168.12.62 | CI-Bench-1-Phones | EPC, adb | 2x COTS (adb) | +| hutch | 192.168.12.19 | CI-Bench-1-Phones | eNB (B7) | B200mini (30C5239) | +| starsky | 192.168.12.18 | CI-Bench-1-Phones | eNB (B40) | b200mini (30A3E3C) | +| carabe | 192.168.12.211 | CI-Bench-2-OAI-Phone | UE 4G (B) | B200mini (30AE8C9) | -**Purpose** : FDD Band 7 and Band 13, LTE-M -**Note** : Legacy1 and Legacy2 are duplicated so that they can run in parallel, thus avoiding a CI bottleneck -**Note** : Faraday Cages 1 and 2 are physically the same cage +Note: The available resources, and their current usage, is indicated here: +- [Lockable resources of jenkins-oai](https://jenkins-oai.eurecom.fr/lockable-resources/): + "New" Jenkins, i.e., with RAN-Container-Parent +- [Lockable resources of open5glab jenkins](https://open5glab.eurecom.fr:8083/jenkins/lockable-resources/): + "Old" Jenkins, i.e., with RAN-CI-Develop -![image info](./testbenches_doc_resources/legacy1.jpg) +## Networked devices -## Legacy 2 Bench +| Type | IP address (mgmt) | +| ------------- | ----------------- | +| USRP N310 | 172.21.19.14 | +| USRP N310 | 172.21.19.13 | -**Purpose** : TDD Band 40, TM2 2xTX 2xRX -**Note** : CN can run in a container, could also run on Massive +## Testbenches -![image info](./testbenches_doc_resources/legacy2.jpg) +### OTA Testbench -## Next Bench for DEV +[Proper image to be followed up. TBD: add antennas/circulators] -**Note** : Benetel CI can also run on this bench at night +Note: obelix and porcepix are both used in the OTA testbench and the 5G +NSA/Faraday Cage testbench! -![image info](./testbenches_doc_resources/next_dev.jpg) +``` + asterix + ┌─────┐ ┌────┐ + │ ├────────┤N300│ + │ │ └────┘ + └─┬───┘ +porcepix │ +┌─────┐ │ nrmodule2 +│ ├───────────┘ ┌───────┐ ┌─────┐ +│ │ │Quectel├─────────┤ │ +└──┬─┬┘ obelix ┌────┐ └───────┘ │ │ + │ │ ┌─────┐ │N300│ └─────┘ + │ └──────────┤ ├────────┴────┘ + │ │ │ + │ └─────┴────────┬────┐ + │ │X300│ + │ └────┘ + │ UEsimbox + │ ┌─────┐ + └────────────┤ │ ┌────┐ + │ ├────────┤SDR │ + └─────┘ └────┘ +``` +### 5G NSA/Faraday Cage Testbench -## Next Bench for CI +**Purpose**: Faraday cage 5G tests -**Note** : The current test running on Caracal could run on this bench with a N300/N300 setup +Note: obelix and porcepix are both used in the OTA testbench and the 5G +NSA/Faraday Cage testbench! -![image info](./testbenches_doc_resources/next_ci.jpg) +[Proper image to be followed up] -## Indoor Live Network Bench +``` + obelix + ┌─────┐ + │ ├─────B7────────────────────┐ + │ │ Faraday Cage │ + └┬────┘ ┌──────────────┼───┐ + │ │ │ │ + │ │ Y │ +porcepix │ │ │ +┌─────┐ │ │ │ idefix +│ ├──────┘ │ Y──┼────┐ ┌─────┐ +│ │ │ Y--│----├───────┐ │ │ +└────┬┘ │ Y──┼────┤Quectel├──────┤ │ + │ │ Y--│----└───────┘ └─────┘ + │ │ Y │ + │ nepes │ │ │ + │ ┌─────┐ └────────────┼─────┘ + │ │ │ │ + └──────┤ ├─────B78─────────────────┘ + └─────┘ +``` -![image info](./testbenches_doc_resources/indoor_live.jpg) +### 4G Testbench(es) -## Outdoor Live Network Bench +**Purpose**: 4G/LTE testbenches -![image info](./testbenches_doc_resources/outdoor_live.jpg) +[Proper image to be followed up] + +``` + hutch + ┌─────┐ + │ ├─────B7────────────────────┐ + ┌──────┤ │ Faraday Cage │ + │ └─────┘ ┌──────────────┼───┐ + │ │ │ │ + │ │ Y │ +nano │ │ │ +┌────┴┐ USB ┌──┼─UE │ carabe +│ ├─────────────────────┤ │ │ ┌─────┐ +│ │ └──┼─UE │ │ │ +└───┬─┘ │ Y──┼───────B7UE──┤ │ + │ │ │ └─────┘ + │ │ Y │ + │ starsky │ │ │ + │ ┌─────┐ └────────────┼─────┘ + └───────┤ │ │ + │ ├──┬───────B40────────────┘ + └─────┘ │ + Y (sniffer) +``` + +## Pipelines + +### [RAN-Container-Parent](https://jenkins-oai.eurecom.fr/job/RAN-Container-Parent/) + +**Purpose**: automatically triggered tests on MR creation or push, from Gitlab +Webhook + +- [RAN-cppcheck](https://jenkins-oai.eurecom.fr/job/RAN-cppcheck/1664/) + - obelix + - performs static code analysis, currently not actively enforced +- [RAN-gNB-nrUE-MONO-TDD-Band78-N300](https://jenkins-oai.eurecom.fr/job/RAN-gNB-nrUE-MONO-TDD-Band78-N300/) + - caracal + N310 + - pure performance test through phy-test scheduler, see command line for more + details +- [RAN-L2-Sim-Test-5G](https://jenkins-oai.eurecom.fr/job/RAN-L2-Sim-Test-5G/) + - obelix (gNB, 1x UE, OAI 5GC) + - L2simulator: skips physical layer and uses proxy between gNB and UE, + currently only ping +- [RAN-NSA-B200-Module-LTEBOX-Container](https://jenkins-oai.eurecom.fr/job/RAN-NSA-B200-Module-LTEBOX-Container/) + - obelix + B200, nepes + B200, idefix + Quectel, porcepix w/ ltebox + - basic NSA test, known to be instable +- [RAN-PhySim-Cluster](https://jenkins-oai.eurecom.fr/job/RAN-PhySim-Cluster/) + - asterix (`Asterix-OC-oaicicd-session` resource), tests in OpenShift Cluster + - unitary simulators (`nr_dlsim`, etc.) +- [RAN-RF-Sim-Test-4G](https://jenkins-oai.eurecom.fr/job/RAN-RF-Sim-Test-4G/) + - obelix (eNB, 1x UE, OAI EPC) + - uses RFsimulator, for FDD 5, 10, 20MHz with core, 5MHz noS1 +- [RAN-RF-Sim-Test-5G](https://jenkins-oai.eurecom.fr/job/RAN-RF-Sim-Test-5G/) + - obelix (gNB, 2x UE, OAI 5GC) + - uses RFsimulator, TDD 40MHz, FDD 40MHz, F1 split +- [RAN-RHEL8-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-RHEL8-Image-Builder/) + - asterix: RHEL 8 image build using podman +- [RAN-Ubuntu18-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu18-Image-Builder/) + - obelix: Ubuntu 18 image build using docker + +### [RAN-CI-NSA-Trigger](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-CI-NSA-Trigger/) + +**Purpose**: longer-running over-the-air LTE, NSA, and SA tests, triggered +through cron job at midnight + +- [RAN-LTE-2x2-Module-OAIEPC](https://jenkins-oai.eurecom.fr/job/RAN-LTE-2x2-Module-OAIEPC/) + - obelix + N310, nrmodule2 + Quectel, porcepix w/ Magma EPC + - LTE 2x2 test with TM1 and TM2 +- [RAN-NSA-B200-Module-LTEBOX](https://jenkins-oai.eurecom.fr/job/RAN-NSA-B200-Module-LTEBOX/) + - obelix + B200 (eNB), nepes + B200 (gNB), idefix + Quectel, porcepix w/ ltebox + - Note: like [RAN-NSA-B200-Module-LTEBOX-Container](https://jenkins-oai.eurecom.fr/job/RAN-NSA-B200-Module-LTEBOX-Container/) above, but compiled/run from source +- [RAN-NSA-2x2-Module-OAIEPC](https://jenkins-oai.eurecom.fr/job/RAN-NSA-2x2-Module-OAIEPC/) + - obelix + N310 (eNB), asterix + N310 (gNB), nrmodule2 + Quectel, porcepix w/ Magma EPC + - LTE 2x2 and NR 2x2 (non-standalone) +- [RAN-SA-Module-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-Module-CN5G/) + - asterix + N310 (gNB), nrmodule2 + Quectel, porcepix w/ OAI 5GC + - NR 2x2 (standalone) +- [RAN-SA-OAIUE-N310-X300-CN5G](https://jenkins-oai.eurecom.fr/job/RAN-SA-OAIUE-N310-X300-CN5G/) + - asterix + N310 (gNB), obelix + N310 or X300 (5G UE), porcepix w/ OAI 5GC + - OTA test with OAIUE using both N310 and X300 +- [RAN-SA-AmariS-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-AmariS-CN5G/) + - asterix + N310, amariue (1x UE), porcepix w/ OAI 5GC + - Amarisoft UE simulator: expected to be increased to more UEs + +### [RAN-CI-develop](https://open5glab.eurecom.fr:8083/jenkins/job/RAN-CI-develop/) + +**Purpose**: automatically triggered tests, mostly 4G, to be phased out and +integrated into RAN-Container-Parent + +runs tests: +- bellatrix: runs 4G/5G simulators directly (eNB + 1x UE + (opt.) OAI EPC, gNB + 1x UE in "noS1") + +triggers pipelines: +- [eNB-CI-F1-FDD-Band7-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-F1-FDD-Band7-B210/) + - hutch + B210, nano w/ ltebox + 2x UE + - tests 4G FDD with F1 split, 5MHz, 10MHz, 20MHz. 5MHz stable, rest known to + be unstable +- [eNB-CI-FDD-Band7-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-FDD-Band7-B210/) + - hutch + B210, nano w/ ltebox + 2x UE + - tests T tracer, information through FlexRAN, RRC inactivity timers, + inactivity timers + FlexRAN, different bandwidths +- [eNB-CI-IF4p5-FDD-Band7-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-IF4p5-FDD-Band7-B210/) + - hutch + B210, nano w/ ltebox + 2x UE + - tests IF4.5 split over bandwidths 5, 10, 20 MHz in Band 7 +- [eNB-CI-IF4p5-TDD-Band40-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-IF4p5-TDD-Band40-B210/) + - starsky + B210, nano w/ ltebox + 2x UE + - tests IF4.5 split over bandwidths 5, 10, 20 MHz in Band 40 +- [eNB-CI-TDD-Band40-B210](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-CI-TDD-Band40-B210/) + - starsky + B210, nano w/ ltebox + 2x UE + - T tracer, TM1 over bandwidths 5, 10, 20 MHz in Band 40, default scheduler + for 20 MHz +- [eNB-UE-CI-MONO-FDD-Band7-B200](https://open5glab.eurecom.fr:8083/jenkins/job/eNB-UE-CI-MONO-FDD-Band7-B200/) + - hutch + B210 (eNB), carabe + B210 (4G UE), nano w/ ltebox + - tests OAI 4G for 5 MHz/TM1 with both CN and in noS1 model, MBMS; known to + be unstable +- [UE-CI-FDD-Band20-B200](https://open5glab.eurecom.fr:8083/jenkins/job/UE-CI-FDD-Band20-B200/) + - starsky + B210 (sniffer) + - Sniff MIB + SIB1 of Orange, SFR diff --git a/doc/testbenches_doc_resources/indoor_live.jpg b/doc/testbenches_doc_resources/indoor_live.jpg deleted file mode 100755 index ce8b9cda4817796abf6fb87da43befe6090469be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 254133 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"-0QgQ_f&;P+@ae|xCKI-OpCg4A{V9= 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

txr3cvg?DddS~a=5*D+7If^1vV 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%0Ahv6zr6ARw*_`jR9g7o+ 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

gKrND=tx1)lqF~lNhyCK#r{*<~^zl|> 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 diff --git a/doc/testbenches_doc_resources/outdoor_live.jpg b/doc/testbenches_doc_resources/outdoor_live.jpg deleted file mode 100755 index eba44a3c00e7456c7d2d0cc12ea1e77db31ae10a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167702 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 Date: Fri, 6 May 2022 12:17:18 +0200 Subject: [PATCH 203/233] calling config_csiim also in sa --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 34 ++--------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 8e8959c597..e22bca592f 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1412,39 +1412,9 @@ void fill_initial_SpCellConfig(int uid, NR_CSI_MeasConfig_t *csi_MeasConfig = calloc(1,sizeof(*csi_MeasConfig)); SpCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup = csi_MeasConfig; int pdsch_AntennaPorts = configuration->pdsch_AntennaPorts.N1 * configuration->pdsch_AntennaPorts.N2 * configuration->pdsch_AntennaPorts.XP; - if (pdsch_AntennaPorts > 1) { - csi_MeasConfig->csi_IM_ResourceToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceToAddModList)); - NR_CSI_IM_Resource_t *imres0 = calloc(1,sizeof(*imres0)); - imres0->csi_IM_ResourceId = 0; - imres0->csi_IM_ResourceElementPattern = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern)); - imres0->csi_IM_ResourceElementPattern->present = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1; - imres0->csi_IM_ResourceElementPattern->choice.pattern1 = calloc(1,sizeof(*imres0->csi_IM_ResourceElementPattern->choice.pattern1)); - imres0->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 = NR_CSI_IM_Resource__csi_IM_ResourceElementPattern__pattern1__subcarrierLocation_p1_s4; - imres0->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1 = 6; - imres0->freqBand = calloc(1,sizeof(*imres0->freqBand)); - imres0->freqBand->startingRB = 0; - imres0->freqBand->nrofRBs = 108; - imres0->periodicityAndOffset = calloc(1,sizeof(*imres0->periodicityAndOffset)); - imres0->periodicityAndOffset->present = NR_CSI_ResourcePeriodicityAndOffset_PR_slots320; - imres0->periodicityAndOffset->choice.slots320 = 0; - ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceToAddModList->list,imres0); - csi_MeasConfig->csi_IM_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_IM_ResourceSetToAddModList)); - NR_CSI_IM_ResourceSet_t *imset0 = calloc(1,sizeof(*imset0)); - imset0->csi_IM_ResourceSetId = 0; - NR_CSI_IM_ResourceId_t *res0 = calloc(1,sizeof(*res0)); - *res0 = 0; - ASN_SEQUENCE_ADD(&imset0->csi_IM_Resources,res0); - ASN_SEQUENCE_ADD(&csi_MeasConfig->csi_IM_ResourceSetToAddModList->list,imset0); - } - else { - csi_MeasConfig->csi_IM_ResourceToAddModList = NULL; - csi_MeasConfig->csi_IM_ResourceSetToAddModList = NULL; - } - csi_MeasConfig->csi_IM_ResourceToReleaseList = NULL; - csi_MeasConfig->csi_IM_ResourceSetToReleaseList = NULL; - - config_csirs(scc, csi_MeasConfig, uid, pdsch_AntennaPorts,curr_bwp,configuration->do_CSIRS); + config_csirs(scc, csi_MeasConfig, uid, pdsch_AntennaPorts, curr_bwp, configuration->do_CSIRS); + config_csiim(configuration->do_CSIRS, pdsch_AntennaPorts, curr_bwp, csi_MeasConfig); csi_MeasConfig->csi_SSB_ResourceSetToAddModList = calloc(1,sizeof(*csi_MeasConfig->csi_SSB_ResourceSetToAddModList)); csi_MeasConfig->csi_SSB_ResourceSetToReleaseList = NULL; From 02d49ed3b63de016a5b53ed572cf31a2617042ff Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Fri, 6 May 2022 11:56:11 +0100 Subject: [PATCH 204/233] Fixes after merge --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 6 +++--- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 10 +++++----- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 8 ++++---- openair2/RRC/NR/rrc_gNB_reconfig.c | 1 + 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index caecc6be43..9c3e142e0a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1183,7 +1183,7 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_SearchSpace_t *ss = (bwp||bwpd) ? sched_ctrl->search_space : gNB_mac->sched_ctrlCommon->search_space; - const int bwpid = bwp ? bwp->bwp_Id : 0; + const int bwp_id = bwp ? bwp->bwp_Id : 0; const int coresetid = (bwp||bwpd) ? sched_ctrl->coreset->controlResourceSetId : gNB_mac->sched_ctrlCommon->coreset->controlResourceSetId; /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it */ @@ -1197,7 +1197,7 @@ void nr_schedule_ue_spec(module_id_t module_id, dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu)); dl_req->nPDUs += 1; pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; - LOG_D(NR_MAC,"Trying to configure DL pdcch for UE %d, bwp %d, cs %d\n",UE_id,bwpid,coresetid); + LOG_D(NR_MAC,"Trying to configure DL pdcch for UE %d, bwp %d, cs %d\n",UE_id,bwp_id,coresetid); NR_ControlResourceSet_t *coreset = (bwp||bwpd)? sched_ctrl->coreset:gNB_mac->sched_ctrlCommon->coreset; nr_configure_pdcch(pdcch_pdu, coreset, genericParameters, &sched_ctrl->sched_pdcch); gNB_mac->pdcch_pdu_idx[CC_id][coresetid] = pdcch_pdu; @@ -1362,7 +1362,7 @@ void nr_schedule_ue_spec(module_id_t module_id, dci_format, rnti_type, pdsch_pdu->BWPSize, - bwp? bwp->bwp_Id : 0, + bwp ? bwp->bwp_Id : 0, gNB_mac->cset0_bwp_size); LOG_D(NR_MAC, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index dc98b84750..2897a86c31 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -875,7 +875,8 @@ int checkTargetSSBInTCIStates_pdcchConfig(int ssb_index_t, int Mod_idP, int UE_i int nb_tci_states = CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdsch_Config->choice.setup->tci_StatesToAddModList->list.count; NR_TCI_State_t *tci =NULL; NR_TCI_StateId_t *tci_id = NULL; - int bwp_id = 1; //TODO + NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; + const int bwp_id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; NR_ControlResourceSet_t *coreset = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]; int i; @@ -1249,7 +1250,7 @@ void evaluate_cqi_report(uint8_t *payload, sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_2tb = temp_cqi; LOG_D(MAC,"Wide-band CQI for the second TB %d\n", temp_cqi); } - sched_ctrl->set_mcs = TRUE; + sched_ctrl->set_mcs = true; } @@ -1711,11 +1712,10 @@ int nr_acknack_scheduling(int mod_id, NR_SearchSpace__searchSpaceType_PR ss_type = (is_common==0 && (sched_ctrl->active_bwp || ubwpd)) ? NR_SearchSpace__searchSpaceType_PR_ue_Specific: NR_SearchSpace__searchSpaceType_PR_common; uint8_t pdsch_to_harq_feedback[8]; - int bwp_Id = 0; - if (sched_ctrl->active_ubwp) bwp_Id = sched_ctrl->active_ubwp->bwp_Id; + const int bwp_id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; int max_fb_time = 0; - get_pdsch_to_harq_feedback(mod_id, UE_id, bwp_Id, ss_type, &max_fb_time, pdsch_to_harq_feedback); + get_pdsch_to_harq_feedback(mod_id, UE_id, bwp_id, ss_type, &max_fb_time, pdsch_to_harq_feedback); LOG_D(NR_MAC, "In %s: 1b. DL %4d.%2d, UL_ACK %4d.%2d, DAI_C %d\n", __FUNCTION__, frame,slot,pucch->frame,pucch->ul_slot,pucch->dai_c); /* there is a HARQ. Check whether we can use it for this ACKNACK */ diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 7331cae4f0..ff6ae976b1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1476,7 +1476,7 @@ bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t if (tda < 0) return false; int K2 = get_K2(scc, scc_sib1, sched_ctrl->active_ubwp, tda, mu); - const int sched_frame = (frame + (slot + K2 >= nr_slots_per_frame[mu])) % 1024; + const int sched_frame = (frame + (slot + K2 >= nr_slots_per_frame[mu])) & 1023; const int sched_slot = (slot + K2) % nr_slots_per_frame[mu]; if (!is_xlsch_in_slot(nr_mac->ulsch_slot_bitmap[sched_slot / 64], sched_slot)) @@ -1871,7 +1871,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) /* look up the PDCCH PDU for this BWP and CORESET. If it does not exist, * create it */ - const int bwpid = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; + const int bwp_id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; NR_SearchSpace_t *ss = (sched_ctrl->active_bwp || ubwpd) ? sched_ctrl->search_space: RC.nrmac[module_id]->sched_ctrlCommon->search_space; NR_ControlResourceSet_t *coreset = (sched_ctrl->active_bwp || ubwpd) ? sched_ctrl->coreset: RC.nrmac[module_id]->sched_ctrlCommon->coreset; const int coresetid = coreset->controlResourceSetId; @@ -1927,7 +1927,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) ps->time_domain_allocation, UE_info->UE_sched_ctrl[UE_id].tpc0, n_ubwp, - bwpid); + bwp_id); fill_dci_pdu_rel15(scc, cg, dci_pdu, @@ -1935,7 +1935,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) ps->dci_format, rnti_types[0], pusch_pdu->bwp_size, - bwpid, + bwp_id, nr_mac->cset0_bwp_size); memset(sched_pusch, 0, sizeof(*sched_pusch)); diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 0c03ec76a1..cc23810f60 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -310,6 +310,7 @@ void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Dedicated->pdsch_Config->choice.setup->rbg_Size=NR_PDSCH_Config__rbg_Size_config1; set_dl_mcs_table(bwp->bwp_Common->genericParameters.subcarrierSpacing, configuration->force_256qam_off ? NULL : uecap, + secondaryCellGroup->spCellConfig, bwp->bwp_Dedicated, servingcellconfigcommon); bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI = calloc(1,sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->maxNrofCodeWordsScheduledByDCI)); From 04db6ef536d9c89c66b738be19c3bb4a357a5da0 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 6 May 2022 12:21:05 +0200 Subject: [PATCH 205/233] Update CI bench images --- doc/TESTBenches.md | 76 ++---------------- .../4g-faraday-bench.pdf | Bin 0 -> 53134 bytes .../4g-faraday-bench.png | Bin 0 -> 92391 bytes .../4g-faraday-bench.tex | 59 ++++++++++++++ .../5g-nsa-faraday-bench.pdf | Bin 0 -> 337513 bytes .../5g-nsa-faraday-bench.png | Bin 0 -> 89436 bytes .../5g-nsa-faraday-bench.tex | 55 +++++++++++++ .../5g-ota-bench.pdf | Bin 0 -> 232494 bytes .../5g-ota-bench.png | Bin 0 -> 90027 bytes .../5g-ota-bench.tex | 63 +++++++++++++++ doc/testbenches_doc_resources/antenna.pdf | Bin 0 -> 4886 bytes doc/testbenches_doc_resources/b200-mini.png | Bin 0 -> 238906 bytes doc/testbenches_doc_resources/b210.jpg | Bin 0 -> 32877 bytes doc/testbenches_doc_resources/n310.png | Bin 0 -> 120716 bytes doc/testbenches_doc_resources/phone.pdf | Bin 0 -> 4358 bytes doc/testbenches_doc_resources/quectel.png | Bin 0 -> 88455 bytes doc/testbenches_doc_resources/server.pdf | Bin 0 -> 4366 bytes doc/testbenches_doc_resources/x310.jpg | Bin 0 -> 11188 bytes 18 files changed, 185 insertions(+), 68 deletions(-) create mode 100644 doc/testbenches_doc_resources/4g-faraday-bench.pdf create mode 100644 doc/testbenches_doc_resources/4g-faraday-bench.png create mode 100644 doc/testbenches_doc_resources/4g-faraday-bench.tex create mode 100644 doc/testbenches_doc_resources/5g-nsa-faraday-bench.pdf create mode 100644 doc/testbenches_doc_resources/5g-nsa-faraday-bench.png create mode 100644 doc/testbenches_doc_resources/5g-nsa-faraday-bench.tex create mode 100644 doc/testbenches_doc_resources/5g-ota-bench.pdf create mode 100644 doc/testbenches_doc_resources/5g-ota-bench.png create mode 100644 doc/testbenches_doc_resources/5g-ota-bench.tex create mode 100644 doc/testbenches_doc_resources/antenna.pdf create mode 100644 doc/testbenches_doc_resources/b200-mini.png create mode 100644 doc/testbenches_doc_resources/b210.jpg create mode 100644 doc/testbenches_doc_resources/n310.png create mode 100644 doc/testbenches_doc_resources/phone.pdf create mode 100644 doc/testbenches_doc_resources/quectel.png create mode 100644 doc/testbenches_doc_resources/server.pdf create mode 100644 doc/testbenches_doc_resources/x310.jpg diff --git a/doc/TESTBenches.md b/doc/TESTBenches.md index fc05299110..547fd5a23b 100644 --- a/doc/TESTBenches.md +++ b/doc/TESTBenches.md @@ -45,29 +45,9 @@ Note: The available resources, and their current usage, is indicated here: Note: obelix and porcepix are both used in the OTA testbench and the 5G NSA/Faraday Cage testbench! -``` - asterix - ┌─────┐ ┌────┐ - │ ├────────┤N300│ - │ │ └────┘ - └─┬───┘ -porcepix │ -┌─────┐ │ nrmodule2 -│ ├───────────┘ ┌───────┐ ┌─────┐ -│ │ │Quectel├─────────┤ │ -└──┬─┬┘ obelix ┌────┐ └───────┘ │ │ - │ │ ┌─────┐ │N300│ └─────┘ - │ └──────────┤ ├────────┴────┘ - │ │ │ - │ └─────┴────────┬────┐ - │ │X300│ - │ └────┘ - │ UEsimbox - │ ┌─────┐ - └────────────┤ │ ┌────┐ - │ ├────────┤SDR │ - └─────┘ └────┘ -``` +![5G OTA Testbench](testbenches_doc_resources/5g-ota-bench.png) + +[PDF version](testbenches_doc_resources/5g-ota-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/5g-ota-bench.tex) if you want to modify to reflect your setup ### 5G NSA/Faraday Cage Testbench @@ -76,58 +56,18 @@ porcepix │ Note: obelix and porcepix are both used in the OTA testbench and the 5G NSA/Faraday Cage testbench! -[Proper image to be followed up] +![5G NSA/Faraday Cage Testbench](testbenches_doc_resources/5g-nsa-faraday-bench.png) -``` - obelix - ┌─────┐ - │ ├─────B7────────────────────┐ - │ │ Faraday Cage │ - └┬────┘ ┌──────────────┼───┐ - │ │ │ │ - │ │ Y │ -porcepix │ │ │ -┌─────┐ │ │ │ idefix -│ ├──────┘ │ Y──┼────┐ ┌─────┐ -│ │ │ Y--│----├───────┐ │ │ -└────┬┘ │ Y──┼────┤Quectel├──────┤ │ - │ │ Y--│----└───────┘ └─────┘ - │ │ Y │ - │ nepes │ │ │ - │ ┌─────┐ └────────────┼─────┘ - │ │ │ │ - └──────┤ ├─────B78─────────────────┘ - └─────┘ -``` +[PDF version](testbenches_doc_resources/5g-nsa-faraday-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/5g-nsa-faraday-bench.tex) if you want to modify to reflect your setup ### 4G Testbench(es) **Purpose**: 4G/LTE testbenches -[Proper image to be followed up] -``` - hutch - ┌─────┐ - │ ├─────B7────────────────────┐ - ┌──────┤ │ Faraday Cage │ - │ └─────┘ ┌──────────────┼───┐ - │ │ │ │ - │ │ Y │ -nano │ │ │ -┌────┴┐ USB ┌──┼─UE │ carabe -│ ├─────────────────────┤ │ │ ┌─────┐ -│ │ └──┼─UE │ │ │ -└───┬─┘ │ Y──┼───────B7UE──┤ │ - │ │ │ └─────┘ - │ │ Y │ - │ starsky │ │ │ - │ ┌─────┐ └────────────┼─────┘ - └───────┤ │ │ - │ ├──┬───────B40────────────┘ - └─────┘ │ - Y (sniffer) -``` +![4G Faraday Cage Testbench](testbenches_doc_resources/4g-faraday-bench.png) + +[PDF version](testbenches_doc_resources/4g-faraday-bench.pdf) | [LaTeX/TikZ version](testbenches_doc_resources/4g-faraday-bench.tex) if you want to modify to reflect your setup ## Pipelines diff --git a/doc/testbenches_doc_resources/4g-faraday-bench.pdf b/doc/testbenches_doc_resources/4g-faraday-bench.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7e31789e9acb9a3cc05719d98304966708925ff9 GIT binary patch literal 53134 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 ZI_Wz&xj6uFW@Q3yKEsldipq<@{x9A38&d!P literal 0 HcmV?d00001 diff --git a/doc/testbenches_doc_resources/4g-faraday-bench.png b/doc/testbenches_doc_resources/4g-faraday-bench.png new file mode 100644 index 0000000000000000000000000000000000000000..c69065a369c3f94b3e8a856b892f6b3bed2b42df GIT binary patch literal 92391 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

h0&06N?X9REx=xt1C&ZPLaVB>*KpD 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 literal 0 HcmV?d00001 diff --git a/doc/testbenches_doc_resources/4g-faraday-bench.tex b/doc/testbenches_doc_resources/4g-faraday-bench.tex new file mode 100644 index 0000000000..6071c89947 --- /dev/null +++ b/doc/testbenches_doc_resources/4g-faraday-bench.tex @@ -0,0 +1,59 @@ +\documentclass{standalone} + +\usepackage{tikz} +\usetikzlibrary{backgrounds, positioning, shapes.symbols} +\usepackage{helvet} +\renewcommand*{\rmdefault}{\sfdefault} + +\begin{document} +\begin{tikzpicture} + [ + font=\footnotesize, + faraday/.style={minimum size=3cm, draw, dashed}, + duplexer/.style={draw,fill=white}, + ] + + \node[faraday, label={[anchor=south west]above left:Faraday cage}] (faraday) {}; + + \node[above left=0cm and 2.8cm of faraday, label=above:hutch] (hutch) + {\includegraphics[width=1.2cm]{server}}; + \node[right=0.3cm of hutch, label=above:B210] (b210h) + {\includegraphics[width=1.2cm]{b210}} edge (hutch); + \node[below left=0.35cm of faraday.north east] (anto) + {\includegraphics[width=0.3cm]{antenna}}; + \draw (b210h) -| node [pos=0.25, duplexer] {B7} (anto); + + \node[below left=0cm and 2.8cm of faraday, label=above:starsky] (starsky) + {\includegraphics[width=1.2cm]{server}}; + \node[right=0.3cm of starsky, label=above:B210] (b210s) + {\includegraphics[width=1.2cm]{b210}} edge (starsky); + \draw (hutch) -- (b210h); + \node[above left=0.35cm of faraday.south east] (antn) + {\includegraphics[width=0.3cm]{antenna}}; + \draw (b210s) -| node [pos=0.35, duplexer] {B40} (antn); + \node[below right=-0.2cm and 0.8cm of b210s] (antn2) + {\includegraphics[width=0.3cm]{antenna}}; + \draw (b210s) -| (antn2); + + \node[left=5cm of faraday, label=above:nano] (nano) + {\includegraphics[width=1.2cm]{server}}; + \draw (hutch) -- (nano); + \draw (starsky) -- (nano); + \node[above right=0.0cm and 0.35cm of faraday.west] (phone1) + {\includegraphics[height=0.5cm]{phone}}; + \node[below right=0.0cm and 0.35cm of faraday.west] (phone2) + {\includegraphics[height=0.5cm]{phone}}; + \draw (nano) -- node[above] {USB/adb} +(5cm,0) |- (phone1); + \draw (nano) -- +(5cm,0) |- (phone2); + + \node[right=1.5cm of faraday, label=above:B210] (b210c) + {\includegraphics[width=1.2cm]{b210}}; + \node[left=.5cm of faraday.east] (antc) + {\includegraphics[width=0.3cm]{antenna}}; + \draw (b210c) -- node [pos=0.35, duplexer] {B7UE} (antc); + \node[right=0.7cm of b210c, label=above:carabe] (carabe) + {\includegraphics[width=1.2cm]{server}} + edge (b210c); + +\end{tikzpicture} +\end{document} diff --git a/doc/testbenches_doc_resources/5g-nsa-faraday-bench.pdf b/doc/testbenches_doc_resources/5g-nsa-faraday-bench.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f6d960c1a257651b538b404f8f019438d3014f15 GIT binary patch literal 337513 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�P#Ab?f8M>rnq?n9rvn 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�EUbc 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ξ9jO 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 z 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

    SvCyo3%tBgZ@e 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�co^0J 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�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%0D%sqj=-Kn=?=3kz@z+nwCt^ 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

    vh4ehB$&?ymC@{5J~(+oo1-;TWB{07$j*UR>Bk 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 S(m*YAP~e%1va*GvCFDN?3v0Ll literal 0 HcmV?d00001 diff --git a/doc/testbenches_doc_resources/b200-mini.png b/doc/testbenches_doc_resources/b200-mini.png new file mode 100644 index 0000000000000000000000000000000000000000..0355e45f1837b3ca5129566cb524af070ee06c5d GIT binary patch literal 238906 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

    xQ>J8$D zU&WJ8KNZN4GBFyj!?fo~(u6pUIoRv6+aEALH_OK6HuJMH?CuVjRJp7vBHef%NH~#4 zJBM7hBk^R5Sot>Po24Ki=gk(kY_&CC$*4mo{o10N>=H49vqeIYA4OrokW zCfO_OQ4-<2CpD%qO=_YCJi&gc_WAY!5em&|Ai`?3=N?zL(HhT{wN)`P3WJB&k_x8w zLEeH=NmJ+=Cty#tFKuiOfvEEcgUZAS3AFYi*EK=Phn#pI>vfu`ufMTM379lyvNhz; zk)wfAb$ct)7fQTtH+8>IPu;-tFL>;Wef`CLcJxx>?vp3a@Na(kmzhk)?Ck7tYkiF? z*KW}7_qllK0@tr!o>}+kbyR*e`Ys{p~>F*8!Lu?G+^!jh2@UVMplm2jk2F~1l z4+oDNC5{uya!Ri^gY}+AUiUg~-MB_~Zl3XOpP9LNnUuYdUAz^~WgGU$UV*E`971g5 zBxOmN2=AP%LZgVPvUp#K^<;2p(jjTUBoRt`;>r^n2^xqK@#0l+gfDGFz^X|=)_d7Y z)fvcTg|$|aX^aqwtQQE#JIi!h5Sxh6V93Dl;JstFzaxjF(Uj$cB*`#QMz@tR9`zY) zuCj3OxCE9sfeLLUtkwNmJg+1+RNz_jyjX0Hm=EaMz(c9VgWT6QixPu|x>(mxY^`t< zh)EiNL`PhI?rCX|sv)1dG@$YduQZcsiPDHPD~iZxIt;%2G{sh*?X68d`?)W2e<#7X z+PFiL^mT#%(#2+HFQ_+M!}7|>bc~jLm@O+X2BUp* zla~P^wt={%t;2Y5L7He#A!|G|1d$59@HFv4lEe5;0Buk0pgonsk zA7FH>6!nde`Sab4GeC*!0uX^@S3IZLrWgFkhpZy_y`Zf$R6F-ZWTN)<;??; zi5QJWvI3~{zC+<3PbajKjE$WkS+7epDgrB9CZgur3t`$ zrLuU>7<~jEU_`c1sNmPqg8um8d@tx;|Zm$I5fAwn{KZ0@S}@7 zv$?^MwLWQ*Pyi~@=&cc*-4QAdxeu!L5|Ale>%Bs%fHVzz2|v{*DIgfnVamFT)nW^){lGP z6u!TZ?|IbLh}v?tk#*FS!(-PIInayUO(|m$|lei(mYO zU*PDm6YQ*SNY=E9D24;FB%!EEjER_xhU7)b%_|qVpdnCdr!F$E#3qza~}L+O@L!uji_eu2vu&j%?VncIoH})e z*wJ)U&Sk=7^`<86TtiAHto2WsDLP_MC^{LUsY z(Y#kp(UXZu8l+KGaQDlO@tG&S#PuCRG~J~vD&n~r(ln;bCq$hVlhJ_T=8oisSC)nO z1=gp13TKI(BBLUopW)!@Ey~#$-ZpZ4@C~n~9FAa`Llp6jUut{#1f$O=P~(&IbTa&DnTr)x3b&zdw!UevLxEztqXFLhFeZK$}gBX|au}^)Hr_Vjj%E3bz;MV#rxyPKRH9t!?&A4#>0&D#p=4WR) zcJwF~NEQ6S@4uI=-99tjE|;%dWp`(X?%W*ShlzY`m7^z4?lUPJ(l4RROUlGjO>(xj zw~3sg7blcO&NsaB6->*L*7i0lhYpM0)p>LzMAL%@SLpBdC2u@3vf7JMNe4tx7CA+! z$kIfJy~a?KWrzfp0$Fo1BCQ!K&ub4JG0HP`m-r|12T{mV$hqQ4B4{4Yu*+5egbr0G)tgZ0Q;(UcM2{td#X)F-8vvT`uO<5MrW6)r^|7>_FG4QnzQx{1sb9e9u~A^ET7t@`X$M^6}I3ItO`r?Hn5|#S>3o#O*6wcuk}n-Ptxf+oO8;#Vb!@ zgmIi#C0S&|NW%GmSj*nFvKH$rbfSr8W^qPXs)U`X3n3lKghJ;7@an~oMhAjRJIff4 zCJ;%JRKG%JYxP{c=MC3h@SN9L)CHJ5w>6YTIoSpKCV<1EVjUhEY*CG3xY?)OZqrIz z!pK!om~iAe_1+W3abS&C!Oqq;VlpjgwKIymq}^_jR~4~|vDOhsh9r*J+Sv`N1^MkKlQDS9 z#%!D2l_e%g%IU>9uFe=DqXosmp!1x=Up+@RYlSl_6qrULwO>Y8uhvvkn3bm~d3rCR zfM(m)5VifD*-*$4@YV+?dr$pPH?sS4$7?bxIwX$QC=06D=Ns$6+93DxjVaLH#>?-| z1PP5nP#uIKE{<~h{3P(q&L0Svo%~X(>o*{d>9ks`t*`UwYwyF}M$+{{c>9IZc3r4n zNE&nFRH`-djH(+Mci&-{@363ovbV@@xBkdpNC)mFhBd#KTWIE;>mY> zFHfC&hF84u5gz}OkMYD4UuNyvRsO4=_;F62xrgoTTU@z%2~5QO4?V~Im!+cO-e<0g6Bs#DTk)A|&+(=t;cF)k(8trAOsFcyPL{Hg%y1)Z z@yRE@#PP*L9J=ESPhLDnq$89rWHR(nDMzFY{auM{wNBUfIaw-l7+oPLSY<`%oA80; zRfoQ$S%OParlZ})u}eFFV4c7*s0b8MD29R25DVjVGM-2=v=*fWUGdh3Yf)h={8uF-fxS(S1v)UQ!M zRlN12QH0luEQ`r2ab8QJgh9WLRj@R-fZg1c-Knm3n+mtz93#M>)_P8!yhEm4tr%@@ zFtnQ4pp_>`mX8v13HnZ!o;R25FlO6vxw) zNNFZjiO(m*4?e)9vxm9=;s!p78WS2-o75<2*0c}zdQh3DI&7onv(!b|H#u#=^DrQZ zjjP69NK5Tpri1B_SD^_2%CbTSduQ*#YZ)jkjyx}z76nyk0x^oEth1+_&~lINZMpX3 zU3IhJ!|U2#Tut4e_}Y&g+N#*+;23dpV;z#1g*4^D^{ez|=bz_hwtr%Np)_lMajge~ z+cCZOPv$Q+1iu(uUjNrQ_g632f7w6IIbQSFW4z|E$Da4K-~3O%$@bP3=gyy})oO9( z%o)y{Im18x@t@?s`H80_5t2$Dqu{5MUKv0bf z6wU@|h1jMl6LvMFBW|a1$YwFFkPAt6&@u(7U^-tpi?2jVQ&ko1nHeVizD!+_5o4La z^z|MlMNSgBYa0#>69sEeC%gBWB&5(BTsg#eFvM0Rt+wJH{nP)QcYNRXa`@08{^wu% zm(0vA@H-#;W4td(j6&H8@5gl7v&1H%mu09ZVfnx!QI;|-aw4mVTP2a@hH51w>JBJ@X-d!cdh~xC)^gx6U_vE)~colYBzb zPD3LWlT{PEj^vOEdDU8J*&SLR_K+eu@u85aeMCoNVhUSftO=4mjc^#{*lVO#Zcmoz zjSj`yH>9uqM#|pW5PlOT1y^D7(6neJ(S%8iEiEdFP`<)C0zu4Uaa5HYBvC+H#1CGf zy-4p&D3B5mVkz?}-Bt^*jK>qQc1DpGv|E`xPZCiTRq(W~0w2IO`d1gCMaR<-BTMU%PBWQ0vM41lawbKNEh2tZzVhsIWN}P^W0Du-?S#Mame+G|rbkueIOo`%dVcQ{A7?V0 zkVUZ|FLj=~52#lCn%&+K+=S1W1`$vV=SPQcU0&=y&z5vFb(BfWd}i1g^l5jx&nwWk zn|f<6gZ&fo{z?0VOz;{^q&*n0vvPn_uX!0=6f37s@Y&1fY0WR-h66hJqU>agf|cz)Z{Bw3 z^@`2mgpVAX<-_-$p*1df)#K-INgN1Ss)_qFT2Yp^F-_1)izK5GmbA0JNk$SEI0s${ zY0YTnPoIQNi~Pc>-1tGq?6vR`jE=C*3X~m56rzcO#b`JHobq^Un}VZtB>U>9))doB z08VS-I@{OP9D;D&xRCC!PqwCNf{$oo1l945Vk#DpMjMnec&zyPSCvT7Q~>CW!D1U2 zzN*T7{_vs|kJ02sK~-7OC}vs|^m;RtwjzxaQ7}}NIF86CIVO&TPf`seRms(oB~MjZ z;@B|S9b=59tSSy4IYv>I!r^ctNjki^L|%q$@L&|830n1Fja!>Ap}b@pOz)io$!cBxX954DqS6WT6T6&XJ@M`FP5Wjaw+C zSeTs&*OqToGG2jcXN<}b`hk17k(frW9GRG6u){r1Uq!Vu{A4QkhL;_$^0m2=52U_2 z=GF%?mTF+%6V zEavTqX8U#9xBIsjlZxFA8o!+^Ec&ZL;eXj;{ZhF63kx|wS(bmU+c3uPU2p$eeAnCm z7L(DKcfb3$`NI!=kjFp!8Pax}UU!BlHe^wPi8VS(h*U@)iXyVuP=@`nb(W$OiI3Ng zsw#-$M95~|HBq9@xv+mRVkc=7#>sxAZZuUatY4?Kd`~10q6<5@c9w>NUv3tqG}^?N zBxX9vWp8dP(pHz=+&q&(pQ$dmc72W2wVN!=FS5F|&56S+Jp0@^iRCOSq9o$z>gYeV8oHWQT1uv2IZf$EYMCU0URmcb;Hy z^#*P<*SwQrze6h2z|J?g}R-L+sXwNfI{@ zsY42=cdjwZsCAUyI;PWHqSd#VGpm2!4?JWdyYe=&F<6B5*rhmI4=|<=K|Dj zoAd$i9V#$N>%+j-1~XFKhLoXTj z0lldi$GR~{7dGDEqep{*Nyal5uJVKb#Sin)**h9$_u||f0Ebv^Xhtz`{KUKXr~l$- z=(an=z%&%9Q}^7(bI(4*t+jRj>v#RPj9!p}G}yXH+?q#3UhFiT0q zV9=-A&iKNGr@8mkS-PDLyMqxsyStn{eTFBV_#$zZhQJ*O{KzLcMKKlhFOGwWN{^lX zt~693OTKiLGA~(LT3|HEg$Nm%80Q@MWX#I)GG8iF*d1^X&-VO`81mSPbTB4Yo+wUf z4@W2!($wr;W`MWSbXxECt_L6%_a6=|Em7fl)2m;@+U5qSiRiRDeBklNiPEHjpzkA(WV7MAed@r5U!<%`cg!0^d?)Yx!FK}ih0lGC4}at%eCG3?<5QpfQ~IM3^NWkk zK1LaXwNmsWrHJAP9mT|PEaZ+XBdRKN6k+0+Bx_-ep{z=5QKDjlD+|08%SwsKlMn`9 z20KS54qz$-H8h7&@ai^^5q311qN*gpER8A3f+&uuD#hHw0^>oSG)pO`IUjiM`?+-P zG9I|+zI!=#U&HzmWi3gp>GrzJ%(iLw=9ulwQ%om#@5$mQm z5J&(71_R8BS$d{>ruXjJtG?yln?LTY>PEAB26Qy%;0&g>s;=dG@B6;b^LvsMB^*sF zj6wn>X?1$UvG=I^D~I&SjSi99qZ~`xSFaN&<#)bu*u5_Am11UhRh(lh%3Oq!#uOOFe}kaoj` zeCXT|2EkA{A4o%{jT=KtNtR_u;h;G#49BM;`2$U^T^y*D&g!W1Y#7LcuwI+Nmc`3Y zk?@m?fknr7MX*uI(f>)RZH|f(zdR$n%iw+f|p(4&Ry3Ho_MvIe24s+|y0#V>ZTDi46a{M?~uHWE&&pylV zf9-38VK^kkS%J6y`E)MdmG_z*<~j+*LQEkFY4*EBDq_4^<%`dMl^^*lKhELfXK1yX z!vkTULpFOItQ1sBWi~eJLv6;E;B*^Sc>rx028D=WScn-0=jD_`xdly0y5u4vD}<mex6I0FY?V7U#8RVP^ylC3P^M9e`!s(onvjC++^r}Mvg`p z7oZVF1%k*$;SKhKaU7$Rq!2|ep0QYQ7xB;uM6fRAQKzX_0Eh1X5d_{%7AW5dses5r zmN^D?6e;!}Jjk6}^TdTBDh$|c)amzoocrd>{Ph3wkLafv{eF)|qe;^3VReQG0+LK1 zWJagmW8$D7@6-w6g4?ZSiL+q%Zf|gbih|H*j~D4{zr**jjE;aNQH78@%{D*ttG|wn z3r-M|CS>&nMPVsbMv&Ul3wlIJMlK8$p-2lwifawpl?q{@K)a|ou(^Q-x}_4aF}!&C zD4ae@qtjvS{1rAXT;kOGKfo7`k3gV!>Wk;8F0~xH-up;}g|weKc*#IjY8^^5N!^~X zn`A>~u^ia_q_ZAbV~KhxvQ)xgUFuv2BK0!@(X*4ro-?Lg4IU7$EWt0U!(`l1`689J!2n6=FSwEf7u> zA0SXcNRm3=|KRaao>(`iL&^urRlWRm0M)0*s}g0Ng+$zXte|)V+4C>z;eO z$p$|B$Mz`3R<5bkY8*LqaM*oW0bT$4-+8qWQn0qRjxgPApUvrZQmj%0vomZon$%le zI#@==$GAB^M;ka;s&M(*IU3C_h3eRl*B}E)z0vjumBFtkSY6*BkdnpaRX+_ftgddd zv|_wIXP`zN0J7b4s8qma+95nJ&1NBBMif}xSR>{zJqfnaCWs@_C?IGy={#_VTcahW zR$KI!?(pD)5AvneWe%?YsHt6RVEh~xO(jxaj}R} z(oI2(AxjNUKKKx?U%Eo5wgk5W1^6K3Z#aqcWU?>_un1=MPtmx$Kw#lkqt0V*e>an} zN9eU%?j2;^UcPd0lEweLKt(7fM@MPYo1Pi4HToXJiCbemnQ{9$&Td5s`$KhTEc>P> zhalQ+zfZ0OQ5ay8K4X*nhwItBM&kEFi|+i~;QO@JZ}MI5rVQWNo$s`O{i%x1?$=fC zRNC!k65mTbx7!%rTKlE~4lpsfkB@)+<9z(%A0K|K)9LWd7hmG))yuqo+V9iv^w8EYGFIc0pZElS z{xAJyUOj)FAdE<5-wB+w^W54TiZKM$B2iets>pTufU*WFozgk*`M$Y7kv4i=0x5j- zuXJxr>308C3u@I-VioZ6%P;W0N1x&&@BI!w^_4$DG&>|opFlc_9HB%fn$c>3wOWZ| zi<;3=6#~Orq&boq`dAL!x=l@h4HUW79I5T2K7D|rhmIn(<>Q+f8)1sM>4ISfQI!Tu zILPQWxFX|E{U$ft*yrAgC*3$q%7LfUsr#c!0fSSUV;uU>c*}>d7Gu1J4e6jVQu<SDj3)5vZj{q=2w_RHoWO%%gx_fouAb6E%C&WvS>X?v!sZw+h8!3nwK2nR z2KRtEF1hsI6L{gLHpVIVtszY^EKsdfsJB{#QAn%;x}6SdjScpVA0A#K0*{Owq;m~| zQw9PeZQ;a;BU^sxypIaXL#xr7lD=TrsS783=K9T>oPFRFqty}efw*|v_t|YZ64EQg zuU);unUe>7k1oi&8*H@u6suJ(Ubx7MH{Kw*_9BIk{5i&R%f!?ahsMUZeCaan$_OWC zk8$bsOFo@Tpu)f-UGeXK-yKQ^h8d`(rBL1m=tE=tb*@q_GC48Mt@%4Nx@|%wsT@8+ z9zpx$NqR3`ATAWBd%toa%gMYb@$iwuoE;fw^SQ5aERFc7pZ-SxT>hQkq$?CbDCkxS zR8V}^#4#TJ&?5l+^gsPs&P*TXyFdB?zFvHV{WC}S&Ch?9AaGPZ>(pot_+<1pobi^r zU-(d>LA+?LHOFRW**`JKowZf6BqdK$P>TBH8|?e=cd|bmb(;{KBcc#pi1_y}KSwon zMY#~gbkjaj7;U8p4#jT+kN1`+&iI4z;Giw>P8VSi#VJ2u3E5a&zz9iLEU?+yWP1O8 zvMk$k54~G~-ODpQ_!-+r+S`M7cK*!!b@I|*FaA^u_O}x;cCX^R+fshF2XE(}EX%%y zL{lgfc<*<-kN1AZ``+}m_4+2)-?+wW7cOx5@nyFVGCE!(j!RC_o13B1 zJk>dwhd~53cCZd_{n6Q05y_Gq0d)Hbw%Z4tBUM0_XKoj3xBRh#r!$OWKi_lRywUCz zUyNhsZqBi`xJVF&(Cw0CIZ2W^zON97NRj7;Qf-QIb<~~CgrL!F5d6kgG*evImfAE$GO<)(YU!l z?a)DTEH*RPJVQSAFn9YcesJm#E6sJZ(dc9QNpwzObPQT`%7KG@BsyoNx{ph9*Lc?> z&tRH8%=#viLU6I&#}r&J3nq8FJgW&+NUk&2iTSQ9&2p@;gess?F4Af{#=9$2?j+so z^oWWvL7-6n0;erVA54lD8d>df$DP_3FIjU=1Xr8~)xp`^G=tZr@2;)06&dSy?sfo3 zb;33w1bR@cmGa~?mxiXL6EjNVs*ct<##7z;-8x3+1TrMkIbk5tMi2!KFk+1+v(C@$ zywYwrZ!N=4+97x~$|`r^)lRHt2MKOUdRm;%Jh2)vDC}ZD-ZCy!#52*Ae^2TxMQb-! zADh~TM3DD0iq(jAv&q9}PZGujAGc{-R4rR%H@1oNPy(A7Y6CyC&kRt-GTV>*Z`w?5 zE8G!q<;rCimsfe_9q-v1aBsW(sBPafa0@Li&GY!#)5Al{-K9IIC??4>o_X{URzt=5 zRE<@gb8z|qQ5e#$H>gB0lOpGIwZPX~9je8`R_fV+2jUMUgHf&=jE)DSu5IjVvF+mC zXtahguOp)s@>>g>sa05-Zlkn8ja1xg&g&v(zrdNbIx19Nf$dlZ z%eD^K4#^uu8uY!{P`E{>~%~lU1B-5ic z)^FZm`ryfLp`7hCDc*}QzP;EF>{#0i{7yUA&U&>Q2KCz-iQkXHa=&7IFGlLU>|nd0 z-tKqXyM1kzWy5UK?GHqyQsr$=Kh4{oetHM3tVW=hp38y!!fU)YsRj zjgKR$m0>P(+Up?$IiwQxyM3n|*X}eLdhe}_ofW50C=4&;66-`m%3TnxF^C|5JV)o& zlZ^s0n=v^x$*+Inll)Kr;2-kY&wtjTSB<7JIzpE8Fsa5yma*|MVilo{b(SL)lJ$E; zr80w;xw3*%q2RPXv7%A06G`}MKlArkTV3J(k3P-q)&}co0xBSq0fmWicNj5-f|AT0 zI?Nlr9-A8*l&clAfI!6r#h7=$dX2@gNuD|TAYXj>MIIHJFFx@A&n~PGOw`=YHqY2d zdxSI7O!Qj(>Xmc6``9T)&pg1Fzy2r0gT1M>2<5~{Qi#qn%3tXC1SG;Z=CZ z<{nt5d^(o3KDQg?#`NA+gZ6?nAO9(=V=B9vrN9W1Orye(cB}0!+O?74uG@M+kCKBN zZ$++i!omL8xImLQj44-3bUQtYVG$)H-Chr*Vr*=TcB=zI(Cc>@85t$dHT`~wUud)u1WbBrj&$wX`$7g3*U0r71Il_RiI>0e5-+@Vo{xUyJzT$i!*!TS zxo&YFV)d_O*I9`nY9k{gX`hMdGpzPAG9fWh zi1i08l_a#SVPd__RL^p|5TGW ztwjIKG1jHv=P$fMIS$FwoL4VgVKhm&JU2%W2L3U3hvGMG-(oiHv$nZ8%==(VmN`_n z4=sDfQma(Cdi5$Q3K^dmgT=cj1F{hF&IisiGd@8e1=r{2DaQp?It{K}zvYCW#!xCc zCb}FOVP$oNaa_vTfG#;8Kw7sv_k}U{fg*yyINRl9CH7^{aodvAqJ?B+q|Ex}1}D!{ zzr}*J7fbwp;UK%Ed~NTrcWX%Z3J|$3OZ;9t=X=ffcPq$mWz4_4*JkN(|U2h6Vmy*Yxc!DS1_O@@iPnR!UroOSx#Vc26HR{aWzQw}gA~$c|Anhjv zBV~{Q_^e_fh4ax$NfxID1{T5O zma+WJzPk!Zz=Ltb)bROGL$z$u z&)i#521p-xuaqPTBht);RwPO0g@0z7IMkAB!;#}h_~29TVt!+ZtMdye=|omiNQ6hV zTH}l9paTk&j~^Z2L6mo4xr?ecPQ7itVpZb0N9)=0gE&BskV6CkfzU3V&}fVbfE?vR zQLKN>Sm7cUF^0@)G}_@Ku>?X6N9%5iJU9UQVrX#(6sd@#qI;ct&vL)lCypYFfx0Pi_7zb#*kJ^M2#k* zSmI6~IaDffV0@g#W}Wc(VX|}Q_}&lyAji&}B5k$#$#bt^l{1c+$)8mhxf1ZQ;_p`LJ$}^8W!iyI#(r&kiVizHs8O_YT{Sd?~cblA; zevHoQI;AK?g(0<4#pP!kOS{uWo1AVhp;E0boCOqZ_RV#=5=o0 znrHsb9r{U+ez!-b+ePclD|0zXBqe$&WqDhiqwDV4yGy`E-UR- zX_6C$ij9p8Oq%f>A9@!{%XbM&RZxK&Nre*O$EFS};~)l=+6dqH+8^^1A9{>KB}I3w zPNf)gsH)gEF1gW$)aJhEIcSK%(%Nj08cSR)Gg2%wF*e5S#6SL(&vCQfWjamB(iF6})mck#rA-nls-+^WZV#c|E-DBk zN2?PCB||i3E9~Cn1S&=;w;LaHx&sVio@Pvp)mW{svb?tD_iO=DiD8I~6oXK4cd!wG z-(wpfR35nHQr@g|>TGArF@*RLy7IOYDTYZ%!;z?u@iaKUzgCjK<4GM_*1jo&+9VmN z(P#{D7}4u>h(zFuwGc=l$&BOi3+bNMKq>HCX=@EpA);UJF)}hjx8J2$hzWv#R;x=~ zD7gG&A&BDw?N*0cZIm!Jv^#y*sRKzrBMe-~ixPq~PhFslu>{HihdQ0MvtJ>Ii$#oe ziejB*jE#=bOxuhVO9uWcO(u}E%7PD&|CLe!@TQ8lXJX1q# zY*IUXn*PEf-3O1r`8UY344ql3l@iB~AEZ1!$qN@>Lv(X``y*ze8Vipe;t>mDg#sTb zj&dp7q*pFbo?Bqn1YCXWAr`LPqyiwiKcmYYjTLL_x^cUVMpCvFM;RfY64gA3TdT zmg@_5X!bjUJ{v2Y&Y{~+P(eUHO}MkM!I`5+$Z|s%g)FVFvbkBOTr6_#^~;n-%2aEk z^!o`WkVv8U;x}F-%QEz6g_{d^shDjD-~kOC9#T5fye5+meUgP<9kv9sixg(l4c2wW|u70=q%?w zfA$AifAvMW-8y;h#&JOy5yv5ff>Z`{laxZS#P9v1}o? zPgVJWx1Zu4eD)kNh_KQ)P=Pf>am1aqb^hr2uW@4X03Y}>--(riE2}HCdp)L0RVVhc z4k9zSIBEoy(8AOVq-_|fQcp6p3eYMgQv(*X!UPg)-46P|{s}H!yXAsbfTY*=Ar_F@ z%nL~@sqd;&AE7x2KT+OKZ-gMpQcfN_Lb)=6jEcN;^`iT{2ppSI47ARJ>|O8SmM-Ry zL02eUTAZ~wtfbaXC?+kbF$CI@Nry}oeq=7K7>=)99e2gv5!ti{c{%umu`ZD=2nA`9 zxjnXahlC&q9C^(@vT2?Z1Zvn}hf2AF%HV{KqF!$hs*rkP(>WxB0|jYg335fIGm?JK zCwNKL*EhV3LeuTLop_)E`h8EZ@?-w34%!e&Nw@3N)lm=ytr65FCn=1M z4EN-6XeaZwG=DhSewFHUyKY?S4+&Xre7FQOI~`JQeX~*`bWW*MB=~aA6r|cm}flm{`bn=-w*BMx8olqO z=)0HU{eA?Sd+ivue@tsln(qBL3LyyNm@tkRtezwLc@R+FZ*~B|rR%rch|T%}B&8Fv2*(B&e!n@m z;wv>w>2;@j>oB5eo)85QUw!QyDsr)paS*x*M3xUJTUH71+5vZ+QN#Q{VO$3;GL4j= zO@@`BV=+4sIe`-7V(_N2L#vBsD=fij4=h0-F$OZD38nI4BQXqj5lYhScM($3Yc?1u zR}osf3wx5Fm9ypz!IXg6EzpBkl~WsG{;kM^wU zVXUSn&>6pmu({#X!D)i>IAl!-1$~cnl@jWWCO0l#BFl0<{9k>LrRC+V!Yn1NBw=Ch z7ROE<2Ow5k@2Q+Y>e1GLvH-mB$~lf5nDz>B_sYFJe-~MpAPg1#Bz2s4FQ`g$&B3WD z8l5)9l4D+<43Bd|_c$@Pj5up!IXoioNK;bjTl?6vwy9_-}|Yb0^mP<^~=0&w98}&1G+Zn;GHJ_ z_{aYN0Kf97-(cbPJpbcg{TqDs#XsT9JKn)R`PE-yZ0roEUAnw_QxSvh6I`o9P*Fqqx$%l!m*yNz1*;>iv^Haa@W&;H!c z@!~75(Cu`1X3FTp|zr3D7g86=0<6xcXMp+HCTNm6*p>#=AcB{Vd$Mg_bGL zcVezDuQPw=6$0O7x)#}h)=n*NEn%^UN4Beee&Ay4P+rV7G;G~?`nJqh*MWf>BsBJjdQJ#C?l~x1Vc!R z=_lAQAdpUh9SEls7e4*NJrC}FN?|<wZ_N88qX9r zWOfkfG5Ecmn?cDCiygWQ3?Kt{*fAzY+rjJI5U7A|KcO~SVPj*RLx&EsvDu(hjOq1L zdfgr))v`~H^6Y38y5JLUQ%idZ{eB-XjE>b@gs5>r2wB>vP@EwS3Z8ZD!#;cf3O+NO zK@oQWP3)VVa=S+9b;rrJeVqNXT2XGfk-oNs@AF zY0+8Z2N~Z|((QE#C0xES$4Irx%*kUc&E3F+A@c{vICA|C<&iGmfA(=Y3Pwf1-3o6c9e(t?KgJu)b>8*Vvjih$YFK2NajV;8 zzPU*`F0mL0l(tyqNIW7Gj4szX8cwqCnp4% zLt3`{+8_M^kDolng}lw`Xo-J!^<`dOp66)VB`w9&F5ckSnWM}do8@3D;fW{S#=rX2 zUu3FS271In$aCjk^{jL?B-RaZp~7qhyJ%}E3pFg(09WSb-228lF@eoACmuL8{QRBu zHAJ~cv6uKMjn9k^73DPZhg~~N1aqd1Ek?Q=M&0I2PEQOa?KWAX)6bA`KzDPEeNzWX z{d!>Vp1K!HeEW`ix3TyA#^$^EulGs?yKjfRTjAYVcy_k$+dsZnk=!lO>#YdW?pJ*8 zrL@}(0k_-dcT@en)o9WCb?5imvG28q-|OcW3I)FQwXgA8zxz9cK%>#-@Ug>uY2msL zoN!4zS~I%c^;Q;ty)f=) zSZ^r_yagrK)`yBX&4QAH_|QRau>1L0n);+Bjm>k4g)-V2lnRhKclX@nXeklK#Uu_q zzSbwj4Wku}%b5;T$btP+G#U-c#WG#dci|C9Mi3Whb~?lb2MCfPrC2HwdR>A*poK-5 z0g6N+C1g6sn0z=PDi(_LJ&Lo_>oT=(iZpO)`B3<5dMU|`7p-}TxEw@D=9aO@tF?tB zYp+uq8~5@vxOnarCijh_wc*Me*BlFca0mlb6jB?l5vqVzufs2Y;#Vof1?tTP(_=OI zX-+TCoej-Gu~=eqY=TH8!vj)3=@F>Xup5)oJ-!11iu8pjOH*>KIdN!~ULZ03ly0qz zUS4BdMXV`JD}tocMQTBuYFc@pq6)a3_IY)q&U2sq6;|ixdHii}XJ-EtwQ`liafSJd zSGc{eLb%psu{=sFCGl8|M%pJ&HADfn*Bed?1`#SoNk(ojbE4U$U5aT~!|Mz4%vi;# zu)-IfdydIkji)9@X%+(hWa$>azdT0;$%vA?{Muy*Bd%|5l00^n#n&!z=dC# z^?Fof=zRIwaO9!0oV#{~YYTJC95~2@YgZ}MJBUD$3+=Qby_7=FgLVc1Bn%=v*_OlY z(W_RFCJ6-luqHwXSXo}g%9vnegtXmp*SI~_vfG7VJCUQiO?~B#=HXtoXeR}2ukX7j zIOSd=>w6Wv-Bfe;Qfl9C+yV|^mvKm_GFQWxnrd=!Seb9d(OZ{R`auRul)P3wW?uE2S< zw}9a|TFC%mHE8Sn>mF`mJpWir2$kEpyTgnniVC#aZN^4wV6BhJ6huOiT8qMvTi4M_ z;X+VcV2yTkHDfR$_b67&#>OVbS{9a;h@%Ld=OkVf)ai6w7z)l4-tVV0noR=bXliH+ zfjjV6>ke6Inh>anH0@I^mxtgPp#oaGJ zjilYCG(ADEbeE&Y&Tu17IMwK|xx7UA*hyp*vQaE>AW3OO1->yBGnsZdeC!0vt1FZX z1uk5@O7i$A(yJHg?t7YW?k20G?2ihROJ$}jN2%4N8W%9uVL>Nqqr7(cihC^zXsC2ewai1_Y)TjWZJuCvJ9jpPg8PZ$#p(VY0R>mPOs+<#n#as zg>);Sg~e57_D@qRmq@6RrzxelK;B+syf!|x|K6{rY+wKGh11@?2EAA9+KD8+S92MB zJEE(-vafgUo44diaPVcd+896a zlm7!&K$4|oy*}1h;_)hBvqLr*_4*-{8SG!BbmQDiV}+&E>T=B#IoI7lWrnfVB9ZcR zu)rmZX}e8bJ2+8FC>%dZFYTe1SBXSSf<_rdTPLJOQ!Eq^)=;vJi{0(DIWap0xq(P< z>23pM9b3DH#wtap-659}V{&>~#^HkpNhc=hZ>*CA!ox%?#`yAy$>z zK6T9wJ?;zW2Vr_lmkdOvTKA5#(t#S(Wz(J$! z08J_cLb+9h6kB2{?bidwuNmwh?$UUxopc>1(#2pHZAki#8=Y%IwOnDf)f(E|1RAR? zI)Fg9PWSNaG^?v?E*!)sW=&5`vbeZRtu~6amU^>Ar5t0i^txR}Mys?MElTAwY3hJO zVGz2cF9ey+C>4q>5lcAVx)#>4*@bnsI-zK`98yvn&CJ1rges(?UCgGGl6})to(aFT zTpXZdg*!p|@Q-1#HpOy0wBFP=JCsHa4eeJKE?(r!=`(!(cYcdp1bo*AKJX@cR-UAs zI&=_g1(j-<3svQMw;vm5e(#l6c=yD9 zzVPBV=(pQ+?%d}4-upgQKmP~(^z13V@~omfzeW&*bd!XK&z|9HdCGeqeS$ec-v6OL z!#`iV!szW~p4)8k#>Q>VsDNy=LfOB_j1WG7&`n#UF?7o@ojij)B?qU?q~y>D9AZpCxA zj(NAPbm!9itq9QmON!b3IOT6u?LM^oTXx-F?}nPYUy*(*xBce(;dd`~3+*rj5$t%Z zilq|2`}yDEg_mCh8({Lp@t;wwn+(%%p|z9u==QONSjDzh^)qm*D}PH^PparR%D z<*6s1qPvBScE{8}vAndw$KL+|{@v$3L$z4quYTY6@Y03XnHn4C`rI6aN@*xY!U}}W zK!igqXG`?M7^A|L8!RaH% z__fb}mc{xSX{`cBj-nb(uJkght95Ffg!WjCu`I?EyP zm2|U7SqNS)3O4&0vv(UV=67}<&CxQ;-3IAYi8wA0ra5UpA#b)gyxgQZQbna1@R4}`5nqId<&pb4Nv(Q8UCmg4lH-|5os&)@~KVZB*g=`n7j#AJ=l2~LEbATcyHIxsZ=G?|@{>szu;>+j0 zflV~;edH6$NSPE{JREth725f|#iT2MLU2YWfi0Q+b2SSMP9S ze~GzbjrztWV@9KeaQ8>Jv1xSxxAB1jD1``ILX|L<&~wt27(A}hV^{nR(mHyTH6Wx@ zY=^?D%6;i>}Z~C+Ko9m+rOP>UEfz-jB_4M#e`ODOb6>c$ZeM zL#;MKx7(%L?P83f(@hwwji6Kr#&GP&VV0IwC`2`)FeJ$`_V1e@%~J|NNLDIvcX^30 z3^Ce0t`bFNb2K@{s7R;Xank|cC1q)Xm6A@gg|$#`HmHq^d#R5c=3QIs)(}!|@xg@^ zj1~h_Z3glV1V!fWZZJ_P(l(aOUZ0tn1AOMwpP?8jp8M)^{Dq@O$-TG^;Km!*c;KNk z{QB>FhKEj`;Ntab{M+CDG=Jwuewej-i$ zamhocPLuTd>^ppz%xJFOy~CNA1H5wiJl{-Oq(bq@-~BZ6rw_9f7@|wxBq~=(T5B9= zEV5Fl(2QcGv>~^KXP)q~Ug?xhQquP*R^{H4E%Do5P9b&DVW z%SO<_M;tMRx0*55$IMf&w7oo<(;+av4uIdu3CtBZ>+Wl9KS9HDaq#tFj) z^enI6#!8DoA_F&)ba9aeEd_xJTvu*Az~4vHN$+tjl#8gIK}AJo_V4HX;tg)BFQSy< zrE9NI8>_M2Y;bk%I!ZX-`Qh0KXt$tHWwqWy%Yb63KuLyFss-lOR=Cq?@lQYT+x)j5 z`yf|zouuD)q@zK4(_qJC{K3O-K80>pv+%L6Qsmou34;*v@OYdV!;xdh zdHKbcxO-=Vzx(5Vhu*g9Pdm$b`{Pe=ed!L5oITC?%Wv?$C!a(J!N2_EZ*unJDGp7~ zkZDa6INy4nWt={93P}7L)N*BEo`)WKJK7ewa`QSTk00aO&6~ufBB~J6X|=dBcZbgO zC=ZuM`TKwG$M|!<@eBOep)-8svA6T_-~BcI`rALi2jB55AN%yLaI)Lx!3Q4Xx%1!T zu`_3Qx!>f6rVsMXwPnIk^611YUwP>j%9ZjK+@)F~Q3@dp( zB3iWyI?;$cCrAv7S&xfXub^+=Wqoy(pZv9d!=HcmyI4w6M!g8sNJ&~OaBhB%w;wvp z+U5rJPLulDDn*fTnkiyL97-C`iz`cCYU!Yy8LG{tsK%-@$5Z zSnLGFatRN+WxQDAQ@{30tgJNn>wo8ech?e~k!RViW9h+)cYER8`S)IAwC$hYIqKdE z#fn(k14G%=I zJ}Jv7y~Q?9=GMVX3yTT^NVNle7*Bxqic_=`by;K3N}#NJu9cGHB0vr2vx8uVt$#<# zX?l|^#WP-fsKy|S#2v8Gz8fn?aYWkgQ5dPZ@gD+dooZQoKJ|Ki6Co5ynlLl9k8-)p z!ks%SS@uNpDxn;B2q}}b%Y<8HMoaFeiBec6+W@h#S zu)gWwD`DU$XGUwZ&_1!tlH?fzNo{Nl5x9gZDFyvrm+{(Rf*{y>A6j=nw7A5tTsvFW zz|)-@%dvwKxJXR_TfC7+*s%HpZg4l4<6<4kt4i*}vv?fe5RvUG6o>DH9`SLfO=iJqceC)j+ z;PKKF*Opf}JU+qg<|gyY3j{$h?5qriPtD)pBS(*Mb7h71PwnUAlW*focdk%Q64D@` zy3s&Xt7KJ3tl>nd%#)J`Xl<$6i7PakD&V~v0Q+DoX~2ouY7Pzq%%hjPJ}!-Oy2 zy@I`SiD#>0eE2)wN28a~5t2r`!($JgWo~|+&Lbz$VZg6^>G#QcJ*H}7Ozz)Da^@6o z^t+t8GEW@FLk$5I=P=0#e)i|MK6isZ`=kFe)!GlZkA+cO_03Iw={J6jLKL&MvO>4pCF}RmNs6(Cnd3*fa_Lo~ zP&t8g-`K6Lbqsfuz%O3Y-31!4Y zt;+eiHnWG$QtQw0hoAX0=WZ=Besh^?)}o{noWv)NQOXQ?ZyxJ;=;lq2rPE+rI~eVI zc#G$FTW3+l@Bfre@tylam9mOKP;}-a zDXq~6og+fWg_gMZNvsot4aeaA;52ys+W|>igHCgDV+e-P)b2OVeJ8A~OIiE>NPF)v z%d)e)_qW#Cd!KwxEfR z!f?UH#7i(qC|HtF9BDMUr_*$0}2J!9W_o_?llDomZQ_qV?9 zd*9!yq!aQeA@GzUNi?-u6`Qs?)*5${kq%|LRjq*)1isJA+&txCf$`B%I_(aFg9FrR zHKg*$W<2WkCSere35irb?M{#{vP~No?rX3KV&g67*B!{ zj9x4u1RJeTgJVPf=Vds}w_cLTNSyt{l!N&3;x^3B49-uHbK_o0+{vV&^ zfbdwo?>M&y@*HhwblL<6Ye_tXRFb?AtY>^gqrvdt5NnMZN=P!94CgPt#=GA49R#@? zjYch{k_i$Zu2qphP6e1G?lnI~YntsQ-|*B^bixjWe2%ZY_%f$Xo#Jz!{XF0L!4J5@ zpCru**j-n&xm9D&^u8V_r62CPAD*-yt79khs^5yZU(I*j+ESp5NQ<|{>IB=pSd0^m36);_Y4U@OHQA|Y zHrvi;9M>Cotybz~b`0?DZq->#q$|8O)sqQl!SbYZk)&yFZiMp<_aHGgcH?oSd)dG` zRfel{L{6%Tbr+yS2#9e;Qi~-?ViKJIhISYc#UZyB<~exa07|)TRSA_&3lcmOu}+Xu zQLWY;lRfpOD$h%Wc*~MAhKB}OTwWpX z``mZ-EZ1+|AdDP8T1Y{&R&$Z9NkXw$Kmv_=ouQ#&!cLo^kzs}_WlkMA&b9eD*48&u zb~txvXQnsfKq4rV^F6M5z1|>O7{mf)CHaY;{%PL#jo(OlaEO2V@BR(H{O^B(Hcnmd z0oSiyV|im8zve<{l#;A&ZQ?6M)DDrtg;8`f&@C?E?cl*fDI-g9ZFUa445O6+>a7-g z#z&}a)mhuB68HgA`66Hc;G_KJU;inm^98=Jxys^aKSgeMgj@4#{Qe7HVK86jwT)$h zCBZ~J;<8jce&#e^difp>XoYmI$`gP zEw0>i56jnY5KNBKNF3>|Atd7&L1}c7=U#b*!My{l$1Ohj%JV#W&uQlCRs1xTQW#63 zBw-S{)tHoAzjX^Q2-xa0$!Bte7_RIep?Tv5M{lq4`u(FU&Rs`E5zVPR=toX7{K{oY z3oFb{j-oHlK@i+o8~Tz~6p=v^`-);aCh`*wPE2rlW|sM_D(`;JHxZi*?N-Y{hLFUa z4v)U`asKR!f5(}Fhd8`viqF6J0t2NY<3oc~8!cYG^g1^e7HKpZe8ZD(!y3!l`Uao) z_(wT;^f0H+oZ8kz?Ow2#mlrs4@+4ZDzCz=EaFgvtyI-is_QJli^WOQn-E7f!YxKO` z)NcKPJnq(w?$%j!W;u3WyfLZI?R+QmHn@-kxnHd!GsN~P*`>k_ntOjEG#?9V(zy7P5` zyq*P-&0t0f$TaR$0%~a3g-z6(ZtQJcktX$RdNPHZ^v0%+sV$6louCm8nj*Bbk{J(V zf{>YXdgKbLNyop!-ocf2)#O4*7ueysHp4Cv6k2fT8Lxmz^VjXUe0|Y_PGmMx|1qzEvX#GA_2ZR6=V_kjaqE6*+S7AS){?Y_4y* z-zi-Mm{fT$d+Q13q1{s|)0K|jx^{!T`=3Mu4Qu(ypZPEsUpr4Tiui#a{2`j#P_!Bt z94IqBG{nhcM|t_uB{r)y9>4EiZq483p~oI$d25T8E?#mDigeehlZZ;GaOYaoE~s&^ zP{8**CIIR{6~6~YDQB`A+&j&U**V6hCy^2c2-vIgJh!^V%NsTJuh(g6Lv3}P!oEFR zFdBR55)XSt#`1aI_4pHf{<&wF*uS5#`~ZW;Pw`dH)o03*TEmbOwPMsxDO37V#Zw^?H(U*!4r2A{n45}#PU z#mZ2T!wakUc(jW-ZXcY$vxX>)F<4&L8d0yKwiZ#!$L<+p@X}TGZPaK!be5|oa4FVF)%nn zl4>}17TWFi^?u!PKi$*LDei9drfVnMUK6(eyRI4@i?ww}6wvIwMMY05I6UxORlcRh1r62uK{`~L$j_-Ts{ru{W z{s`9>=6Uhz>-_L{|3m(d|L3>aYPAr)Op|ZiPFMIoN(hFFMWT90MYRz55?ALI&~eQ0 z=qURu1MC|qGVqS~@?Zb_54iXA8D6-0fdhNSm|s~zOOVF0r|5wdXavjaHC7|dc-HOJ z+fnR@W!rYg(zp?%85<>+Xxg5m4@u)hT3(uV<;LE|k+ISuB}Ce}TJ3J(-NIfdAUhGQ zC_!y(k*QQ@dJ(x&#dX?9r<|7#W`ar~CpeptvT5X_NqU9FNaOsZiR^miFxq*FMe3`z zvFn&*8b)ArcT|_2*wfcM*19*w52Tf_+>njY8j5}ZpPDEs_ zP7}bL8b0t;PYWW2hpc`+{Mf6pPDA{eM}UkxY-my(^b(6_nLad zj33MDwkis%wlzm<>y~ODS@vP zwPu~KyY~T>R+ibQ*3uP+qhv`d$jX>(c_8hu1Lo&fK}hyYPSR+$*lN}&7xK(5t+Kqa z!Kp)soB&XH#ML^-o0?XqkvjSufOh=mBF4G{btl*6?EV8>zVsRyp)i9bUY@1xv{>~VNl2|Rm!k=O1$ zO6mG}$`>u)asLxM{Y`(L+)#yY`orHqV<3*%stogp_4t+(53&F70ov6XANt5I@tyB} zA455h#d@7v*Ke}2RcGPBQ^d6zXKpNz-8;>x!4c+`7syrX_y%+~Am3`IB303|gKE#m zZ)>cQggRlYROWME{VGQf9AJHE9wh{?eCav-LY~mXC}U{=L8Cs+J@O+QC&E+tOrVwP<8qD!8KJWqJ z#4s{4(o1ICUZl4>pLRB|{Q^LCvqyLH9Qv(7cXGpbir4xzklx*9>bs3^?`~7-|FJ8^ zyuo99MFDpkhs!jj>2ARxyLH~X9T2vEthFYS z$?ymN?;p}>w~68qZ6TY<5%?ME^%`-fgVCD9$M$n`X~kt=2#LTF#)9J~5A(SfU*yv- zzJMnMvr8-d$!9*z-0C_yPWV^<;Xm?|ANptf&@cZI#cbh@s?w^|o?~fOn+)eXHp}}+ zvL&*9!wH6xglKJ+bGO@k|1 z%fz!Y_@0l6+jwM%78dcOQ)e3~dimKV^_^SjDbOWp;D{7=s8?EImHY25ZOpMqtr5;X zrvyEwR~qqXx@>o2kiJ402}V0nqvsO?CXU_2L3ajeEX5snYkq`#RtE5z<4VdqNzWY9LlO zR%lkMXe3@#=bOI%9W1sLt$K}2PGXXTBn&xz{1{prVhh)9-Evko;m!$4fV8rA%&@{b zT&zsM4;)Y&!B(wBsazyUsc!;UUYy6KhYE4WKo7v;!XlN?Vd5wzHclLP`PwaJW^Pk$ zwFq)q2dPO9Ox9XPhbkcQU}_*eZr#3(@B4iE%g;Je7BXeID}3MQxmVBe==~2cG*Dq_ zWto+YHS(D(((~N=SDSRqs>%31%j@f$K5~@N@o7{hAg??otj}wUa~v%eSh;nJK=^dR zh`Lsj);7rIQjN2(deaGGEy8zcc7Bp@a;?hz+n-?V=54;Zu|R3= zI?c;Xkdn!P5l$aF#z&TK^76UMFu%ro^plixMTD^|Eib`Hk)+;cb8D0N`;OtQtZ-mv z743OEFgQxH(p@*4ST0m8b zRE2TZr%CV-4$qqy;%tVt5JZV_0yZg$5<>^g%)%m#W)n|HI#KKjn3T@uC2(4_sw%y%o+cZ;~Zdx{ocBed$*Wp9_!@1{E6ji9#uwA4>Tu=D@5vw(I9H+LhM z^~=hBGhFK3e%|(Rj4=k!^O&ET=L=u{5<)-{M<`FC6GNxd#`7eh5Gdub_visGZf+8! z99=AEn{fZBD`YW@QZ}B)|1j|2toJ@g*)^zk%n;UJ_L|>_eo; z&cTr}uATiRK3|?9s}oM_eT>riFLG9Gu@QPCro~@>>N5nH49!N9_Ew8TN;1ChcD}~2 zr<}*j21x1g(##BjCtW75CG85+jx{A=nRJYtl|Eh}=WG@_EgJUDbJkT>y9%p&7im}N z*^OE>u_4Ll(497cbcM+#78y3vD87_IoURYG6+N*U2$bgvkW-sUOuQ{nMaQmFm8R=b zr(2>)+z%8f>$v-Iceig5=t!fyRH!4|>!7Tl)#{*?MYm!GhlZG2m}6*QkV8j~a{jef zxV<=!ClpALb`SzjC})Y1D&3bW$10bCM4R5|Tngn>^`fg*b&;Ve73m;ArT`jGs`Phr zf0R-*TTL4EDv7a-(cyC+`*k>UA3yTLKfz2=AZ&+tfkzZZOzxW^(HbQM!gnKjtaY!u zQ0bmsq?MZ5O#lEO07*naRFe}IXfmAl0iQ1Ar`8C`166P$MBu4+J>9d&iIgEOq!TOc zJ+P0_ks$_(C0@L6k#;BIiTlp-#QkS^_3AY~_QhwBf#0J63IxTxzuj!cX2S^(1J6Sv z5ITX>>#PMBDfr}>82sj$`DVn$0&_=+pDGYpg~gki+k-~d~dB3Bxl=#+pby*v5gW}Cf; zGyx7F!1E~HUgbco#q8c8W<8(Zxpjdvex7gfE3^wOZZ0hF{^2oZ_l)yw*y68Vf03iW zhu;4TtJN)*BEx~PF)B9U{>?3xW>&G;3_`oGnbCZS3nz=@uU@6>DXxwVGV;nrP?_FZ zN&$)Q!}>Z&Hjgx#D2%9Q+FaT@%w{7X2qUyo2&IUnr6b(m!P2mxl%yjRo{~;wKQTsq zeu=yn@D0!WJ?ixavx^IyyK>Fpc%@rqbmGv-ad1E*ZNW-Oz1<<7&0vki_X8RmTV!&@ zp7MNq5#3%)cUrajjl25=cJ%vi-Hv;=>FRFN+wEWLy%~1%Zn>auB`~8)$=Xerwp*Il z-4Lj6CTQFK_wAkVTOq1#m+rq+c>8Y8&D~yrorjO@r>$bK$nX8p@6l?uNy0W;wHnoC zn?kNYCSO2@E$7!YhT`M^tJh~4E9MCji_wOH%=6cu`#jZ7n?j+$&DCWt%+63O6){@# zi+}QmjFu}rckVUH`2w*uPT^_qL=Jk=5biz8R}13|wrUIuNmVE|#t*W&I?p#2Hi)x1 zK6`nFGe?iG*=Uk|Vyk4qGav6O*OBaHCC1KTDPT z>Cd$(Z7$K8PNNCh;0FPXW|Lf@NW0mhlrM1b$Wbnyd(9m@a0OZ*P^ltaNr{q*m?%}t zDmNABC78L#pQK4-R&?K&?)#(^Jtg%&)ESdmsHcQ54|^K}x-I zG(T-EqeFu!`Az^K^>&B|JWN^~j~_V5X0^)r*eF=b{NfUOM@Cr5c|7Bf^N&984SeWR ze@ZOeV>uVVFPwQ7kxjVL+T`UIFObjY-OXBSl;?5p!Q(7$Y~UviR&TBH_WK{;_WCMQ z!{a=A?qx`BTR9h-PlTDaXfznIz41rYq7pZ&}?($;%zK$dbKKGJv|r; z1mgB>3~)jzjwYJpM-TIfzx@~woIFFawaGOp8OsJB0+!SFmDZY$kgS${>X$CCZ@t0V zJ;#x&D^!G!-iD;G#**X$vh9%0P>~9P;h_zhf-cT-6B z6NugMQHwV%m~X}7eYY@@orQh7+IqKrc|U8_PSoo5ukH8u?dtCBXuE!@@9yWj%-Wrg z!}oo@`oeQuymX#+qssdFI^X)9C;5{fdxp>a{D=6f4}Uk``1n246KF;;?|Se_e&&0> zm&*I!NiFNS%w7rARs%1aLHa)Rb{nJIzDN2#^J{Coa_t(KOyJni-4m%&DIrWCrN`p> z2KjV+s)b-FiJ9Hn;{0ZviBtEJq zCzH!@X=M%B4Ht38V5Cl86XAA>2Jn@{lm}=Yzt7o$j6sGG#n^RyI-4WT=4cy>3?saD z3*-5;g8*49f|Tf3yPcNS=(t0?;VeEnJwSGM=h_&zuNF>ejCLRtPdWyvv?kqetMrC0 zdY!%PTFN|??vuNtS!dz0%2~YJzFDM^gBGphJJ*OwEsoNeMqV63KC zDO2mTX|~!NI)0qe@E{X=_cA&$PN7_)RH;xX6&M&D!OsSiDrJIPj!Yqk=XqqZc|6}E zlg%J}C!kZFhmaEA3sTFNMkg^*9Agsg{uo0Tb%^8GMXYK=u2A6A{g3dI|JUzAW*icY zC|CIKzxsX#%L*d{R+pAIbmSnO@4UqrvkeVvoPs``gxKzNZ4ACjW74c8N;+in&R3h4 zg5UqcKgRb1f=s|`ue?gDy44f(`BHG{wR2p!ag%HFx4C%jrn@O7hOJti;lV+sCdLWF zu*Y(@!mXc<>>cl+YFittv^ufVLTGp0=CfIRsR)2hqs7S3Ak*WMe7?59kY9vAF|)N! z9zl}JQZ@qJXfQr9!Y9tZOg3Eubwz;rd=3nZjt#Q3y2XL%NvdskP}pcyncg!&*l0qz zMAYdpprPiuNs_fT?LbSff%O#HQ&`{SzjwLrQb~mGp*$x*^n8ySGqVho z2bf)6q}FL8GXVy;F~8sr8DUIua*WKx2oo`sH`}Z|c$(FAmHl(;&UvtXBJN3Ao=4s& zl3akSxA1+R=hv3lxbQmd(}%hB=owyn+RG}?B_c__zdrS_fzb7;3VJwUEjs} zeMfoyo-p|awQ3}R#zyxF~75?*|{TZ)bxX5!ay^Ki`v`&buCP^Y}C#2nJp*tO< zaydLQNibnZ5{GP>h|Iz&b4zm!4iAyU$s6v=+l9aV?(aMAj+ zZ^p{LTX(vfXRXgyrYu+cmO9k!Cyst&@tynVH>0NQMwHoYtl#fY@@C;5cl%xL7MHet z=OLxUQ;LuL#((UM-jhW0o`=rx)UhJfmD^ZV;%C0?0sj6I_w(&P``e7J{S{ZA3;4!~ z$5=n{cET`ZY-oUMH?Je56AO(F57TH?>4YJZJ9>t+3&>`(M8kuarDc*RCUfWzjj)Ngw1$xaACuazY>%%ZyI$|@ zJNFd->higT8zVgx*L}4P0%6P8RPm7n80%HY9iXgR#CN>)7sT4#K z62%>|eu06(A%|oY0?+sH{0!DOpK-BVrrl~&7^t8VjqmyRL54=7j+C%(V31a$j#3^` z6q7F$X*KHzCCTUVgq;qLJ|Fnj z?}TuJ{YMV*8^8EN{Nk_vXFmVZ3gu#nc2{{%y1Y(pMAq1JkM3LnUGmxfvC%Gaj-{JR zj8E<-MY(hK>>0vVlcd$=u_qsAXkdVjcHkU?AXhB%u16mQA$al9C5(|AncmCWAGnX1 z`9+?4?Ht)4%@j|ARL}{<(UD%Y*=)3seh!J7!URHc?ba=FnH)3oi%jpGWMz5Ph3I4z z)y*}k)hbm_AhkvNicY7+*x(R1mltWZ8c3xaE*D1`ThC|s%fJ0=h9AD4nZ-Gpp%YsA zzR%6ux6_G(CHKg2T1rq}+2rQL7!$YG!SkF~Tcn))RMSCv9e2m(j-YFMI9MSCG9BrQ zbhKgRosoLA+2~E3R<|}?=$uk0ogfeIXLG#5z*+<8`z+pj9KEv2v6&TQ5TxJd?i&Ju zk&?t(WE9aUDtRwE?i}*86zJa=Xw3c zO-ggC6w=2tduW2%Mw{CF6iJfg4Hn-5?KPBD&47#nKr|oXL`2Ur7&$|`>w*s2kZ9EQ;&*%B=-}x=# z#5h%>F>EwD9304C6HP8C5hx4N^5n5Rum0LU;m>D1o;Wkf*5U$Rw?9jx6%)%Uk3D{r zQYlMqW1X#zVWl#Kmn{${F*8eZ=tR3+W$J5Aqnq)COZf7F0%r3nPyg93@aadt6;s=S z+7@iA^N)^=;+dG$dYe7NWj3o-1_#PqxPBRt4|wd<1I(^2aALGX94DwCpwk)MH16q3#P6qK_CCDP($e6raLK{lY(YLdh;L!+ZaVT|-UfTcVzK%>#1-D;5- zL!;S3c|K7X(dk5vcqbM4LIEi~q?Bay8UFo8{*tq2jxVrXy2V3}uK@;lv)HRwcB3KJF&O!22tm8ndQVjg5JN1>fi z@_oMimFEz}EXOCOSe;*FYpT}V$Ukt53tOv)lX=) zy`k#vpYN8;)%`i$0=@m7-)|qjTN2n#wRlg|)^85quRz|-*L=5K?{<>eZt#=ce9iq5 zQ+A$&b|T`of4<-2+5Z3CDID0XNbmI5^@~2+c^ca(|L^yBwtMcRlq@eV^NIidaeU8- zm-6`>L&GB+ni!<9xk01Tgs=&f3QSCLX}-<}zV$<#dEgLFK0HXSvY#Md=CKLGp7;j) zM+1h!8;p5teBs$=Id<$YQ{!VStu683k<-LB9StK;q)?F5B9q0(%NMcLCBFI6r?`Li z0*9(gyl+@?JlkgD%F8_d-~&`d#QgFm%d5*AKYEmB-u^TP_w8k+zD_269G;R~UD+V# zdFdEAJ^iPUk{%~K-OUBUK}?iLM_S;zW$Wxf&LZt@y4Hg7B&u5gQq{BQ3LHiH-qsdh zs1z{^zVztK-6oivB0D-l7g|Y=G)A<$!YSply#0|!nI0Y{6U7*1xm4ezTyHZ_s_@MZ zejWEkh8wLKpK5II{?WbMfA%!*eey|896!Nuxx%;P${1g=<@p3jN_it~Y`UpC%A-8D zLjBl&HXB>)y)w(<182BO1Szpk^nNeVm4u}DTzA;CA}!KFru$&WwHLzi@~!Nq)XCoA zQ&{Wt6V_5abCAm(-1Fsg3{OmP?w&&o-qm^6Et?W(fDp_-0ffAH?3=@ zcetM|>TWXgyQ#$ASX@)5#$D`VJB#+taq;$I*&SnSA1iEs3_A;Vzj6O=MfGm~Y&W_0 zJ0$G(T=fchACzfVX!i5J@N+2N2O*G3(rmPt9v@B-lM$7{FcJrHysU0|O*+>;NO4;OifKoR2;CC466@ zJRhC7%x6z|#2Z@>Yh;k4vvQkB45L~wws#Me^*J&!!@iLU&wqK5#l;npBt``R8}$tk zlG){T-gf^qxnc#OHRl)C+BNfDjhKn`tUlAG5FjvOI5e2`|fN~>LS9djm&E)Q63t^cG^67^ep9UhRHo+ zwAMDsl#4VQP5$_~FEVQ+|KzEsdFk2>e3j+(i`Qv38wA24vL1y(j)nq-reh+4Y?dIO zbKAfN)Dv9Uw^(|`73{N``}4iMAYy2jY#J~pC=Kl<3m$>nm+1(1sGdz}X9 zT0tAMP-JZfS;#=_<{1kM%RDhO(!>x&B$+H;H(@7b{aY#Us&%HCW6Ym8z`@J2+#@YFAG(K2v-3=@)$tlF zGJf!e^DW#VRC#Cy0BE60r(?ALd#?Un|3b!Z1`X1r<5Lc$h2sSo3cHt)ZqepmQ zVvxyKFEJLT*KB$Xn(nF!CwR2R5E{(~Wr567Pw~g+&+%>dKg4slu5$S8kFvP9KpZ9b zHWi)bGhDfHl?w~E8Ehp8W5^xa%axMvUMJ6|U?QS+!s?YP{EK(JmoSRSx%4!X}HaUnhUjkn414>X^Yyo}5w)WQ*({8zcAdDGJ#fn``U%nZO-jrP!61 zv9rPIm%`OAX=`UkzO%vIEnTwTN_Kl==WXxscAgOKMh4q02Xto^=ynm8Ln6}mYjzg? zod<}oA-<%60z2t2JI5H^Cv^MQ@3zCNN&SIXW7qOL>HVTyB6!XZfD*_%@`F{N=|!Mxj{X$lim@tuCg@VwHv` zK!U(G78y8emXH!JlR+m5fiD>^`a~j2Qx>@J`W148JdJvba;3!T>MDCjM!CJTj6raE zv`p9uDHU?m!|rR8?s){;KnrQHP5sj0t1bM^<_eMi}i7sU8BW6K6{b zBShe*p(>VoA|VN2nyDsOo8gw2H9Me`Pvr6AVaBCrkpKsbz_5SrOZ$q^Xspi z!)r8Ybwb*CftSf5VuREXK{LklB_7&gHIoLpX2)SueV>kR$p=2kK$&_V7)?rOUoqHD z2u;Y=W)&lp6CC9;3=dRTy)i?(8Z$UDOb0O4Ccf{d#63f^*~Dm#rxZgYBM4*ZbVAmb zT8!)&C2m(~fDVkl-s=1o~O`>VQOp)WDcw= zp0y5?m7r!UjZT~I{k{)z^uT^>8VwRB+D&Fs%2`Ih_k8mQ>2x|=xqcIC4fh;8#NyH- zXO11^^_w$zDWhDaWAREh)v2f1+trOt2kc1o72PecO%IA_!!z%En(Hfz1j;ZtRAOdk zfl{0>w=mBS{@=gEg;RSG*XB5G6jv5z38TcN<+VH14@^?4Z!+o!*d$7=b14)x)p0m` zq5|Kea_Ksa`%ZA~7@WL3!^vuk<*89-@0&y?3Cg5n(v)v6w1FhX>V&KicsP(tBkUkM z5uOzI7DV8qtUyEw*aQ(|(1Rtag)I4cn~B%1P^mWQ+;f!Y%LNX;c%A_x(Vpt*IJ%A! zD@0GNZ;ZiMOVSBhd+7p$od!88xfXU1S&z1XL<#CyXWc6$lBwfI8R%$iMN`@{O10qA zyn4fl9Pc2p0|$t2E)STIW%t`V-tLL!ZUQouc4Yk$x%w65e!1KIUX-5C z&hzmzIeaDgy?^(EbUIahVd!)sip8Sq9IZksMKSBfN>LOuGE(3N-hBkm%Tdl{QPR?A zwz&7y2|DXb_}M%QGuP-SC}agz_#|n`XVr-0}(?4ePa4 zGJX;8_`(;SMdkwXL5^#4Gi)@gNaa+eU22HLl2}9A*PJ+Ugu$YRrxRMn!&>+HZK)z# z8%yNJHz4e=ytKg7)HIzW;gipQnMS7#O0uVtM+iyA3wVC+HonS2>b-V5Gf-z6o!6(k0%atdveify_(XlYXji`K|mz3Xa2 zA@C6{TGP62-~A5m=lckJlRgelrQsC=W55kk-z8Nn8Ec+*oH@v=NKx`+9ho1i4I@YysGxl)N`;BwT! zQ;H~x31ZFBscFgSFdBUnQpcx##r|5p8}F}tlVK{Wd)HNb(mM~ z*c^>;@0WHI^Z6HEAgtusiao|lW1L(1Jg0&TASfBb!{=u)STY8h)*UT9<8ss6*#J4e zimjAMS`ofb#9b{%I%yK=^=O1ZD8NDdh?rIdG72haWn@=_5x7!-!x0t3PF`GRVdGS+dG!%NqXp`LD2Vutee7 zJiq?RW$r(7hQE8^1qw0+^=OTfl3aRCD=bNRm~kDnJJ@BL9mFG+Tp{1nS808xj_kyQ zcALrV|9yMc-Q8FBE9^U)$$rq3e!8db=lfa3ynZKvHyerXEQ;Gt0sYWwJ5A(LT8xoz zh@|WX9O>tW-dQaBE<N-*=8Ec1zgWIr`qsG~f4-fg3KI_+O43sJ)N>LA6oINzg-0e+PGBF44J&mU%?QDh1 z8(ZMFiIFsAj-cRk{oo$_S6<=5Cq9A7W=WESC^6^+{7isnn*7(#{0-Jv+EJUslY4pp zQ%~{X|MC%}R8GmP9Kuqiafi*Q&E?G+ljQ;fnHuwp38(LUm=|7tp4)Rb`Iue5F#rG{ z07*naRFCImBj4vpd93oo+623>XE|Bbffo7nz-dI zD3OX|jJ7Vn8q-sb3nLI-nungoCtB-1FD>X)ah$Fg-RIIk4Q)+|!L-;k2GcrEnJdPk zOQBB3g5AA1X`Z$eNb8c!WLG5z7^P620-YA3z(XYNkdPg&kW5cuuU%np7=a4N2#Hlt ztJV16_xuQdap@dcPftzr_>q&my0Czk z&0@5ML~!WTeSGo4>lD{lnLc=g%)w#)k(8DcP(W2uxOx4Fc>`@P?Ulhf$S zO-vA^9&^|2X{4J_8H2GJPix{_mRzSzb9jhivyJd%4*(#$??fR|kWl&vrC`r%*C>q+ zvUK(ot0sZfH3p2uGk}hr$T5oXG9DRR=#7XXgtho|uD3KuVS}Pgm*$!`qjR>@i(@Q)w`X1KYi9t&UjBHzMIAAt?rudwg=zYmEUiJ?*!e`ttKyM{@c962^kvt4JTkmvZ} z1F7f9aCz=JRqgVUQ3_*QwAPl{YVTn}gUaXJ30)~#?GS6A-i+K$*IJ}@1#D(-EIvDIku(?9VKnXA_M(yL$L@X3?B<2!$tvxg2aI5@)h z|C@hFHXE=svq8B~AW95Jhx24I9@-jKY7JUOGhqER{YtvgU6%)(-oPby4sk}in}9=% zniMB#dwVyTcC{Ge_Nm6c!JDdd+M%R{-%D>aNy;QgxFJ;RKB^$X;W z=GlxRY`)0ejZM6$PJU>Rqlb_1(a(OGAjr~AG=8qgzI_Mjpg1xzMtpmnkNl_q#6SL# zA7HRjVsY^{x0jX)ayc-TV7S8G;ZY{W_n_x*I*><_Aau+~*Jy$6gj?Atf(6yic~ZRQ436MKp2+UeW@uj}Pj z-M^>I_hzj|kjbXuG46G=*7CWRUvka^>lF3f_*Dm^(~J&edz3RBwozUljlrPPiIO|q zS-M3WCid*%#g$nyLSco1T%PL>oCKqZft+vzy3E=Z{+eGJbw8}Z+jF0dY5rKPedI~Khbpgur zNz(K&4Jc{&%NwtAb9$IB&RwMN*nMoI$ltcLc-oSSLUPp>`!C%j-wd5D!q4;)uWTCB zq6IzFu5>d0bZX*Wkik-gTC+hXj#7~uOx#{$?c^b9!62>aAy&c`QV52H&+N(~wb~Y~ zdXst*VoNy|ZAfNmg`=xAYMU`CPB>aDQf)M_2!bFWiXtaA)tadLDhi7yloJYeMRchK zLtBd~6j)wdCI~XuX4B?=QO~<2ac$>}cZF=*N8|nMUT-MaI~V5vGwD`$ z%bUJiqRE}Fy#rS1e!P3y*Bd+goqL6ypp|NrU!We&aKN%&sec^eIJn$6P zKK^k^`2w+S;$PWmvZ=z@CcJp*HR61p@A$Vrg_4$HY-w%Q_{m@W_ng>wh-<6!5Qkh| za0RE)c3j@+V%Bx763*RT#Y?e+-5g>!_D?Uvnkyw|D3((TE+(T&1(8R71t7HM#Xi^ROcxEE7qD3dtWID$k-{`0T=Jni`zgcP(aNTXSi{~u@X9c1Tqo_YSx zIbXQ3b3#r8KrjcGq(suPNfCQ1z4Rk=~?i;^w&e=cCxwo6G+n{EQ47C%A&4j2@qLt*#O-_>3mPsAjki=vR@7=@t@*=HvMx)sz&kbdzxH)%|U1K|` zH7~hNF$h>n8{8f5V|{s^ROk344l=E&mdY$F%(A*%V`*-YxLROjWQd8eQPx`xCmOV% zbKNoSdD>lvA@1|bjip?0K{138tBsUWUw@b4&{0x%7~tA>)~(jQi$6asA3Q@*pJkrAT!mW;>G_EUjk3B6SMQHmhTOBv)p+ z=frsutI35xhKiOH>>l62;^+vwca<1QQ&w6HnsbZ1rGJFv?z?!;(W6{U1ICAjdHSUn zyyegpdhNaG+R<#h&O@M;BoYeJjDZE`M6f>7T?)5b?ktjWqh0r{+!W*R-hEs;^9+w4 zx{X&aTz8*2>qTcCRf~0YJ0TRpA8v&Id~_yQ?yxGl@vxiK;rP#KW^#@mIKXPN!HvZQ zKr&G%av%+<4vg^kUweXMef?N0b4Js@YY)3_JILRE>f;Pej8j~xBl^oM_mw!1$J}}J zc9vFFNwb7cf9?wmj|?+4GYiUf^O?~IC4FL@B`0?W3ISmdA#m2!RKTuEk)@eMhQ}r_ zKGVKar#dxbYdz`J`1KMoZ*3ZT)xXVJsqNOWy&C4O7ONLFZL0w7^}N@;TKZe<#kY33 zy$W+joYpHd`kM*+*pAHBOI&xWgU9PV|L*t#$)H|l=$?CEeG*d~A$Z^&aOotbP(tpy z6IH1|tpQ^PiFfQLo;n8y?xb?Zak%^vl*%x68-w*a;q8y`!~1TBnad<~1EoG{c@8?K zZ}>QIp@?l+;vEOjYwM(1JK~lBL_o1zppm3RVc6v@=Sk)|)b4X~e|9H&e20w-xv;Kt zHDN&5Y%m^0k{AoLdiDIn8^1H(ng+syxO!-~Jt(ow`C8D5lq1EVUX;UAaIph#}OFY0mt~H1Y5l z+{Kp&qB7QKusK9AFHO%e zzGH$`v%!toX*95Ve1dXcnKPFz5Gp6wt42loD-nyQPm-+6BUMPQozQP&bdVEYKgqz@ zn4^j&t5dQj!TlJ57kg&>iZ8 zKpG$jxI8@t#u7`(j@{#2xbHBfnFUHgM23JgC2KdheRQ1VW`hGGJNf$g7kIFLkS|=m z%=oMPYMD67Fy$aKDdk;-jHbrCtcgmsc2-E)mbY z&Mq}fTbH+<8%-#^4Ff`EssCWXP&Rb7Ojj>(etnKR!Wc zIcb(UEO9YJAt@9hw3H0SWu})e(BD7Ug{ExoY_|q?Y#v!}RfcaxjcTpC-@jea-+VaO zx{BSrhTU4@I)4wgw&Yvc;oEh$w}O^$ee$>Bc5i1yS8DU(zxN1Y>&a;A&)Iw^*eX`r zZil^{LVi1Js_=WbI1YIAL&mGWiAlq#ins678#yr`qBHnWd+iJr3 zXA#HV0HK1#>)2L<@}axP<|hfssnyn~_VuIFgd|8Ql=|rF+e^J!qup$?-dAKft5 zR3MSs&L+7L)fmSl)Pkp~iC{g_!g#<8bKV6`Ps((Qdh5P^%|6j+4O zZeXk=fw!M2?;EwbMg#%5fc_}JXoHR;q-pc$&N9t3Bh!|8GetxJQn`*RNfVZ=LL13w zF=nROrXU4t?KTD64lMT<1{t_ryY_-Jr2;}ByvNR#LhWOGXB2-uzc#D*T+;)j_ zEm@$j=LiJ^%QvpDT5l65NwqjYW^%rI;Vc)M>)4XpeHRYg#akYF6SG$@@|Dvk>8q5e zRQnkl9!K<-P}MS*r?2sjhaaO^tFy4Y1cCBX@JZ$)7}EsTPVXE-rHI`V2uBBCSQ0O3cqsvpl?$7q4Aqb#9tGH^fSkqj~Y)zK?||yz^b(#V`GrU*Um!?`L9gm{+*ucANgueclC5n;OVx+e|NvDi7!0s0>Q64PQ9vmVGogGezfIQ6^9vI>W-})T@ zTv)rwloGV=KE}D(Y1*ex69+N%RtZl0-BL@9Vi0O7#Db<1d)=o+etT|!&>J@M{W`Tb_%r@ze(SzcH(M{KgionHq5}k zyu!)%+z2`dHZ7Gb#VBH^I>7kI2+y5<5n~MPAZMb~&+bFV_|jLOq}6CJIM~nCRvR70 z4CIFO^%^^Nj+G?ZO%2H~=A7U0dgS z5A?GZ>}4t!jHeeEI(VE?vdr+%hzl_&50Md3my&kYASPs`k@Eir+JP-n4f9LfwO(iM z%sfVUb+vULZ{xd2sT{XhN`XS5rE)YW<9ZYAGooeZRH#CYBC%?UWCyj3U9G6shG znQiB^@)jTb*hh$qMef{tfUr;?>R>4m&}=a_K0%`tF?#$hOrCn0J!3l7HMo*0+#6(&5n4_7eEBL0A(x#gH>P1b3o9P5nul5SEx5? z1aVArX_=@}rX0rvVN9i3Wo&GW=^NMi^S}Np4jny=PSWn!HuK${#OoU@9vcJ5yU<=Uq09ymO!~M@q@cR6O8?SYcO0y-6rzY{ zXP+X@;ITJ8%YhMl&+!EoFGz-advwJ${4SVo42IQ`05`YKiWs#W4Jq7WBpw^DSgPACOIwT#L$ zWT0pW!??Bd^;H=v^;0jkxi~qAQURa*$`cf#h}!BpfAg`w=Y2o^-q)pZcEdgLoR!rE zx0fo;v9g6e)+?oJYvJBpVD+ZtwC#4)Ti3w1!dk!Hq&VpH1A3hbwzErZSJ1a3rfn^F zy;ODE?Uy&_Y2Qk@y`6`8YcBTIQ%|ojj;;TGJ8D{PHB(D^qeo0ANds?g54a`ggHSRjgup77OT zXH;Q-P~<{0;Yb{?OJCsrLd^d0VVXBCGI`-7OSN@Y8wo-P`b#Ay?)@Hae(p5?^gDmR z+kfTXv4ZC7_a0$nb&W#mz$C)6p1bdn@ySKPkQ>>I3 z?vs?NMWmI4VStp1NJdx-#r`5v39NSGX(^R|FE!R!WEgnzmfv*;fe&I1Hu^K+B}-x8 z+7qF?hsW=x1L>~v+JCoe1e8JtR;jPoXt&$cTN%Z2A2y1Jku0t*VT|I&@+=sIGKxZ}%*x6-1kh?V(LqcY z#)M&+wN{g~nUbZ;WICXkv0%?U#n zhbn|12m(y2$+?>+$P&X)aey0>*SYE^55>ZZ0fRsa80&YbRN5h-AP_vreg4pq6J2USus7Z(QfX%>D#@7r_P?@Ql3zVL$FY4B^W6YNOEB)#zpchacXEkdbXNw z$0MY2r+(x2y}}DmjH6*GZ$&Xeu%j4J35%q{PzEjEF)T@Ei7Ac_ zu|xSbPY5^l2t&WO_0OlXACr-PoO1okTRqcMy%Fqv|4r6R@VsQ zGCGK<#s%uF2JOs{H0m^TPCGReOGT8Gv|8(|)$=YgwHg;G6pO5^EYYa9*?<3oP9ZJ@ z`SLWWC{ggStjbT<3=l>k+E{|fdxEX?Tzt_T)3PZYPzVRrz;^un65(H`SSX*;<TfQL)AW>KK}xh(k^NP zIjurW8V0N_F7iu1{fTH<1XLLuVB$q&->my*4WB&-A+)qbwvJpK`5^g$^p$Z=fStW9hxpN43p-hih`6hQcVG(TBBkSx1{hY(Qg2NfYr3WTUaiHejU75$w~ z*m`Nb@;^fbKCoN4$LVph$c|ZFx;?NpphEXtP3E49&fNVNKhZGGbg%20-+2G8@RL9C zBOH75TcEM#4nhK2xg@ia$PZnOSAz>FydFUyg(SCnV`A$&V2wo?H@fW%O7LAZo8UKA z8xHLo^p#xESiDXyTt>a*88+c5`WEiCwjfhwi?ePe1o8 z(^l|wZJygMT;ubxX2<*tJ9h2BN=tirm5F2bu=B1Vo;rQXozuE$Hr}Bi1v)nj3=J~e zH;4!YSvw&Rl55k~85%&QJD|R=_H3)2Qx|YoHyV9AU7B0 z38RoNKm8Pu3Q!78=XKVWm&ghsgBl{Cy6Y6{mGg8O18cea&U@IiXAhN1k^cV5YwCtl zicPSx?$^r7DtWu*Cwv18+GE}LSrZFxJdcd%Vc zzPYGvH?qD}8dfiISg(^rulw$WbnA6Uu+|lq?MC&RU$o5z`ChwBjc`^>l#6|pCBBw=ZF9RAOJ~3K~yY* zwkW%RS((F#2&EKJslc_F8$|IidDf)fTBcg65>Je9Un^nf!YVRUL{ho{3S-GrH!?$l zoLj;eP1mG`|W*&Sa8CzDf`c=FO^ zhT?#}N!bqh^5DEs%C8D@Uu}~mSQgWM93?MF+87&o{5F!#onze|ch`xfN zSg6of4Jbtw(lkL?31kfPRVhR1jUpxBtMIg=ycqX(*Mrc=<%+RZsd;} z?EpCVJ!yx4*Fke?+}ccIeTKO!^j2;ZYrCmi9~m3*k4bX%$~D451*~xc5sxCZJ||ti zCZI-OtfpKpVS%XDKmvtQAC+97tf914qfCGe0tRbM8p}1t#&)0^2~*QIC>3H-tC9ZT ztU9lpZ%B-=>=-DpcO<88V3hgA8=SqqOtDbp;X95nvv8S8sh@TuV=a$ZuQ$j$qB;*3 zl2WjgIj0wJRLDHpBY2+)eS?b%c^#LSaX3 z?w~hL`F`l|J^}%!PCdtyPd$mqTI5;E$Ur|rh7?Oh;$j(v<+dZoC=U%GZG&pxAnU8^ z#N}eo9ro6J^yWWzl;GQ8t9y;dZ)Mxs+GK8?6mK4lcS&d472aMl_t&GhY^V5sy-2ce z_N$lrd@H17^Zjo|!EH^*+Ab)(*NeYdg1=R+-#r-#%d8k@PdE)>KP+5EX9iVLKns?y zk`ocA7Bm(hNg&&jLV5HkQl`9pQqKR=xJ-#?d*&Yh}=4bxG~PE47uDfbrrCgjT zTBE|Ci%T_mMmY+cdb||U7nd+9B2Sy-)=*KF{^}q>AtX?eS}UOxK{+ZhSn;B)N(HSo zZJkpLBvGltU@;(wVpOiFHyRXT$v|H}g-V&60Hre$oibdi62uO%OC;P%A#3}+J`j1> zh>AC+2fFS5-nr8I1B8fN_pNi^iE9uAjE)#AmN<0peg1w!*SV{R8#Bv3X-#;p_J-r# zy_o*T_}El8b~D{WrSL59PG_vWgt=3Y5CWYxsFb2EG^I`81aaOP)g1>*$1=b|;58f_ zV2&}C#ieBjcTtkx|Fi$@8lByv#94|hgqZp|p$F*vw-0}axLl-EC^67q#k5mw$2;z+ ze;ojj&7mMtzWpt4V_d!45=)L~zRhT;=D_aZq>$!`z?yql^=-8_xe@%ydt4{KpRfoUc zuJ=|u)8GiS+j&5rTi)Rren(}aQQD5iE3qnlJqW#*R`Sjz;5AG`;V zwpd(SrLR~*DG5rE+ZMBpY3loKN7kB@Dg}ZdAkPgtH?F%h8;sjL_rxKs2?vKr8%@-X z3GOrjBO_yc_4M=PX^PEUumvhW`;H|Dg6_0fYKIgpl*{ZL+rfSNZsXO-E9~39pOKL< z{@^1Y;_kh-(acjWU7x~e&5p4h)RHz5#jDd-xjJ`)*jVm5b~mfd8VB|q;Pk6!c*{M< z`NGNP2!epL)$$2kZd})y&{`=_Q9#x*BuT=NoqH%o5uZ5mG}Ry?(HW24aUZWtUZJmC zWMpuRXV0Eyuv(=OmpOj_w{Z35G}S_dgS+?e%C)O*va4MOu#qtx0g@<`QV49p{mvSpAW#I_Unl~X zjwOU7REjjqK}y!OV7*~LHwi+8wTd+)H(E`E_A3nKfEz-zy_eiYe=6gWm84YUCP#_D z)mE*MLEv_tI!AUsJKdsaoXAQzu~i%@3<{IAi2_9sC<>9IiUmNf+w>L6*g!E*DKc0Y zzy^|LqlplPlCt#oR}pc95HYR1$-nsz|Bk1geTE-=@Aq-$#0k=@4LYGzE-*Qt5XenB z5>F~Kox*G#^WF-9^vdi`5!##}B?TK}<}OInt<%E1MPXZR#%s(*H*~{ z1g~M{`_G(`BzelCZ+M8`__?3uQ(t+W>K}cW{^JiJrKB8b-v1*%#SZ5+1L61P1io}g@+BqsOYXk@U|_3LZbbSzvCp4ShNK#Izx4kNlLnBobv~E zF!IVp_RcLM`}=5CinO#w%O-V)Hc_~UU?PUcG%lRMBXZ; ztJg_lYa!e&kfYZw{Ph<18~?52OxQkQY-=p%*7(iM>hWG9^IdcjTNYue*j^PAKfYpkxUvAnX* zO1sIyU33 zlM67p>jWYhpp@)htc4Zi?Fk@d^e**BYf@T^H>#-WG<{Cv5xEfzcel?>K+<@sD!* z9d}a-i~PfrpXccl&+))*ck`LAKY{Ya+J%W&A8O*(48nViApn#6QN41ic4INtV$q1) zrK1UN8`7P9wNFIK^Asr~7lWzkMvV%uhSt&`6sSNTGJ%xVgG407s6?Y2U>i+TmZMaF zlFCgVr0`k=0YU6W$7UlW!3uZbH{Py@@KBjT)qPH+plRG^JCG7=$YR?asFvzY3{9tS z7831+RMyrUt;@KGQ=*V?REiDi1-p^29Ka1DSBZMTgt}9ED7Dfg15A=KQ*2Zfu{EA9@-}TDc zBJcmB5AfJuf0W06MbW*f@C+b*! zosm8pbU_bkF`X6HYf3o?DgCbAbZwq4id4J*<~wkw#J9-QU`7Wyy=R0wo;gJn6_|a) zz0{+SN;7ev3n`qqO)88Km_U(d8W{?=E|Lb3S_bCkIX1hD^q*&EBlT;IZq_&5DT>wZ zI=y$-9x@oJpj%Cr>M1)5F~+=B`Mq^a z-V5y2tL|-{?Dq0e_u60IN_@6C`=aMae7mCEs}R3l)Ml@4x>u{;3thVP{HE-%cd3^*JdFHfym~9N@tp{~gvBriqop>XgZu8JbB( zZY+^7tkzp7q0#LY>q7E{b1$=Fe3^xOh6kPPn_WNvAZnbn&FR${F{U6~>(m*|fx z{N&r;%?JPSFHtJQ1_4G$?!D_y_V3(@kd`b>`O=Fok}WMk5MVSUTJz7RohP4V*7q`T;J$D)F^$++*x9VGP@&4!QG^njInZa>7Sm(SBb zG{nuNMd~J_uAqOSpQIL2E>-yKvrqEqUB~&_>F4;V@BBV)t}k+R{w5c1T;cqMR~a1{ z;?zrLsT3kaZdh&CF&IYs2DvnSiT?gU)*H31y+s&-kd|h%P9cg(OpX$YG)oAgm{g~3 zBCDB=qun>g-2FNonj`C7st*uML z69PR@p*4S#($D~AX~n5zeWH?70otcgNx$;$JP+5!YOGX{8)V?s`M8bl2c!fKW;=)^?qgQW7PCjzrXRSO=13yJ>G5 zH+DpzXb1U_9V#6g`&ex))y_D)b5JlLhP+e5oB*o>6tNvjn^SNL1DViVo|@#s)K$VT z^!r@*SlW@|SZSEqvO=#n(1Ac&L1qn0>kTx98_UatelIJ0Q8d=G3{K6PtkUhD~ zGuo|x9gLq?Y+%;dfX#S^4%lvxi?S9%#Vbema^&R8ND*-Df!i6pI?crN0x~y_zUalR zTDf&Ys`D<)NGL@VhA1FHx$pPgy!Nh)4rNen3P*8=3wJm@{mRQ=3^EM3u`u6#-*lSu z-QWEvd1Ham-615GNV5!6E)ixqIi;Qy&0YzRn>+1JRP(J?x7&%#df`U5u6S?d%?@s* zc>A9%>}uaaITl{dTVjtx&z;O0NGsFw?bNk z=y(}5S!$MsQmaG_Igdhu3+RY}ThwtJOpZEkv@4lPoW)j}_ zwtHAxUt{&k8E(7hPRIl=ytK%h?th4}filPLJVbK(t1PZAvf5~{vbfB{hYwR6xRa@c zMfz@ggp(&ulJ3m;^DqA$L1#}BhU^>LL6#=GeEA}e9J`mWT;kcw=lM@R`aWh(pWyjV zeHdxC41&R~?_ z@Wfs&w`-VcKW$-X)SLXoFaJl{YpaN%GKUAoIJW;VFP=Nc;=&U9hela#HK{GFGB!BQ z>5CVL3Iz|W@URvXuU@&pV-FnXsV|%+knRIiDwg@vPkoXo4$+gQi*+nnMcg#n* z-nhyyyz2+KHZxB<&FHW6F+MuNn;&|dU;Lf_3q3f>tJf!aef?ZH^*m7=Q49+l zJ9rx^lzixu|3Ia$>inO9>t*;1 zEK6BgT_s5qthNk}45Nggoo1}9tze8H3?laI+Kmu~G|OltteNq4!GYg9Zl|iRU78^Ff zPGRVD=|=j;Z$Sl$K&TCvnp<>}5peIWJ)BvXByP7s6=^9;kQ#(gE}>e1&;~m;%+k^# zLq@rTFXx*=O37ExTp<;f&zzkiQX$1SKp4xV$tjGm==@otAS4bHQJ~zr<{w}85lJ6l zYuviU69%tap1xkx+ zj&N8EJ+4$@1Q@NUjt}z4ZHF*g(@Ha1S)0=@o+S<(vw9;`yS4M3LXAY+0`fvna-6ZnJlYu!d$v7}qAR=s1RzcKV$N}LTBLi8%y0FCw%)(H-3M8C++;6g=~fQ;b~Lul zX)2qMt-Wkxy<$mUI}LDn%Nuix4D?rNwwh#l3dWMQ8fcv&5M)_G(r$A6q2tuoSNQM~ zSNPyZKf_0V`~T*)N8ZEi)C=smZ6C|4Gko_O9w!i2pni^cC}&q8W>-CDLFWX^7s-~+ zQH=KT51;%LKlHut=J}H+99Gd^d<1ZFX@PgW=e>OViO(=l9^#)*KFzz{@h&bu`xL+Z z>%Yk(Z}>KT@@Id}1qmbxkKOwK|1$k4uFhRYWDZQQ+Gy}I-}z4FuFoNA4GukYFQpK& zo=F#Wpi4o;99H6pZ(egSYMnc%LV`85B@8a@gmo*UM0;lO8tF&{pzb^ z?VQ~^$2t4Td7?tW0XB?93pZY~dCtV}1anJ^v@+-I4W&X`Lm`SW#&pNz2gi3ZzqEku z>*J9k&p1;BH;225P;uFt4 z$#AL6$xCP1F)~4+7;nnxcJFgvT;k2wv=dYB7@l`7Ay&CxFvjj3tEC`O@N z)Thp|)7RW}M@kMoDxCod7mkaDqvP|9__#!zdlQ!JN=qatw_ks8Zj-ykYbj0_DR zg~V9F(#i^AV3aTjC{@bXJf~1B(rDC4(-fTU(w&t}IhoSmn@x1H?hdQmw{dslrmyq_(oeeGlBr zWBYdVXMgm26vBZ1>HrNR_+P&Fhxo(Kewxr|j_uw}J4u+EyGh_Da~&FE=TJenR&-Vh z)@e8b_t^=Aqs5gX3Dses0ff}%m!0k*j>wagLZzQ9&tFrEwie#4e(cS+Z*J(eW>{}G z?(T&m-L5$ID&E02Ta3TiukBRf+eKXVD*CswhTX~^z41iNCf~OD2EhBR=w@3_LAOc- z+m2YX^-b5-(o9pNRMhJYglGC@Sw@zoZaQE!NwWneMOdgWE;75Y$oIehxA=i?dx&5B z(RZ>ow?d%+s|(kWOD8E2!ukRt%V?#F%tEu>qFNr{#o2jYIz7p|zw0|Vaptr;ya;zO z&<5^5dL^?Nzwwim79_5SA zJVg*kj8&^#o0(!@m3dw|^&-Veg!6;y`*hf4V@$H3&xq9syDsym*UBe?RuC1_ibCzOU^u#p_T2m?(NRlQ- z!JCgiM7xuthf(RDIXlC?9!C`5$PjV9X? zV>NY?5h~9$PYtPUv%Z!f%!=#Agg}H6YhY^n2G&AYjJ)N_xI=}KDF1ViJ{MX@W>*)m zSc|OOZmL0!b7Vi6h6%b~G_J){g^0xzvX- z8L8H^T5UoVkt7LLI4`f(IZY4fX*OEK#h8~bUM97!lT$*nyyW(nYxNq!7;^0@t+4K3 zmgbpfRlCnw90UjfHWUUg2+`JZ;f}_nz3#L${m8KN4oT;5747~ITHV9A% zu1w#con=gnk1{!Xjq=zC_l=M7WAA@ zHaahW->=zk1gZ3*ul3p~Y&OepJ&A1>aI$&)E`gO=jVKDKH=ADgX2_B@d7hEwDMlOG zX+k?qXtgtvH0Q{%qbx2ia$wI+KJcNB@Y&~H{*<#o3 zee6AWjMe28vMj@B&B*8wFFySWQij9?)Z0x;O0aigH?x6cyjrDQuOW1fwt`$+qEeA7 zFFsF@x3PIbqn%)-;PUA+^bZa3{onqrB)R6(pZ+W+Ot9y3v*0+8e z?|S@geEiAJQ3xZacn0>5?c~ITS6C_(5alAntv0cCxJnZj5sfwnq@d7hGuqe3%7s_h zRj#sJTcy!zQ4uwgH_=!gz2|;}g;v@kO$=Gqq*$!d*FS)WL)L0F zvLq#KXY}_~&{9&0LfWkssWmiK)`|Pd?CKk&T&1HfiEs>!h7fs+7@r&YnGcxi)MTpN={79vu|_)ou}m5fNikWNEz-Qb1L_w zja8C`c9VSrBb19pgf`6ASCMIkDntzR5AlXO?x4S~%KYM@kARgZr6?8)7^8XU-uo~{ zv%0p%ZF~0f(4>p0c%zT?%k8WV#f#Btf&FGpfkAG}&U6E8I`+QKd+0FkrhEHl9=clrxMw(4l$-qj0W<6KSfv>2 z?`P@kIifgxO~<@>qPlsfy|p&<5{+#)vEADEZBGCCX4lM{3wpr+&#!OBSN(d$d~4_2 zD^I(ZT6}A_-pfP2xj=W+ubW5qo5$I`PEp&9F?v0Q?Q*}}Hwqf9gkltvWEq8Gfo8o) zsZv3#8=9>KNs^GJZIYx-mL@I%Dk^dN9mklNy27p<@n% zlN{K)k2?<^@bo{kI6VWvjhR{g&ma3K{>KwvWTbzP z*`&#UHKcihD3&Od2B z^GmCoyMB>ahHj!^C8?f56eHgB$m8tYyN{3j%O@!ZF^9{yanHUZ{N=xV94Q6EL&F^2 zbAU7F&+_7>b3{>rxZ1~hV}(+#oZ|T6IdW@J7>Y7PMvB!&ld1|BsPNtP*FP|Zd6wOAHwfY*ySPt&q&6?Hx$@{*Cbtd@NZyqM+d6xP9-uGK; z?R{U=t=2BfvgLh`6FZBuk%feigaC7xnE>a4!*VzSGt8VYFvA3f0D(Xt36MBsBTgJI zalFRzCR?^-Yq45Vs~7dYRCn$5t?&8c`>ND#w`7x<qtcel0Qh}yP0zY8S&{kH8d0LyAapT7igOJ_Z zwsU%N5?$9Yq)sLfILalXVuiPiigq1^Ne~Iqv{=a1IGaKm+~|fIK(0Hh{bAB!3-QKyx;OHAuYWEQC{(K037h%lWs1u=owo?)hz z!w}G%Zo$C80q%IX-Vp|Nu6 zG@oCXBauq*H|GvvBuvWli)6|k3CpDBR=Mrk>pAku%k(w1(UNZFRAq&}ExRb?mf3#Y z?O0Zd)5l(e=f z@4Xy+{uz4v`Zzf~jxF_QUnv9_HkY+$n7sFUJowxzy#F_Ui5cCb)zk@>3;gb9zrx$z z{!?rn*~-MRBY3qcx@pnU)yts=zsoy+_80l_{okUqtBb+zehxqVEbsZidzqe?#V`$Y zU5|x70>^O}=FkgP*zLMqWLAnmew&iKA0veEN>}F?Q}OAH4J3{PSZE^4mZC3#hdsH*LR+-~amG z@~J!S;v-kxz-qC~zyHdgv8}s@|L}oN;MFR8?uXxFc43ZBzyD|1)zinlho9n}hacds zeRuNVp*@_No#I=MKSXCb3$7m@Op&hR*r`*vo`+@{WHM=T#Z>|nJ?)*0k;Pg_Bb}PkQ zH{6UDg#7ov{v6$VcaqGex%=A>@#R0i3p7bFACNFD3Wa>ca@HidVv!w#0~~(kD1n5T z{3?eh&L9ngp5|7@!wPD4nW|9O!ay@Ec6D`Aa~w>=!uNfyyy9}2l4%x-#Ykx%hkg)u zXO`{(Hi#(YZKV=rh7%W5B`&0=`7w7Jz3z37dSk7@?~F6eMRYz4w{?0Pjv*Bo zZh#)cF6!f7T}4JiA;blGSF{|DJ?b(_jSJNUp;;Wo+9*N-d=Ue$Z8owXt;q#ds_m4`Icj5$#a|^hxheWVtXbbQ7rB8D3 z>F2N#Hii%^E-#@;fe1p-FLccHy}JrRq!wGn;&a=NJ9gsS2Bo02y((m%QWIE~g=U&uo!CdITBKC2;RYUr0goPeo*?wOV(V^R z92-RjA=mBM&+h(yD#a>Y-R+Fe&N3NTobjeWDf+T)oIZD!q2VEZ@a&^hDm8-Q3fY@? zGJEo6oLZTM#YI#Qa_0v=#_>Z3K{uIMT1NOGzUPt7q?wtWV{v(&Y^IIL=_wq?jVxkP z!!RwX)fx{z^dNn`y=XF0zbXMzNmSsoR4g+xZ1epyc{*F0NpudNPOT7B3cPT9j;`K* zvMHNXQyb_8Ed$%|N_iH~kJB-h}gvvtYn6> z3r|1J?RVV8P*)%GdVF+fD(5ZpubM(|W>5hJcreSLaOAFI<^$#KeUnsgW*+%Cyx4OdR1H;^O^;N7D z%AA@y$F9MCy1TkiC^AhgtSrvcWhPl!&Cy|{sOAd1_{0-*_H>cC>`J8Lb7XdkMAgF~ z#1BIrd;Uc%A#uZiWHJ$}pJQLOg8sHlEX|7kDX*@eq>hX>)v*OEs@fuqva?kX7qc*o ze(ox=!NtS7MmWm}<MPvR@Hh|Va{XDBH z`-A9X5V5LVg_wp#wNwP##+sakL=xK%2t!4GPcLc9!ts2h5@;d>U*YH0c>MVzT>kSP zC1crW?R5Z{cfiP3yT~moq7<4Oi!KX&p!XBWRfYm+q>{w2iNsTrfh2E8pn?wCI}PGw7*<-mmh|p{Ho-?_;@KVAs~2ym;R|Wb7oaCTUM+nWKtSA%5UT zG$<{Kr*u7!*0y#=r^i8oSFJJ7(?hvhqbZpp%;orZ|M(sHlK;Z=ic5b}mZ0YF+{yDy z=Kh}Y>M~19MH03}uvX)DzjHVHN4D|kp~IxI?dXoj%c{_wqYq_ZU3no(7Uqz*UT zavAqN{3C9@W-p7&Gh{Ejl9!%&hAa2(V_|iP=Z>Gm9zBcex(xL6aNWKu={XlR7Uyski1m|iI%lg*Kxt&Y-+JOL8KOO6;vyGMKb*j$J)MXO zwh|x$ik^O>=WMr5SMQ>LZ zD}^<5)5NgtD5OD;q5_RNRZ$82s*7eBjm+IT2+HwXLK!h`t33EtXzg*xuxmS@=@XbR{((Gs~Eicp4)y0|dbGU9mQ#ymv zpebYWksGdL%bxxG{HH(7b+^3*H*hJt4tsC7iAos7Q=Xok#_Db7$%%7RM1bqKOwG@t zLXSi$i4uY>Jw4c#!SvibuJ4mhrQ?GfsJaA1fi3mM1QhK&_KJ<`<+ z!iL?e&gdqT1X5z?I_GC*kXzcge9JIry&^`jgfF18y`58M$7pG7V_|WLs^=nwNwr+U z$Tl<5)5n3`dzfEdK}wCQ_g&7y$}&@PbLc{luuKMfdr3EEDQOyRxky1R5-2cr9jQwk z$BDfCUVtwYzUM_x(u)FGlu)R^qrJE90*ppEc#cn?Mcz%Ev_|cm*Y2jC0q$Wuuk{G6m zZQ2}v`7l>peLb$%&iL3_(y0t-+XA7pWoR2uojHwWnRr^X%Ls#rMJ-}z1fhz8DnwnJ z0YY71GK-j?A%!IL744a37FSk~o<~bhAMY~Cy!*Cm7@b(8t0{wO>Z4`v^<4Y$_tKh5 zvAD2Et-YDQ`S#aICoImdF0$7$IrzXgFcaxWpb`isjywyRj-6=X{M;-~&A~Et2DWTr zZgzqG{sGFa;HvAdqc(bkps9x&-}51smlnD0mfOh{mbm}mBkUj9jiw7yb_!jyIeO+5 zwsddjbKm}_$f~0hHP0itZ8r-m3tYWxKdbp1U8yFT`i96aOp~+|ER4QFqOF(K;XP!s zSx)EHq7V>WL)RoNJzZSBWr+E66Kw74#R~<(G&yqSB)%W=wzu5I$?Y0KPy$zoUiju5PZ}HNf&}fx}}{w58H$wux!m^z_pt&?py+ z+_f!DTP=qgeV#T}`QZ;AqJLzB%EBUF``f?bL%;s(ghrBzt+V>J8#ricB>f_CZkioC zb|9r>eq}iVu6Q1^`87<_gi?i$7P|o)SzDMRKt@C=HWkm| z)a1FiE0^f;`al1axur$CN{vvHyz8CsMAJ3)?c7CMTL+Il{urfV328)G`=*j;dKA9n zpvYP&nmV%4S?havj)!Ggn5K!65~p0n3j;JakDg4$$~sAIa)zC|cadvvp_DIRm}bLk zUO%(zB(9BuT^eX)NphTp9J_f2)!Z>DI!5rXs0j@UQL8oyZN z>1H7<7kkXjM)0raof3o3U#~>p%%5Jj0c`e~Ngqr!k}XB+cpr$|fLh%byH2IHGr5br)>n^8Se zSqV_FdXkuWrQ)pc=1i8+*(sznC}1b(Xu3Vy8DblkcxV`=hLOxttJdghYok!Dk+72t3=T3q zHOYKwm77y7T>s8J9D4LVM)qIFa>c_iEPnl$e}ymK{Z)j3+xB0Lr$SbX1x%q~T7pBb zzDj@Z0Oyw$5t_#0!U6#ydaOEDA|j>j7#w6_eva0b7QXfPkFYaoe*U_*u(-6q{-$oe zV;j8xmUpnPyPr}^I}g8nn4Kd#xwCf*!-2+4X$vt=T zTX%ev8~R6>U0LEc|LL!}dH0q4*1JE%%JK?-eBU>5a;sQUkDw$fvh)ph_mZzvKnaqu zmlibI+uEohdEYhr*tPRo2DT0J2Y>vZIGwN1(bLPdU4#70M}C^)qbK=~fBhG%Ik`5yF4hTr(h zFLHUyFsp?E#cGL{j=xF}g!Fay@W9a*N$tNmLXrDEg<_s!Hq8nyySsb%e?I%?ESJ`} z^Tu1K_#W4;7O43OXZJo%KK>NjhlVM+F7wOF=$giGe;@5_ZT#TjM^K@nf3QEIhiL|5 zQ?m%&jK|5*9#o4**o`!|FwR5g0-`ety{UV(ql4eqb)@HErP5UEe+Ohe%2PB{>Y9`h zeM$(Dj!!L{#we5!QUf6=c-2V#FJ*+448n$BPL~p22y~@lReO{}ABy_9NMmmu)_LA4 zjDm0)QE?X{T*JDJO&1MjdFTZVB3VN@52P%c>G?U1o;*R2v=Kp?`yPIfCjxLsBwz0-+R>apcDqbf%-b9>sG04Yek)|7W+iyyAwo#DvlkT(sHJ5`o5~FSQ z0$&Oqa1MMpgITu7AAO{J)e`3VEuIOQl>U3*1Pf-p>zDjPjRL;|z8TFgH7m zZ5T10IRNF6Oe)^8s|_z$VRCMjOv)w<0!mefQq{pWO){ATl}e3jtrjImB~2EViv0dR z{|R2Pg!SAJ%w!9?g!a}JzWm%%cvAAk?|uti)1%8r=<{zs@KKr*8BU*{#IkL&MuJbi z?Jkm&hbfsw^p-Zp?zx9;olT4%eHK#+1R6?8O3O3UN;#f*=^<8X0fc@G*zoBe9%i_| zpPBO$c(n?trdAe;Q_RoKGB7wu&8xC=%T}H^e272%wcp{7zw#H{e&Aa6?byc?hYw?* z#c0fsL^4T;Liv$EC(s05;L()H($&$0nNDF?29|D7c3p0~=?snQ7)d?|3_a>vCMgi17r`=uu(6vBRoK`~ zt9YDUFIx4~l{&{;#*d+5v@3CNjSv@-%T(;hQ1OL7${kU7p&vnF>ZxaOTAJqvSf>PJrjr-`>JvSmnD@WSC2*t%tqrRAkYI%FUu1_~*($TnGrkO&PS!iJN)P8ZYa6U5lc)|N>`9QeQi zO{Z3J(U4fF6t(FU48w{dR|T6+bJrKxOYtr)mJ7bIk=$5#H^XVxJN8Qjth}j${jafh zy%{^$#@+A53jP~$v@eD5+_=}?YznZkXup0>!9_tP>v!UtMcZkb7A1vw9%0}`C^pl= zb3JrJ#|!+3_$0t}TnyWakeH^1?|4{A3u##R0T{YuCG>gg?gJQ}qEad2cn(Slnz9)* zO;W2>DOW3$t5vE_4XNo3WL}|M!0`ec-@()kTtC3jV0mE)&vR+EQ#>(shDXo&@bL4I zLbbhxj_eB*YYu&Fo&4O@H(;6u&z?GlZI~o9om!%msiqO~K?jv$h1=h8H7`GVkYd@5 z^M^xRr;4qoNu^R03wdg872mcn30SUpwC&o(*!&E3ZjP&V?dBi9{dKl%8D_1#j0gf& z-GFiskkAZDg#s^+jgqjFkqR`Kple{5)sqk537!4>E@yJ`EVi|i$@4R4hQ!njgz~6V ze1w!VXIq$>pQUeL3#Cd4$8|WcdoPbY^CYL9|1nM}$C0@u%C4fPyA8+j2tq}^QsmC- zZs3XMe$1`=ui@46;|N{Dv~8Ai3mAq@p(xJP$nm7|jR{oqUP6Tz)OT@@Id3(vDwDs$H@H}L4GG4{2kSectf35OR>oh6gWP!S<2RAjO# z{_r2aNLwO}W?JOda+J$u>|}yRo;`?Vrl<(P*S_&Be&=_8hw<}cQHHo-(B9e+by8zz zsrogB+J-oM>;&I^^f7c@$2Lt4pBd+wTkH_j@ytzkWFQ1&St4ps)S0BGD5JMz^wurGxO7tKvdOP z&>-rhuUNdPV!D-5AyUZ*^}TL<9C?-l%1MhvxrA$I*o6|h&2f^f8EB}c_DP9kpd@UPqU(d zwp8)+6JbaymEyqeJ)E9C&&t{Y!ZnZy3*Ylmp^u6aN&I3Nx3ife?izPLdyMuY76~`xG9!)D4eVSY0?E|(^y)$sY#78~nD_;)Un&$a z{3cXnTACU*FtAvh86(sU5~fbRRKbcPjHP@%`f{T>T-WuBaZFpXhPu@YLNdCBov=vTDcW0_s8wB%A*n=~Oe#a5pe2>%rHNCl6;^rQ zjkhz<+D@QBOCrlZJ$xTdp@?586I5N2$rL|&`XHUHZOj!4Jn`x=l7S+-XFI+EBbmY$ z@b7=|-|*_mlRR_mBvsEvHzXay!<3yG`f8Dy>tXbC@!h9>Oh<1o^Q)_5iVjjYnOK$Yw=BN(4_~6W zy^X%Xt$gbL_!a*6zx_UiwH!h-$(1UYhQ`G7EJIuRIWu+^O%o*2Y4WuS)ARGpx*>M| zHjbV-Lz9x+dc*a6@he|IXaVz!3rx*SVrwQjCwjx5nVzLNk)ge-n_zm9mQZznaoYo|Vxv{Lz`S^rf=wy6tT!&*u;S;qMT>!}~73 zj+ShSYPEvnxKyiUYHmPVM=N4>0vRfDYipEU7byi@?VW5(%~6y|9v(eP;ove?Zrjaj zewF3PN&eG6{Vk@@n3GdIUYasEUp`%dnFc^7OC1PzIOljxaY~o zNNOfQAAN~*pBV=VhPhY1=I+bN=dN#7$b@oM; zE*X`*ndxPuX2r>Mb?8Sl8n1&+l#Fa!LhATI(AW{zeaxYZ^Q&zOqg26dZzJml5rR_H z$LSY}ucp*%iZ=C~vIZik)89lqC8J?oYD~yvw2Kx&v~nR1T_Fe%x)!a#8lOi4wzaO_!Mc*@mRVtut3q1r)=#hdx?)W>J zT3DQ(S=!Z;9O%Vh>x~?@Y zA7Kzg{&y8!Jmh-LcYLX+^Sv9B^*;Tv)`kTcht;y*~JAV@*~RMBX2rQ)j5ikN&1z-+&-DL4i}jn?d%uw#>tY0x%@pVt8&PZ)Y_fTA-dx}o z#`c|!g?S@XWc_2ChQX1Sj$o%OWHtJ_hOXiH9!e@SUE)?949i0KK@8=HfDx;?C5E;P zqan~WXl?CeeszhYX%cE0mJy|FDG}YStD#4wxYoGIYe242fLwzW2%5wXFHmkg-{Y3O zSD}PJHx0h|)T0b^b}_S>W2mo>@zq6I+S;kKG?Ur6o3-2uxqOaa|KP`PPCte6e9SA} z&S(De&rmAB3j^MN>rH&=JNL40=Qf^QS!75vqa5)3DuX+Bak6!VmXkAlcH$KOXInS^ z_!PT_x3ko?#_r+m1fI`{$|{TbRUVLn)x)RRHnfGjSD~la!r|kq%;#76*zI@l)QOj| zb(_<9hd;Xi$4E0lIn>$K+RleR_DN10KSFo^Al8V6<5cM>d*s&E*nZg_980Hrm%;hb zvmDsBkH4Q8qxIN}SeNZ)_T}TW2m{SBdFsST5-E#a+jjAhEe<6i2r@hQ*1;b|RynDY zFm1*sCmJ|OO_Nk=RrL5~u5^vS_wd~)wQE(=3FFJzVs06QM23E(d9W=|g4&EjI+fzl zBhRDhW)#QT(!oS-F&bTJ3M~)_EkaYOFl5zO#DCSnO4#^eh@G%lnwdj4BuP6-GMT_} zT?{37>BMmox{2et4EA<$ZuUH;Wpe+MPto7ifhh$|dV(z@+X%e?zq^xUGL5cD5|)kJ zwVROW0GGOf4B_9s>)iw@!1q1EP@)?;RoCaY-~ZEShEAXfkJ6AvQP>|Nf%_w6@tM I7*TxYiu>Ovu{7nDFn^eYsIAR?lLgvc=Jp6l%D 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 O0000S`{PTd|i30zD+sTMw!)aQ? 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 db@{|^9|4aNWf literal 0 HcmV?d00001 From 5031649c4766364eba2995e1d12331cc4c19b9e6 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Fri, 6 May 2022 15:57:58 +0100 Subject: [PATCH 206/233] Improvements on BWP switch procedures and harmonize it with RRC processing timer --- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 66 ++++--------------- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 19 ++---- openair2/RRC/NR/L2_nr_interface.c | 5 +- openair2/RRC/NR/nr_rrc_proto.h | 6 +- openair2/RRC/NR/rrc_gNB.c | 13 ++-- 5 files changed, 32 insertions(+), 77 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 5fbfad04d2..0d5755dd71 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2365,9 +2365,8 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *CellG scc, genericParameters, NULL); - sched_ctrl->bwp_switch_info.current_bwp = bwp_id; - sched_ctrl->bwp_switch_info.next_bwp = -1; - sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_INACTIVE; + sched_ctrl->next_dl_bwp_id = -1; + sched_ctrl->next_ul_bwp_id = -1; const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; if (ubwpList) AssertFatal(ubwpList->list.count <= NR_MAX_NUM_BWP, "uplinkBWP_ToAddModList has %d BWP!\n", @@ -2900,52 +2899,6 @@ void nr_mac_update_timers(module_id_t module_id, } } -void nr_bwp_switch(module_id_t module_id, - frame_t frame, - sub_frame_t slot, - int UE_id, - int bwp_id) { - - NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info; - NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - - switch(sched_ctrl->bwp_switch_info.bwp_switch_state) { - - case BWP_SWITCH_TO_START: - LOG_W(NR_MAC,"(%d.%d) [UE_id %d] Schedule BWP switch from bwp_id %ld to %d\n", - frame, slot, UE_id, UE_info->UE_sched_ctrl[UE_id].active_bwp->bwp_Id, bwp_id); - sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_RUNNING; - sched_ctrl->bwp_switch_info.bwp_switch_slot = slot; - nr_mac_rrc_bwp_switch_req(module_id, frame, slot, UE_info->rnti[UE_id], bwp_id); - break; - - case BWP_SWITCH_RUNNING: - // The BWP switching takes at least 10+6 ms (3GPP TS 38.331 Section 12) - // gNB needs time to schedule and send RRCReconfiguration message - // Therefore, we wait for the same slot in next frame (10 ms) before update bwp_id in the gNB - if (sched_ctrl->bwp_switch_info.bwp_switch_slot == slot) { - const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; - const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; - const int bwp_id = servingCellConfig && servingCellConfig->firstActiveDownlinkBWP_Id ? - *servingCellConfig->firstActiveDownlinkBWP_Id : 0; - sched_ctrl->active_bwp = bwpList && bwp_id > 0 ? bwpList->list.array[bwp_id - 1] : NULL; - const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; - const int ubwp_id = servingCellConfig && servingCellConfig->uplinkConfig && servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id ? - *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; - sched_ctrl->active_ubwp = ubwpList && ubwp_id > 0 ? ubwpList->list.array[ubwp_id - 1] : NULL; - sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_INACTIVE; - } - break; - - case BWP_SWITCH_INACTIVE: - break; - - default: - AssertFatal(1==0,"Invalid bwp switch state\n"); - break; - } -} - void schedule_nr_bwp_switch(module_id_t module_id, frame_t frame, sub_frame_t slot) { @@ -2954,16 +2907,21 @@ void schedule_nr_bwp_switch(module_id_t module_id, const NR_list_t *UE_list = &UE_info->list; // TODO: Implementation of a algorithm to perform: - // - the BWP switch trigger: sched_ctrl->bwp_switch_info.bwp_switch_state = BWP_SWITCH_TO_START - // - the BWP selection: sched_ctrl->bwp_switch_info.next_bwp = bwp_id + // - DL BWP selection: sched_ctrl->next_dl_bwp_id = dl_bwp_id + // - UL BWP selection: sched_ctrl->next_ul_bwp_id = ul_bwp_id for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - if(sched_ctrl->bwp_switch_info.bwp_switch_state > BWP_SWITCH_INACTIVE) { - nr_bwp_switch(module_id, frame, slot, UE_id, sched_ctrl->bwp_switch_info.next_bwp); + if (sched_ctrl->rrc_processing_timer == 0 && + UE_info->Msg4_ACKed[UE_id] == true && + ((sched_ctrl->next_dl_bwp_id >= 0 && sched_ctrl->active_bwp && sched_ctrl->active_bwp->bwp_Id != sched_ctrl->next_dl_bwp_id) || + (sched_ctrl->next_ul_bwp_id >= 0 && sched_ctrl->active_ubwp && sched_ctrl->active_ubwp->bwp_Id != sched_ctrl->next_ul_bwp_id))) { + + LOG_W(NR_MAC,"(%d.%d) [UE_id %d] Schedule BWP switch from dl_bwp_id %ld to %ld and from ul_bwp_id %ld to %ld\n", + frame, slot, UE_id, sched_ctrl->active_bwp->bwp_Id, sched_ctrl->next_dl_bwp_id, sched_ctrl->active_ubwp->bwp_Id, sched_ctrl->next_ul_bwp_id); + nr_mac_rrc_bwp_switch_req(module_id, frame, slot, UE_info->rnti[UE_id], sched_ctrl->next_dl_bwp_id, sched_ctrl->next_ul_bwp_id); } } - } /*void fill_nfapi_coresets_and_searchspaces(NR_CellGroupConfig_t *cg, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index b80b954b8c..4d82f51868 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -558,19 +558,6 @@ typedef struct NR_UE_ul_harq { NR_sched_pusch_t sched_pusch; } NR_UE_ul_harq_t; -typedef enum { - BWP_SWITCH_INACTIVE = 0, - BWP_SWITCH_TO_START, - BWP_SWITCH_RUNNING -} NR_BWP_switch_states_t; - -typedef struct NR_BWP_switch_info { - NR_BWP_Id_t current_bwp; - NR_BWP_Id_t next_bwp; - sub_frame_t bwp_switch_slot; - NR_BWP_switch_states_t bwp_switch_state; -} NR_BWP_switch_info_t; - /*! \brief scheduling control information set through an API */ #define MAX_CSI_REPORTS 48 typedef struct { @@ -579,8 +566,10 @@ typedef struct { /// the currently active BWP in UL NR_BWP_Uplink_t *active_ubwp; - /// the state of bwp switch procedure - NR_BWP_switch_info_t bwp_switch_info; + /// the next active BWP ID in DL + NR_BWP_Id_t next_dl_bwp_id; + /// the next active BWP ID in UL + NR_BWP_Id_t next_ul_bwp_id; /// CCE index and aggregation, should be coherent with cce_list NR_SearchSpace_t *search_space; diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index 9c06dfaff4..c8df5d4b5f 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -297,13 +297,14 @@ int8_t nr_mac_rrc_bwp_switch_req(const module_id_t module_idP, const frame_t frameP, const sub_frame_t sub_frameP, const rnti_t rntiP, - const int bwp_id) { + const int dl_bwp_id, + const int ul_bwp_id) { struct rrc_gNB_ue_context_s *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[module_idP], rntiP); protocol_ctxt_t ctxt; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, GNB_FLAG_YES, rntiP, frameP, sub_frameP, 0); - nr_rrc_reconfiguration_req(ue_context_p, &ctxt, bwp_id); + nr_rrc_reconfiguration_req(ue_context_p, &ctxt, dl_bwp_id, ul_bwp_id); return 0; } diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index ab5f5fbc65..6a320123de 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -171,7 +171,8 @@ int8_t nr_mac_rrc_bwp_switch_req(const module_id_t module_idP, const frame_t frameP, const sub_frame_t sub_frameP, const rnti_t rntiP, - const int bwp_id); + const int dl_bwp_id, + const int ul_bwp_id); int8_t nr_mac_rrc_data_ind( const module_id_t module_idP, @@ -188,7 +189,8 @@ int8_t nr_mac_rrc_data_ind( int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP, protocol_ctxt_t *const ctxt_pP, - const int bwp_id); + const int dl_bwp_id, + const int ul_bwp_id); int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, const uint8_t *buffer, diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 3f47e41b0d..3d41787a2d 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1942,16 +1942,21 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP, protocol_ctxt_t *const ctxt_pP, - const int bwp_id) { + const int dl_bwp_id, + const int ul_bwp_id) { uint8_t buffer[RRC_BUF_SIZE]; memset(buffer, 0, sizeof(buffer)); uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); NR_CellGroupConfig_t *masterCellGroup = ue_context_pP->ue_context.masterCellGroup; - *masterCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = bwp_id; - *masterCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = bwp_id; - *masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = bwp_id; + if (dl_bwp_id >= 0) { + *masterCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = dl_bwp_id; + *masterCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = dl_bwp_id; + } + if (ul_bwp_id >= 0) { + *masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = ul_bwp_id; + } uint16_t size = do_RRCReconfiguration(ctxt_pP, buffer, From c587fe366d9db874e4703b63fc0188080e021961 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Fri, 6 May 2022 16:01:10 +0100 Subject: [PATCH 207/233] Improve log and cosmetic changes --- openair2/GNB_APP/gnb_config.c | 148 +++++++++--------- openair2/LAYER2/NR_MAC_gNB/config.c | 2 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 8 +- ...gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf | 4 +- 4 files changed, 81 insertions(+), 81 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index ea3133db76..bd3c3b8d2c 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -382,84 +382,84 @@ void prepare_scd(NR_ServingCellConfig_t *scd) { scd->defaultDownlinkBWP_Id = CALLOC(1, sizeof(*scd->defaultDownlinkBWP_Id)); *scd->defaultDownlinkBWP_Id = 0; -for (int j = 0; j < NR_MAX_NUM_BWP; j++) { + for (int j = 0; j < NR_MAX_NUM_BWP; j++) { - // Downlink bandwidth part - NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); - bwp->bwp_Id = j+1; + // Downlink bandwidth part + NR_BWP_Downlink_t *bwp = calloc(1, sizeof(*bwp)); + bwp->bwp_Id = j+1; - // Allocate downlink dedicated bandwidth part and PDSCH structures - bwp->bwp_Common = calloc(1, sizeof(*bwp->bwp_Common)); - bwp->bwp_Common->pdcch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); - bwp->bwp_Common->pdsch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); - bwp->bwp_Dedicated = calloc(1, sizeof(*bwp->bwp_Dedicated)); - bwp->bwp_Dedicated->pdsch_Config = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config)); - bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; - bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present = NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; + // Allocate downlink dedicated bandwidth part and PDSCH structures + bwp->bwp_Common = calloc(1, sizeof(*bwp->bwp_Common)); + bwp->bwp_Common->pdcch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdcch_ConfigCommon)); + bwp->bwp_Common->pdsch_ConfigCommon = calloc(1, sizeof(*bwp->bwp_Common->pdsch_ConfigCommon)); + bwp->bwp_Dedicated = calloc(1, sizeof(*bwp->bwp_Dedicated)); + bwp->bwp_Dedicated->pdsch_Config = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config)); + bwp->bwp_Dedicated->pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; + bwp->bwp_Dedicated->pdsch_Config->choice.setup = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA)); + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->present = NR_SetupRelease_DMRS_DownlinkConfig_PR_setup; - // Allocate DL DMRS and PTRS configuration - bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); - NR_DMRS_DownlinkConfig_t *NR_DMRS_DownlinkCfg = bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; - NR_DMRS_DownlinkCfg->phaseTrackingRS=CALLOC(1, sizeof(*NR_DMRS_DownlinkCfg->phaseTrackingRS)); - NR_DMRS_DownlinkCfg->phaseTrackingRS->present = NR_SetupRelease_PTRS_DownlinkConfig_PR_setup; - NR_DMRS_DownlinkCfg->phaseTrackingRS->choice.setup = CALLOC(1, sizeof(*NR_DMRS_DownlinkCfg->phaseTrackingRS->choice.setup)); - NR_PTRS_DownlinkConfig_t *NR_PTRS_DownlinkCfg = NR_DMRS_DownlinkCfg->phaseTrackingRS->choice.setup; - NR_PTRS_DownlinkCfg->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->frequencyDensity)); - long *dl_rbs = CALLOC(2, sizeof(long)); - for (int i=0;i<2;i++) { - ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->frequencyDensity->list, &dl_rbs[i]); + // Allocate DL DMRS and PTRS configuration + bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup = calloc(1, sizeof(*bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup)); + NR_DMRS_DownlinkConfig_t *NR_DMRS_DownlinkCfg = bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; + NR_DMRS_DownlinkCfg->phaseTrackingRS=CALLOC(1, sizeof(*NR_DMRS_DownlinkCfg->phaseTrackingRS)); + NR_DMRS_DownlinkCfg->phaseTrackingRS->present = NR_SetupRelease_PTRS_DownlinkConfig_PR_setup; + NR_DMRS_DownlinkCfg->phaseTrackingRS->choice.setup = CALLOC(1, sizeof(*NR_DMRS_DownlinkCfg->phaseTrackingRS->choice.setup)); + NR_PTRS_DownlinkConfig_t *NR_PTRS_DownlinkCfg = NR_DMRS_DownlinkCfg->phaseTrackingRS->choice.setup; + NR_PTRS_DownlinkCfg->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->frequencyDensity)); + long *dl_rbs = CALLOC(2, sizeof(long)); + for (int i=0;i<2;i++) { + ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->frequencyDensity->list, &dl_rbs[i]); + } + NR_PTRS_DownlinkCfg->timeDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->timeDensity)); + long *dl_mcs = CALLOC(3, sizeof(long)); + for (int i=0;i<3;i++) { + ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->timeDensity->list, &dl_mcs[i]); + } + NR_PTRS_DownlinkCfg->epre_Ratio = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->epre_Ratio)); + NR_PTRS_DownlinkCfg->resourceElementOffset = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->resourceElementOffset)); + *NR_PTRS_DownlinkCfg->resourceElementOffset = 0; + ASN_SEQUENCE_ADD(&scd->downlinkBWP_ToAddModList->list,bwp); + + // Allocate uplink structures + + NR_PUSCH_Config_t *pusch_Config = CALLOC(1, sizeof(*pusch_Config)); + + // Allocate UL DMRS and PTRS structures + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = CALLOC(1, sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; + pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = CALLOC(1, sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); + NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + NR_DMRS_UplinkConfig->phaseTrackingRS = CALLOC(1, sizeof(*NR_DMRS_UplinkConfig->phaseTrackingRS)); + NR_DMRS_UplinkConfig->phaseTrackingRS->present = NR_SetupRelease_PTRS_UplinkConfig_PR_setup; + NR_DMRS_UplinkConfig->phaseTrackingRS->choice.setup = CALLOC(1, sizeof(*NR_DMRS_UplinkConfig->phaseTrackingRS->choice.setup)); + NR_PTRS_UplinkConfig_t *NR_PTRS_UplinkConfig = NR_DMRS_UplinkConfig->phaseTrackingRS->choice.setup; + NR_PTRS_UplinkConfig->transformPrecoderDisabled = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled)); + NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity)); + long *n_rbs = CALLOC(2, sizeof(long)); + for (int i=0;i<2;i++) { + ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity->list, &n_rbs[i]); + } + NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity)); + long *ptrs_mcs = CALLOC(3, sizeof(long)); + for (int i = 0; i < 3; i++) { + ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity->list, &ptrs_mcs[i]); + } + NR_PTRS_UplinkConfig->transformPrecoderDisabled->resourceElementOffset = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->resourceElementOffset)); + *NR_PTRS_UplinkConfig->transformPrecoderDisabled->resourceElementOffset = 0; + + // UL bandwidth part + NR_BWP_Uplink_t *ubwp = CALLOC(1, sizeof(*ubwp)); + ubwp->bwp_Id = j+1; + ubwp->bwp_Common = CALLOC(1, sizeof(*ubwp->bwp_Common)); + ubwp->bwp_Dedicated = CALLOC(1, sizeof(*ubwp->bwp_Dedicated)); + + ubwp->bwp_Dedicated->pusch_Config = CALLOC(1, sizeof(*ubwp->bwp_Dedicated->pusch_Config)); + ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; + ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; + + ASN_SEQUENCE_ADD(&scd->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); } - NR_PTRS_DownlinkCfg->timeDensity = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->timeDensity)); - long *dl_mcs = CALLOC(3, sizeof(long)); - for (int i=0;i<3;i++) { - ASN_SEQUENCE_ADD(&NR_PTRS_DownlinkCfg->timeDensity->list, &dl_mcs[i]); - } - NR_PTRS_DownlinkCfg->epre_Ratio = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->epre_Ratio)); - NR_PTRS_DownlinkCfg->resourceElementOffset = CALLOC(1, sizeof(*NR_PTRS_DownlinkCfg->resourceElementOffset)); - *NR_PTRS_DownlinkCfg->resourceElementOffset = 0; - ASN_SEQUENCE_ADD(&scd->downlinkBWP_ToAddModList->list,bwp); - - // Allocate uplink structures - - NR_PUSCH_Config_t *pusch_Config = CALLOC(1, sizeof(*pusch_Config)); - - // Allocate UL DMRS and PTRS structures - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB = CALLOC(1, sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB)); - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->present = NR_SetupRelease_DMRS_UplinkConfig_PR_setup; - pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup = CALLOC(1, sizeof(*pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup)); - NR_DMRS_UplinkConfig_t *NR_DMRS_UplinkConfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; - NR_DMRS_UplinkConfig->phaseTrackingRS = CALLOC(1, sizeof(*NR_DMRS_UplinkConfig->phaseTrackingRS)); - NR_DMRS_UplinkConfig->phaseTrackingRS->present = NR_SetupRelease_PTRS_UplinkConfig_PR_setup; - NR_DMRS_UplinkConfig->phaseTrackingRS->choice.setup = CALLOC(1, sizeof(*NR_DMRS_UplinkConfig->phaseTrackingRS->choice.setup)); - NR_PTRS_UplinkConfig_t *NR_PTRS_UplinkConfig = NR_DMRS_UplinkConfig->phaseTrackingRS->choice.setup; - NR_PTRS_UplinkConfig->transformPrecoderDisabled = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled)); - NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity)); - long *n_rbs = CALLOC(2, sizeof(long)); - for (int i=0;i<2;i++) { - ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->frequencyDensity->list, &n_rbs[i]); - } - NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity)); - long *ptrs_mcs = CALLOC(3, sizeof(long)); - for (int i = 0; i < 3; i++) { - ASN_SEQUENCE_ADD(&NR_PTRS_UplinkConfig->transformPrecoderDisabled->timeDensity->list, &ptrs_mcs[i]); - } - NR_PTRS_UplinkConfig->transformPrecoderDisabled->resourceElementOffset = CALLOC(1, sizeof(*NR_PTRS_UplinkConfig->transformPrecoderDisabled->resourceElementOffset)); - *NR_PTRS_UplinkConfig->transformPrecoderDisabled->resourceElementOffset = 0; - - // UL bandwidth part - NR_BWP_Uplink_t *ubwp = CALLOC(1, sizeof(*ubwp)); - ubwp->bwp_Id = j+1; - ubwp->bwp_Common = CALLOC(1, sizeof(*ubwp->bwp_Common)); - ubwp->bwp_Dedicated = CALLOC(1, sizeof(*ubwp->bwp_Dedicated)); - - ubwp->bwp_Dedicated->pusch_Config = CALLOC(1, sizeof(*ubwp->bwp_Dedicated->pusch_Config)); - ubwp->bwp_Dedicated->pusch_Config->present = NR_SetupRelease_PUSCH_Config_PR_setup; - ubwp->bwp_Dedicated->pusch_Config->choice.setup = pusch_Config; - - ASN_SEQUENCE_ADD(&scd->uplinkConfig->uplinkBWP_ToAddModList->list,ubwp); -} } /* This function checks dedicated serving cell configuration and performs fixes as needed */ diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 9dff368ccc..5eb6db2cdf 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -467,7 +467,7 @@ int nr_mac_enable_ue_rrc_processing_timer(module_id_t Mod_idP, rnti_t rnti, NR_S const uint16_t sf_ahead = 6/(0x01<0); const uint16_t sl_ahead = sf_ahead * (0x01<rrc_processing_timer = (rrc_reconfiguration_delay<DL_req[CC_id].dl_tti_request_body; NR_BWP_t *genericParameters = sched_ctrl->active_bwp ? - &sched_ctrl->active_bwp->bwp_Common->genericParameters: + &sched_ctrl->active_bwp->bwp_Common->genericParameters : &gNB_mac->common_channels[0].ServingCellConfigCommon->downlinkConfigCommon->initialDownlinkBWP->genericParameters; for (int id = 0; id < csi_measconfig->nzp_CSI_RS_ResourceToAddModList->list.count; id++){ nzpcsi = csi_measconfig->nzp_CSI_RS_ResourceToAddModList->list.array[id]; @@ -2836,15 +2836,15 @@ void nr_mac_update_timers(module_id_t module_id, // If needed, update the Dedicated BWP const int current_bwp_id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; const int current_ubwp_id = sched_ctrl->active_ubwp ? sched_ctrl->active_ubwp->bwp_Id : 0; - if(spCellConfigDedicated && + if (spCellConfigDedicated && spCellConfigDedicated->downlinkBWP_ToAddModList && spCellConfigDedicated->uplinkConfig && spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList) { - if(*spCellConfigDedicated->firstActiveDownlinkBWP_Id != current_bwp_id) { + if (*spCellConfigDedicated->firstActiveDownlinkBWP_Id != current_bwp_id) { sched_ctrl->active_bwp = spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[*spCellConfigDedicated->firstActiveDownlinkBWP_Id - 1]; LOG_I(NR_MAC, "Changing to DL-BWP %li\n", sched_ctrl->active_bwp->bwp_Id); } - if(*spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id != current_ubwp_id) { + if (*spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id != current_ubwp_id) { sched_ctrl->active_ubwp = spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[*spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id - 1]; LOG_I(NR_MAC, "Changing to UL-BWP %li\n", sched_ctrl->active_ubwp->bwp_Id); } diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf index 2a84743a0c..3d93f2cfab 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.273PRB.2x2.usrpn300.conf @@ -175,7 +175,7 @@ servingCellConfigDedicated = ({ # BWP-Downlink # BWP 1 Configuration dl_bwp-Id_1 = 1; - dl_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) + dl_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=273 (100 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 dl_bwp1_subcarrierSpacing = 1; @@ -192,7 +192,7 @@ servingCellConfigDedicated = ({ # BWP-Uplink # BWP 1 Configuration ul_bwp-Id_1 = 1; - ul_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=217 (80 MHz BW) + ul_bwp1_locationAndBandwidth = 1099; // RBstart=0, L=273 (100 MHz BW) # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 ul_bwp1_subcarrierSpacing = 1; From d7795c9dffe91fb81509c11a6f2dcec921b3f735 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Fri, 6 May 2022 16:15:43 +0100 Subject: [PATCH 208/233] Fix build after merge --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index a8ee9f4b98..be3c2beb26 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2878,6 +2878,7 @@ void nr_mac_update_timers(module_id_t module_id, int dci_format = get_dci_format(sched_ctrl); const uint8_t num_dmrs_cdm_grps_no_data = (ubwp || ubwpd) ? 1 : 2; + const uint8_t nrOfLayers = 1; const int utda = ubwp && preferred_ul_tda[ubwp->bwp_Id][slot] >= 0 ? preferred_ul_tda[ubwp->bwp_Id][slot] : (ups->time_domain_allocation >= 0 ? ups->time_domain_allocation : 0); @@ -2888,6 +2889,7 @@ void nr_mac_update_timers(module_id_t module_id, dci_format, utda, num_dmrs_cdm_grps_no_data, + nrOfLayers, ups); } } From 421ca8aed5634650363e7ca743bd4d742b8b9cdf Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 4 May 2022 11:36:14 +0100 Subject: [PATCH 209/233] Clean up of unused test cases in NSA with B210 --- ci-scripts/xml_files/fr1_nsa_quectel.xml | 33 +++--------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/ci-scripts/xml_files/fr1_nsa_quectel.xml b/ci-scripts/xml_files/fr1_nsa_quectel.xml index 0f8c90abdf..99c0e1757b 100644 --- a/ci-scripts/xml_files/fr1_nsa_quectel.xml +++ b/ci-scripts/xml_files/fr1_nsa_quectel.xml @@ -34,8 +34,8 @@ 050000 050001 000001 - 070003 - 070002 + 070000 + 070001 000001 050000 050001 @@ -52,14 +52,12 @@ yes - Terminate_UE Terminate Quectel idefix - Initialize_eNB Initialize eNB @@ -70,7 +68,6 @@ yes - Initialize_eNB Initialize gNB @@ -92,7 +89,6 @@ 20 - Ping Ping: 20pings in 20sec @@ -112,28 +108,6 @@ - Iperf - iperf (DL/30Mbps/UDP)(30 sec)(single-ue profile) - -u -b 30M -t 30 - DL - idefix - 1 - 95 - single-ue - - - - Iperf - iperf (DL/90Mbps/UDP)(30 sec)(single-ue profile) - -u -b 90M -t 30 - DL - idefix - 1 - 95 - single-ue - - - Iperf iperf (DL/125Mbps/UDP)(60 sec)(single-ue profile) -u -b 125M -t 60 @@ -144,7 +118,7 @@ single-ue - + Iperf iperf (UL/8Mbps/UDP)(60 sec)(single-ue profile) -u -b 8M -t 60 @@ -155,7 +129,6 @@ single-ue - Terminate_eNB Terminate eNB From c6bedb6c27052a7bb1ca4c1a9b5035c64ef0ebf9 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 4 May 2022 11:39:59 +0100 Subject: [PATCH 210/233] Increase iPerf packet loss and bitrate thresholds to make RAN-NSA-B200-Module-LTEBOX test green (due to UE host performance) --- ci-scripts/xml_files/fr1_nsa_quectel.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-scripts/xml_files/fr1_nsa_quectel.xml b/ci-scripts/xml_files/fr1_nsa_quectel.xml index 99c0e1757b..a7fd0fc0d7 100644 --- a/ci-scripts/xml_files/fr1_nsa_quectel.xml +++ b/ci-scripts/xml_files/fr1_nsa_quectel.xml @@ -113,8 +113,8 @@ -u -b 125M -t 60 DL idefix - 1 - 95 + 25 + 80 single-ue From ad8f99dc03435180fa7a23d7d355aedfc7cd4f5a Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Sun, 8 May 2022 02:22:52 +0100 Subject: [PATCH 211/233] Increase iPerf packet loss and bitrate thresholds to make RAN-NSA-B200-Module-LTEBOX-Container test green (due to UE host performance) --- ci-scripts/xml_files/container_nsa_b200_quectel.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-scripts/xml_files/container_nsa_b200_quectel.xml b/ci-scripts/xml_files/container_nsa_b200_quectel.xml index 47a939f63c..8d04be3efd 100644 --- a/ci-scripts/xml_files/container_nsa_b200_quectel.xml +++ b/ci-scripts/xml_files/container_nsa_b200_quectel.xml @@ -142,8 +142,8 @@ -u -b 125M -t 60 -i 1 -fm DL idefix - 1 - 95 + 25 + 80 single-ue From 3e3d73ef935a05a6de626031cb9dec7eab181a78 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 4 May 2022 10:01:09 +0200 Subject: [PATCH 212/233] Install UHD 4.2 for U18/20/22 --- cmake_targets/tools/build_helper | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index 0d2c82d62e..179e2de428 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -381,7 +381,7 @@ check_install_usrp_uhd_driver(){ $SUDO apt-get remove libuhd3.14.1 -y || true $SUDO apt-get remove libuhd3.15.0 -y || true local distribution=$(get_distribution_release) - if [[ "$distribution" == "ubuntu18.04" ]]; then + if [[ "$distribution" == "ubuntu18.04" || "$distribution" == "ubuntu20.04" || "$distribution" == "ubuntu22.04" ]]; then $SUDO apt-get remove libuhd4.0.0 -y || true $SUDO apt-get remove libuhd4.1.0 -y || true fi @@ -411,12 +411,14 @@ check_install_usrp_uhd_driver(){ done $SUDO apt-get update $SUDO apt-get -y install python-tk libboost-all-dev libusb-1.0-0-dev - if [[ "$distribution" == "ubuntu16.04" ]]; then + case "$(get_distribution_release)" in + "ubuntu16.04") $SUDO apt-get -y install libuhd-dev libuhd3.15.0 uhd-host - fi - if [[ "$distribution" == "ubuntu18.04" ]]; then - $SUDO apt-get -y install libuhd-dev libuhd4.1.0 uhd-host - fi + ;; + "ubuntu18.04" | "ubuntu20.04" | "ubuntu22.04") + $SUDO apt-get -y install libuhd-dev libuhd4.2.0 uhd-host + ;; + esac elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then if [ $IS_CONTAINER -eq 0 ] then From b37e5e552bf0800e01e987273b0f610eebe8eb07 Mon Sep 17 00:00:00 2001 From: Manish Date: Wed, 27 Apr 2022 05:31:12 +0000 Subject: [PATCH 213/233] help menu updated in document for native architecture. Compilation steps corrected on Ubuntu 20. Signed-off-by: Manish --- doc/BUILD.md | 1 + doc/TESTING_5GSA_setup.md | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/doc/BUILD.md b/doc/BUILD.md index a52ce98cb5..70d69f5afd 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -193,6 +193,7 @@ Using the help option of the build script you can get the list of available opti | --UE | maintained and tested in CI | build `lte-uesoftmodem` the LTE UE | | --gNB | maintained and tested in CI | build `nr-softmodem` the 5G gNodeB | | --nrUE | maintained and tested in CI | build `nr-uesoftmodem` the 5G UE | +| --arch-native | maintained | build with native architecture optimization | | --usrp-recplay | deprecated | use the USRP configuration parameters to use the record player. | | --build-lib | maintained | build optional shared library(ies), which can then be loaded at run time via command line option. Use the --help option to get the list of supported optional libraries. `all` can be used to build all available optional libraries. | | --UE-conf-nvram | maintained | Specifies the path to the input file used by the conf2uedata utility. defaults to [openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf](../openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf) | diff --git a/doc/TESTING_5GSA_setup.md b/doc/TESTING_5GSA_setup.md index 9b27549df4..4b86e12297 100644 --- a/doc/TESTING_5GSA_setup.md +++ b/doc/TESTING_5GSA_setup.md @@ -82,15 +82,7 @@ To get the code and build the gNB executable: git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git git checkout develop - # Install dependencies in Ubuntu 20.04 - cd - cd openairinterface5g/ - source oaienv - cd cmake_targets/ - ./install_external_packages.ubuntu20 - # Build OAI gNB - cd cd openairinterface5g/ source oaienv cd cmake_targets/ From d6f79071abdf7ba6e5fce8e7272263c81475f2d4 Mon Sep 17 00:00:00 2001 From: Manish Date: Tue, 10 May 2022 02:52:56 +0000 Subject: [PATCH 214/233] gNB Build Section Updated --- doc/TESTING_5GSA_setup.md | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/doc/TESTING_5GSA_setup.md b/doc/TESTING_5GSA_setup.md index 4b86e12297..357c62c813 100644 --- a/doc/TESTING_5GSA_setup.md +++ b/doc/TESTING_5GSA_setup.md @@ -50,7 +50,7 @@ At the moment of writing this document interoperability with the following COTS ## 1.1 gNB build and configuration To get the code and build the gNB executable: -### Ubuntu 18.04 +### Build gNB ```bash git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git git checkout develop @@ -61,34 +61,6 @@ To get the code and build the gNB executable: ./build_oai --gNB -w USRP ``` -### Ubuntu 20.04 -```bash - # Build UHD from source - # https://files.ettus.com/manual/page_build_guide.html - sudo apt-get install libboost-all-dev libusb-1.0-0-dev doxygen python3-docutils python3-mako python3-numpy python3-requests python3-ruamel.yaml python3-setuptools cmake build-essential - - git clone https://github.com/EttusResearch/uhd.git - cd uhd/host - mkdir build - cd build - cmake ../ - make -j 4 - make test # This step is optional - sudo make install - sudo ldconfig - sudo uhd_images_downloader - - - git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git - git checkout develop - - # Build OAI gNB - cd openairinterface5g/ - source oaienv - cd cmake_targets/ - ./build_oai --gNB -w USRP -``` - A reference configuration file for the **monolithic** gNB is provided [here](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf). From 1fbc7e080b78b992e9d1426620a3fe9133825923 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 11 May 2022 08:50:29 +0200 Subject: [PATCH 215/233] addressing review --- doc/RUNMODEM.md | 2 +- openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index f741ae67d5..b291bdf2dd 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -222,7 +222,7 @@ This image shows an example of gNB 5G MIMO logical antenna port configuration. I The basic element is a dual-polarized antenna, therefore the minimal DL MIMO setup with two logical antenna ports would consist of two cross-polarized antenna elements. In a single panel configuration, as the one in the picture, this element can be repeated vertiacally and/or horizontally to form an equi-spaced 1D or 2D array. The values N1 and N2 represent the number of antenna ports in the two dimensions and the supported configurations are specified in Section 5.2.2.2.1 of TS 38.214. -The DL logical antenna port configuration can be selected thorugh configuration file. `pdsch_AntennaPorts_N1` can be used to set N1 parameter, `pdsch_AntennaPorts_N2` to set N2 and `pdsch_AntennaPorts_XP` to set the cross-polarization configuration (1 for single pol, 2 for cross-pol). To be noted that if XP is 1 but N1 and/or N2 are larger than 1, this would result in a non-standard configuration and the PMI selected would be the identity matrix regardless of CSI report. The default value for each of these parameters is 1. +The DL logical antenna port configuration can be selected through configuration file. `pdsch_AntennaPorts_N1` can be used to set N1 parameter, `pdsch_AntennaPorts_N2` to set N2 and `pdsch_AntennaPorts_XP` to set the cross-polarization configuration (1 for single pol, 2 for cross-pol). To be noted that if XP is 1 but N1 and/or N2 are larger than 1, this would result in a non-standard configuration and the PMI selected would be the identity matrix regardless of CSI report. The default value for each of these parameters is 1. # Additional links diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 793d7cb5ac..d1c376d793 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -209,7 +209,7 @@ void config_common(int Mod_idP, int ssb_SubcarrierOffset, int pdsch_AntennaPorts, int pusch_AntennaPorts, - NR_ServingCellConfigCommon_t *scc); + NR_ServingCellConfigCommon_t *scc); int generate_dlsch_header(unsigned char *mac_header, unsigned char num_sdus, From 340531038c464cb87257b95199e1c4603f896685 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 11 May 2022 15:21:08 +0200 Subject: [PATCH 216/233] bugfix: n1 and n2 for 2 ports --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index 6d23f2f1eb..a7c736c813 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -503,6 +503,8 @@ void compute_pmi_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig, if(codebookConfig->codebookType.choice.type1->subType.present == NR_CodebookConfig__codebookType__type1__subType_PR_typeI_SinglePanel) { if(codebookConfig->codebookType.choice.type1->subType.choice.typeI_SinglePanel->nrOfAntennaPorts.present == NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts_PR_two) { + csi_report->N1 = 1; + csi_report->N2 = 1; if (i==0) csi_report->csi_meas_bitlen.pmi_x2_bitlen[i]=2; if (i==1) From a582ac99e5cf71f000e063fc20c5026aa3a2f099 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 12 May 2022 09:19:27 +0200 Subject: [PATCH 217/233] Trigger Amarisoft in NSA tests --- ci-scripts/Jenkinsfile-trig-nsa | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ci-scripts/Jenkinsfile-trig-nsa b/ci-scripts/Jenkinsfile-trig-nsa index 772b34231a..3616b2c404 100644 --- a/ci-scripts/Jenkinsfile-trig-nsa +++ b/ci-scripts/Jenkinsfile-trig-nsa @@ -113,6 +113,21 @@ pipeline { GitPostArgs += jobName + ' ' + build_url + ' ' + build_id + ' ' + jobResult + ' ' echo GitPostArgs + //calling SA-AmariS + jobName = "RAN-SA-AmariS-CN5G" + jobStatus = build job: jobName, wait : true, propagate : false, parameters: [ + string(name: 'eNB_MR', value: String.valueOf(MR)), + string(name: 'eNB_Branch', value: String.valueOf(SRC_BRANCH)), + string(name: 'eNB_CommitID', value: String.valueOf(COMMIT_ID)), + string(name: 'eNB_TargetBranch', value: String.valueOf(TARGET_BRANCH)), + booleanParam(name: 'eNB_mergeRequest', value: Boolean.valueOf(ALLOW_MERGE)) + ] + jobResult = jobStatus.getResult() + build_url = jobStatus.getAbsoluteUrl() + build_id = jobStatus.getNumber().toString() + GitPostArgs += jobName + ' ' + build_url + ' ' + build_id + ' ' + jobResult + ' ' + echo GitPostArgs + //calling OAIUE N310-X300 jobName = "RAN-SA-OAIUE-N310-X300-CN5G" jobStatus = build job: jobName, wait : true, propagate : false, parameters: [ From 270c540c3cbf75eb452042501ae35dce68dfa89f Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 11 May 2022 15:09:35 +0100 Subject: [PATCH 218/233] Minor clean up in function mac_rrc_nr_data_req --- openair2/RRC/NR/L2_nr_interface.c | 87 +++++++++++++++---------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index c8df5d4b5f..952f32fb10 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -219,78 +219,75 @@ uint16_t mac_rrc_nr_data_req(const module_id_t Mod_idP, LOG_D(RRC,"[eNB %d] mac_rrc_data_req to SRB ID=%ld\n",Mod_idP,Srb_id); #endif - // MIBCH - if ((Srb_id & RAB_OFFSET) == MIBCH) { + // MIBCH + if ((Srb_id & RAB_OFFSET) == MIBCH) { - asn_enc_rval_t enc_rval; - uint8_t sfn_msb = (uint8_t)((frameP>>4)&0x3f); - rrc_gNB_carrier_data_t *carrier = &RC.nrrrc[Mod_idP]->carrier; - NR_BCCH_BCH_Message_t *mib = &carrier->mib; + asn_enc_rval_t enc_rval; + uint8_t sfn_msb = (uint8_t)((frameP>>4)&0x3f); + rrc_gNB_carrier_data_t *carrier = &RC.nrrrc[Mod_idP]->carrier; + NR_BCCH_BCH_Message_t *mib = &carrier->mib; - // Currently we are getting the pdcch_ConfigSIB1 from the configuration file. - // Uncomment this function for a dynamic pdcch_ConfigSIB1. - //channel_bandwidth_t min_channel_bw = bw_10MHz; // Must be obtained based on TS 38.101-1 Table 5.3.5-1 - //generate_pdcch_ConfigSIB1(carrier->pdcch_ConfigSIB1, - // *carrier->servingcellconfigcommon->ssbSubcarrierSpacing, - // carrier->mib.message.choice.mib->subCarrierSpacingCommon, - // min_channel_bw); + // Currently we are getting the pdcch_ConfigSIB1 from the configuration file. + // Uncomment this function for a dynamic pdcch_ConfigSIB1. + //channel_bandwidth_t min_channel_bw = bw_10MHz; // Must be obtained based on TS 38.101-1 Table 5.3.5-1 + //generate_pdcch_ConfigSIB1(carrier->pdcch_ConfigSIB1, + // *carrier->servingcellconfigcommon->ssbSubcarrierSpacing, + // carrier->mib.message.choice.mib->subCarrierSpacingCommon, + // min_channel_bw); - mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = carrier->pdcch_ConfigSIB1->controlResourceSetZero; - mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = carrier->pdcch_ConfigSIB1->searchSpaceZero; + mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = carrier->pdcch_ConfigSIB1->controlResourceSetZero; + mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = carrier->pdcch_ConfigSIB1->searchSpaceZero; - mib->message.choice.mib->systemFrameNumber.buf[0] = sfn_msb << 2; - enc_rval = uper_encode_to_buffer(&asn_DEF_NR_BCCH_BCH_Message, - NULL, - (void *) mib, - carrier->MIB, - 24); - LOG_D(NR_RRC, "Encoded MIB for frame %d sfn_msb %d (%p), bits %lu\n", frameP, sfn_msb, carrier->MIB, - enc_rval.encoded); - buffer_pP[0] = carrier->MIB[0]; - buffer_pP[1] = carrier->MIB[1]; - buffer_pP[2] = carrier->MIB[2]; - LOG_D(NR_RRC, "MIB PDU buffer_pP[0]=%x , buffer_pP[1]=%x, buffer_pP[2]=%x\n", buffer_pP[0], buffer_pP[1], - buffer_pP[2]); - AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return (3); - } + mib->message.choice.mib->systemFrameNumber.buf[0] = sfn_msb << 2; + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_BCCH_BCH_Message, + NULL, + (void *) mib, + carrier->MIB, + 24); + LOG_D(NR_RRC, "Encoded MIB for frame %d sfn_msb %d (%p), bits %lu\n", frameP, sfn_msb, carrier->MIB, + enc_rval.encoded); + buffer_pP[0] = carrier->MIB[0]; + buffer_pP[1] = carrier->MIB[1]; + buffer_pP[2] = carrier->MIB[2]; + LOG_D(NR_RRC, "MIB PDU buffer_pP[0]=%x , buffer_pP[1]=%x, buffer_pP[2]=%x\n", buffer_pP[0], buffer_pP[1], + buffer_pP[2]); + AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + return 3; + } // TODO BCCH SIB1 SIBs - if ((Srb_id & RAB_OFFSET ) == BCCH) { - memcpy(&buffer_pP[0], - RC.nrrrc[Mod_idP]->carrier.SIB1, - RC.nrrrc[Mod_idP]->carrier.sizeof_SIB1); - + if ((Srb_id & RAB_OFFSET) == BCCH) { + memcpy(&buffer_pP[0], RC.nrrrc[Mod_idP]->carrier.SIB1, RC.nrrrc[Mod_idP]->carrier.sizeof_SIB1); return RC.nrrrc[Mod_idP]->carrier.sizeof_SIB1; } // CCCH - if( (Srb_id & RAB_OFFSET ) == CCCH) { + if ((Srb_id & RAB_OFFSET) == CCCH) { + LOG_D(NR_RRC,"[gNB %d] Frame %d CCCH request (Srb_id %ld)\n", Mod_idP, frameP, Srb_id); char *payload_pP; - uint16_t Sdu_size = 0; struct rrc_gNB_ue_context_s *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[Mod_idP], rnti); LOG_D(NR_RRC,"[gNB %d] Frame %d CCCH request (Srb_id %ld)\n", Mod_idP, frameP, Srb_id); + if (ue_context_p == NULL) { + LOG_E(NR_RRC,"[gNB %d] Frame %d CCCH request but no ue_context\n", Mod_idP, frameP); + return 0; + } - AssertFatal(ue_context_p!=NULL,"failed to get ue_context, rnti %x\n",rnti); uint16_t payload_size = ue_context_p->ue_context.Srb0.Tx_buffer.payload_size; - // check if data is there for MAC if (payload_size > 0) { payload_pP = ue_context_p->ue_context.Srb0.Tx_buffer.Payload; LOG_D(NR_RRC,"[gNB %d] CCCH has %d bytes (dest: %p, src %p)\n", Mod_idP, payload_size, buffer_pP, payload_pP); // Fill buffer memcpy((void *)buffer_pP, (void*)payload_pP, payload_size); - Sdu_size = payload_size; ue_context_p->ue_context.Srb0.Tx_buffer.payload_size = 0; } - return Sdu_size; + return payload_size; } - return(0); - + return 0; } int8_t nr_mac_rrc_bwp_switch_req(const module_id_t module_idP, From 0217cd50deef2fe85952527986958cdf4a3d295c Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 11 May 2022 15:46:25 +0100 Subject: [PATCH 219/233] Update in documentation --- .vscode/launch.json | 1 - doc/FEATURE_SET.md | 5 + doc/RUN_NR_multiple_BWPs.md | 33 +- .../gnb.band78.tm1.106PRB.usrpn300.SCD.conf | 348 ------------------ 4 files changed, 19 insertions(+), 368 deletions(-) delete mode 100644 targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf diff --git a/.vscode/launch.json b/.vscode/launch.json index 250adbbb54..8be6915763 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,6 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { "name": "(gdb) Launch", "type": "cppdbg", diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index a289c6d17e..d0856b415d 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -332,6 +332,9 @@ The following features are valid for the gNB and the 5G-NR UE. - evalution of RSRP report - evaluation of CQI report - MAC scheduling of SR reception +- Bandwidth part (BWP) operation + - Handle multiple dedicated BWPs + - BWP switching through RRCReconfiguration method **gNB RLC** - Send/Receive operations according to 38.322 Rel.16 @@ -493,6 +496,8 @@ The following features are valid for the gNB and the 5G-NR UE. - Configuration of fapi PDU according to DCI * Scheduler procedures for SRS transmission - Periodic SRS transmission +* Bandwidth part (BWP) operation + - Operation in configured dedicated BWP through RRCSetup or RRCReconfiguration **UE RLC** diff --git a/doc/RUN_NR_multiple_BWPs.md b/doc/RUN_NR_multiple_BWPs.md index f79dc21947..2c2c2fcf25 100644 --- a/doc/RUN_NR_multiple_BWPs.md +++ b/doc/RUN_NR_multiple_BWPs.md @@ -9,9 +9,9 @@ ## Bandwidth part (BWP) ## Bandwidth Part (BWP) is a set of contiguous Resource Blocks in the resource grid. -Parameters of a Bandwidth Part are communicated to UE using RRC parameters: BWP-Downlink and BWP-Uplink. +Parameters of a BWP are communicated to UE using RRC parameters: BWP-Downlink and BWP-Uplink. -A UE can be configured with a set of 4 BWPs in uplink (UL) and downlink (DL) direction. But only 1 BWP can be active in UL and DL direction at a given time. +A UE can be configured with a set of 4 BWPs in uplink (UL) and downlink (DL) direction (3GPP TS 38.331 Annex B.2 Description of BWP configuration options). But only 1 BWP can be active in UL and DL direction at a given time. # Procedure to run multiple dedicated BWPs # @@ -21,36 +21,31 @@ To configure multiple BWPs, add the following parameters to the gNB configuratio ## Setup of the Configuration files ## ``` - firstActiveDownlinkBWP-Id = 1; #BWP-Id - defaultDownlinkBWP-Id = 1; #BWP-Id - firstActiveUplinkBWP-Id = 1; #BWP-Id + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + firstActiveUplinkBWP-Id = 1; #BWP-Id ``` Each dedicated BWP must have: ``` - # BWP 1 Configuration - bwp-Id_1 = 1; - bwp1_locationAndBandwidth = 28908; // RBstart=33,L=106 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - bwp1_subcarrierSpacing = 1; + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; ``` - Find these parameters in this configuration file: "targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf" - -### NOTE: - * For changing bandwidth, 'dl_carrierBandwidth' and 'ul_carrierBandwidth' must be modified according - to the bandwidth of the BWP - * Currently only 'firstActiveDownlinkBWP-Id' is used to configure both DL and UL BWPs + Find these parameters in this configuration file: "targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf" # Testing gNB and UE in RF simulator ## gNB command: ``` - sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf --rfsim --phy-test -d + sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --rfsim --sa ``` ## UE command: ``` - sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem --rfsim --phy-test -d + sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --nokrnmod --rfsim --sa --uicc0.imsi 208990000000001 ``` \ No newline at end of file diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf deleted file mode 100644 index 2d8886bc46..0000000000 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.SCD.conf +++ /dev/null @@ -1,348 +0,0 @@ -Active_gNBs = ( "gNB-Eurecom-5GNRBox"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -gNBs = -( - { - ////////// Identification parameters: - gNB_ID = 0xe00; - - cell_type = "CELL_MACRO_GNB"; - - gNB_name = "gNB-Eurecom-5GNRBox"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - - plmn_list = ({mcc = 208; mnc = 93; mnc_length = 2;}); - - tr_s_preference = "local_mac" - - ////////// Physical parameters: - - ssb_SubcarrierOffset = 0; - pdsch_AntennaPorts = 1; - pusch_AntennaPorts = 1; - min_rxtxtime = 6; - - servingCellConfigCommon = ( - { - #spCellConfigCommon - - physCellId = 0; - -# downlinkConfigCommon - #frequencyInfoDL - # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP), points to Subcarrier 0 of RB#10 of SSB block - absoluteFrequencySSB = 641032; - dl_frequencyBand = 78; - # this is 3600 MHz - dl_absoluteFrequencyPointA = 640000; - #scs-SpecificCarrierList - dl_offstToCarrier = 0; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_subcarrierSpacing = 1; - dl_carrierBandwidth = 106; //when changing BWPs, change this also to the same RBs - #initialDownlinkBWP - #genericParameters - # this is RBstart=33 (Start RB of SSB block),L=24 (275*(L-1))+RBstart - initialDLBWPlocationAndBandwidth = 6358; #6368; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - initialDLBWPsubcarrierSpacing = 1; - #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 0; #12; - initialDLBWPsearchSpaceZero = 0; - #pdsch-ConfigCommon - #pdschTimeDomainAllocationList (up to 16 entries) - initialDLBWPk0_0 = 0; # used for (full) DL slot - initialDLBWPmappingType_0 = 0; # 0=typeA,1=typeB - initialDLBWPstartSymbolAndLength_0 = 40; # this is SS=1,L=13 - - initialDLBWPk0_1 = 0; # used for DL part mixed slot - initialDLBWPmappingType_1 = 0; - initialDLBWPstartSymbolAndLength_1 = 57; # this is SS=1,L=5 - - #uplinkConfigCommon - #frequencyInfoUL - ul_frequencyBand = 78; - #scs-SpecificCarrierList - ul_offstToCarrier = 0; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_subcarrierSpacing = 1; - ul_carrierBandwidth = 106; //when changing BWPs, change this also to the same RBs - pMax = 20; - #initialUplinkBWP - #genericParameters - initialULBWPlocationAndBandwidth = 6358;#6368; -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - initialULBWPsubcarrierSpacing = 1; - #rach-ConfigCommon - #rach-ConfigGeneric - prach_ConfigurationIndex = 98; -#prach_msg1_FDM -#0 = one, 1=two, 2=four, 3=eight - prach_msg1_FDM = 0; - prach_msg1_FrequencyStart = 0; - zeroCorrelationZoneConfig = 13; - preambleReceivedTargetPower = -118; -#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) - preambleTransMax = 6; -#powerRampingStep -# 0=dB0,1=dB2,2=dB4,3=dB6 - powerRampingStep = 1; -#ra_ReponseWindow -#1,2,4,8,10,20,40,80 - ra_ResponseWindow = 4; -#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR -#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen - ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; -#oneHalf (0..15) 4,8,12,16,...60,64 - ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; -#ra_ContentionResolutionTimer -#(0..7) 8,16,24,32,40,48,56,64 - ra_ContentionResolutionTimer = 7; - rsrp_ThresholdSSB = 19; -#prach-RootSequenceIndex_PR -#1 = 839, 2 = 139 - prach_RootSequenceIndex_PR = 2; - prach_RootSequenceIndex = 1; - # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex - # - msg1_SubcarrierSpacing = 1, -# restrictedSetConfig -# 0=unrestricted, 1=restricted type A, 2=restricted type B - restrictedSetConfig = 0, - - # pusch-ConfigCommon (up to 16 elements) - initialULBWPk2_0 = 6; # used for (full) UL slot - initialULBWPmappingType_0 = 1 - initialULBWPstartSymbolAndLength_0 = 41; # this is SS=0 L=13 - - initialULBWPk2_1 = 6; # used for UL part mixed slot - initialULBWPmappingType_1 = 1; - initialULBWPstartSymbolAndLength_1 = 69; # this is SS=10 L=2 - - initialULBWPk2_2 = 7; # used for Msg.3 during RA - initialULBWPmappingType_2 = 1; - initialULBWPstartSymbolAndLength_2 = 52; # this is SS=10 L=4 - - msg3_DeltaPreamble = 1; - p0_NominalWithGrant =-90; - -# pucch-ConfigCommon setup : -# pucchGroupHopping -# 0 = neither, 1= group hopping, 2=sequence hopping - pucchGroupHopping = 0; - hoppingId = 40; - p0_nominal = -90; -# ssb_PositionsInBurs_BitmapPR -# 1=short, 2=medium, 3=long - ssb_PositionsInBurst_PR = 2; - ssb_PositionsInBurst_Bitmap = 0x1; - -# ssb_periodicityServingCell -# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 - ssb_periodicityServingCell = 2; - -# dmrs_TypeA_position -# 0 = pos2, 1 = pos3 - dmrs_TypeA_Position = 0; - -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - subcarrierSpacing = 1; - - - #tdd-UL-DL-ConfigurationCommon -# subcarrierSpacing -# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - referenceSubcarrierSpacing = 1; - # pattern1 - # dl_UL_TransmissionPeriodicity - # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 - dl_UL_TransmissionPeriodicity = 6; - nrofDownlinkSlots = 7; - nrofDownlinkSymbols = 6; - nrofUplinkSlots = 2; - nrofUplinkSymbols = 4; - - ssPBCH_BlockPower = -25; - } - - ); - - -# Dedicated Serving Cell Configuration -servingCellConfigDedicated = ({ - # BWP-Downlink - # BWP 1 Configuration - dl_bwp-Id_1 = 1; - dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp1_subcarrierSpacing = 1; - - # BWP 2 Configuration - dl_bwp-Id_2 = 2; - dl_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp2_subcarrierSpacing = 1; - - # BWP 3 Configuration - dl_bwp-Id_3 = 3; - dl_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp3_subcarrierSpacing = 1; - - # BWP 4 Configuration - dl_bwp-Id_4 = 4; - dl_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - dl_bwp4_subcarrierSpacing = 1; - - firstActiveDownlinkBWP-Id = 1; #BWP-Id - defaultDownlinkBWP-Id = 1; #BWP-Id - - # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, - # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, - # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, - # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } - - # UplinkConfig - # BWP-Uplink - # BWP 1 Configuration - ul_bwp-Id_1 = 1; - ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp1_subcarrierSpacing = 1; - - # BWP 2 Configuration - ul_bwp-Id_2 = 2; - ul_bwp2_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp2_subcarrierSpacing = 1; - - # BWP 3 Configuration - ul_bwp-Id_3 = 3; - ul_bwp3_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp3_subcarrierSpacing = 1; - - # BWP 4 Configuration - ul_bwp-Id_4 = 4; - ul_bwp4_locationAndBandwidth = 22299; // RBstart=24, L=82 - # subcarrierSpacing - # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 - ul_bwp4_subcarrierSpacing = 1; - - firstActiveUplinkBWP-Id = 1; #BWP-Id - } -); - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - - - ////////// MME parameters: - mme_ip_address = ( { ipv4 = "192.168.12.26"; - ipv6 = "192:168:30::17"; - active = "yes"; - preference = "ipv4"; - } - ); - - NETWORK_INTERFACES : - { - - GNB_INTERFACE_NAME_FOR_S1_MME = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1_MME = "192.168.12.111/24"; - GNB_INTERFACE_NAME_FOR_S1U = "eth0"; - GNB_IPV4_ADDRESS_FOR_S1U = "192.168.12.111/24"; - GNB_PORT_FOR_S1U = 2152; # Spec 2152 - }; - } -); - -MACRLCs = ( - { - num_cc = 1; - tr_s_preference = "local_L1"; - tr_n_preference = "local_RRC"; - } -); - -L1s = ( - { - num_cc = 1; - tr_n_preference = "local_mac"; - pusch_proc_threads = 8; - } -); - -RUs = ( - { - local_rf = "yes" - nb_tx = 1 - nb_rx = 1 - att_tx = 0 - att_rx = 0; - bands = [7]; - max_pdschReferenceSignalPower = -27; - max_rxgain = 50; - eNB_instances = [0]; - ## beamforming 1x2 matrix: 1 layer x 2 antennas - #bf_weights = [0x00007fff, 0x00007fff]; - ## beamforming 1x4 matrix: 1 layer x 4 antennas - #bf_weights = [0x00007fff, 0x0000,0x0000, 0x0000]; - ## beamforming 2x2 matrix: - #bf_weights = [0x00007fff, 0x00000000, 0x00000000, 0x00007fff]; - ## beamforming 4x4 matrix: - #bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff]; - sdr_addrs = "addr=192.168.10.2,mgmt_addr=192.168.10.2,second_addr=192.168.20.2"; - clock_src = "external"; - } -); - -THREAD_STRUCT = ( - { - #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" - parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; - #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" - worker_config = "WORKER_ENABLE"; - } -); - - log_config : - { - global_log_level ="info"; - global_log_verbosity ="medium"; - hw_log_level ="info"; - hw_log_verbosity ="medium"; - phy_log_level ="info"; - phy_log_verbosity ="medium"; - mac_log_level ="info"; - mac_log_verbosity ="high"; - rlc_log_level ="info"; - rlc_log_verbosity ="medium"; - pdcp_log_level ="info"; - pdcp_log_verbosity ="medium"; - rrc_log_level ="info"; - rrc_log_verbosity ="medium"; - }; - From 9aa7d1c88fd8bda4bc6827015ad14fae712b33d7 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 12 May 2022 10:57:03 +0100 Subject: [PATCH 220/233] Remove redundant change of active bwp (it changes after RRC processing timer elapsed) --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 47 +++++-------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 109a588ed0..5af415eb7b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1983,49 +1983,28 @@ void nr_check_Msg4_Ack(module_id_t module_id, int CC_id, frame_t frame, sub_fram UE_info->active[UE_id] = true; UE_info->Msg4_ACKed[UE_id] = true; - // TODO: To be improved the BWP selection - // For now, we are just changing to the first active BWP - - const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; - - const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig ? servingCellConfig->downlinkBWP_ToAddModList : NULL; - const int bwp_id = servingCellConfig && servingCellConfig->firstActiveDownlinkBWP_Id ? - *servingCellConfig->firstActiveDownlinkBWP_Id : 0; - sched_ctrl->active_bwp = bwpList && bwp_id > 0 ? bwpList->list.array[bwp_id - 1] : NULL; - - const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig ? servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList : NULL; - const int ubwp_id = servingCellConfig && servingCellConfig->uplinkConfig && servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id ? - *servingCellConfig->uplinkConfig->firstActiveUplinkBWP_Id : 0; - sched_ctrl->active_ubwp = ubwpList && ubwp_id > 0 ? ubwpList->list.array[ubwp_id - 1] : NULL; - - if(sched_ctrl->active_bwp) { - LOG_I(NR_MAC, "Changing to DL-BWP %i\n", bwp_id); - } - - if(sched_ctrl->active_ubwp) { - LOG_I(NR_MAC, "Changing to UL-BWP %i\n", ubwp_id); - } - // Pause scheduling according to: - const NR_SIB1_t *sib1 = RC.nrmac[module_id]->common_channels[0].sib1 ? RC.nrmac[module_id]->common_channels[0].sib1->message.choice.c1->choice.systemInformationBlockType1 : NULL; - NR_BWP_t *genericParameters = get_dl_bwp_genericParameters(sched_ctrl->active_bwp, - RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon, - sib1); - // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs - uint32_t delay_ms = bwpList ? NR_RRC_SETUP_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_SETUP_DELAY_MS; + const NR_COMMON_channels_t *common_channels = &RC.nrmac[module_id]->common_channels[0]; + const NR_SIB1_t *sib1 = common_channels->sib1 ? common_channels->sib1->message.choice.c1->choice.systemInformationBlockType1 : NULL; + const NR_ServingCellConfig_t *servingCellConfig = UE_info->CellGroup[UE_id] ? UE_info->CellGroup[UE_id]->spCellConfig->spCellConfigDedicated : NULL; + NR_BWP_t *genericParameters = get_dl_bwp_genericParameters(sched_ctrl->active_bwp, + common_channels->ServingCellConfigCommon, + sib1); + uint32_t delay_ms = servingCellConfig && servingCellConfig->downlinkBWP_ToAddModList ? + NR_RRC_SETUP_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS : NR_RRC_SETUP_DELAY_MS; + sched_ctrl->rrc_processing_timer = (delay_ms << genericParameters->subcarrierSpacing); - } - else { + LOG_I(NR_MAC, "(%d.%d) Activating RRC processing timer for UE %d with %d ms\n", frame, slot, UE_id, delay_ms); + } else { LOG_I(NR_MAC, "(ue %i, rnti 0x%04x) RA Procedure failed at Msg4!\n", UE_id, ra->rnti); } nr_clear_ra_proc(module_id, CC_id, frame, ra); - if(sched_ctrl->retrans_dl_harq.head >= 0) { + if (sched_ctrl->retrans_dl_harq.head >= 0) { remove_nr_list(&sched_ctrl->retrans_dl_harq, current_harq_pid); } - } - else { + } else { LOG_D(NR_MAC, "(ue %i, rnti 0x%04x) Received Nack of RA-Msg4. Preparing retransmission!\n", UE_id, ra->rnti); ra->Msg4_frame = (frame + 1) % 1024; ra->Msg4_slot = 1; From af377945c3229caeb1dc7cf2270db10430ed674f Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 12 May 2022 13:12:45 +0100 Subject: [PATCH 221/233] Fix hardcoded coreset and assignment from wrong array position --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 17 +++++++++++------ openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 1 + .../LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 8 ++++---- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 3 +++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 1 + .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 1 + .../NR_MAC_gNB/gNB_scheduler_primitives.c | 3 ++- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 11 +++-------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 1 + openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 1 + 10 files changed, 28 insertions(+), 19 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index c307b8235a..b62fdb5cf6 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2598,6 +2598,7 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, nr_rnti_type_t rnti_type, uint16_t N_RB, int bwp_id, + NR_ControlResourceSetId_t coreset_id, uint16_t cset0_bwp_size) { uint16_t size = 0; @@ -2648,7 +2649,7 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, /// fixed: Format identifier 1, Hop flag 1, MCS 5, NDI 1, RV 2, HARQ PID 4, PUSCH TPC 2 Time Domain assgnmt 4 --20 size += 20; size += (uint8_t)ceil( log2( (N_RB*(N_RB+1))>>1 ) ); // Freq domain assignment -- hopping scenario to be updated - int dci_10_size = nr_dci_size(initialDownlinkBWP,initialUplinkBWP,cg,dci_pdu,NR_DL_DCI_FORMAT_1_0, rnti_type, N_RB, bwp_id, cset0_bwp_size); + int dci_10_size = nr_dci_size(initialDownlinkBWP,initialUplinkBWP,cg,dci_pdu,NR_DL_DCI_FORMAT_1_0, rnti_type, N_RB, bwp_id, coreset_id, cset0_bwp_size); AssertFatal(dci_10_size >= size, "NR_UL_DCI_FORMAT_0_0 size is bigger than NR_DL_DCI_FORMAT_1_0! 3GPP TS 38.212 Section 7.3.1.0: DCI size alignment is not fully implemented"); size += dci_10_size - size; // Padding to match 1_0 size // UL/SUL indicator assumed to be 0 @@ -2992,11 +2993,15 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDownlinkBWP, size += dci_pdu->antenna_ports.nbits; LOG_D(NR_MAC,"dci_pdu->antenna_ports.nbits %d\n",dci_pdu->antenna_ports.nbits); // Tx Config Indication - int corset_id = 1 ; - long *isTciEnable = pdcch_Config->controlResourceSetToAddModList->list.array[corset_id-1]->tci_PresentInDCI; - if (isTciEnable != NULL) { - dci_pdu->transmission_configuration_indication.nbits = 3; - size += dci_pdu->transmission_configuration_indication.nbits; + for (int i = 0; i < pdcch_Config->controlResourceSetToAddModList->list.count; i++) { + if (pdcch_Config->controlResourceSetToAddModList->list.array[i]->controlResourceSetId == coreset_id) { + long *isTciEnable = pdcch_Config->controlResourceSetToAddModList->list.array[i]->tci_PresentInDCI; + if (isTciEnable != NULL) { + dci_pdu->transmission_configuration_indication.nbits = 3; + size += dci_pdu->transmission_configuration_indication.nbits; + } + break; + } } // SRS request if (cg->spCellConfig->spCellConfigDedicated->supplementaryUplink==NULL) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index d1becd41f9..2c39929413 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -55,6 +55,7 @@ uint16_t nr_dci_size(const NR_BWP_DownlinkCommon_t *initialDLBWP, nr_rnti_type_t rnti_type, uint16_t N_RB, int bwp_id, + NR_ControlResourceSetId_t coreset_id, uint16_t cset0_bwp_size); void find_aggregation_candidates(uint8_t *aggregation_level, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index fce6efa103..0dfbbf2be3 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -169,7 +169,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->SubcarrierSpacing = bwp_Common->genericParameters.subcarrierSpacing; } for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, dl_bwp_id, mac->type0_PDCCH_CSS_config.num_rbs); + rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_C, rel15->BWPSize, dl_bwp_id, coreset_id, mac->type0_PDCCH_CSS_config.num_rbs); } break; case NR_RNTI_RA: @@ -185,7 +185,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPStart = NRRIV2PRBOFFSET(bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); } rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; - rel15->dci_length_options[0] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, dl_bwp_id, mac->type0_PDCCH_CSS_config.num_rbs); + rel15->dci_length_options[0] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[0]], rel15->dci_format_options[0], NR_RNTI_RA, rel15->BWPSize, dl_bwp_id, coreset_id, mac->type0_PDCCH_CSS_config.num_rbs); break; case NR_RNTI_P: break; @@ -200,7 +200,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->BWPStart = mac->type0_PDCCH_CSS_config.cset_start_rb; rel15->SubcarrierSpacing = initialDownlinkBWP->genericParameters.subcarrierSpacing; for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_TC, rel15->BWPSize, dl_bwp_id, mac->type0_PDCCH_CSS_config.num_rbs); + rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_TC, rel15->BWPSize, dl_bwp_id, coreset_id, mac->type0_PDCCH_CSS_config.num_rbs); } break; case NR_RNTI_SP_CSI: @@ -223,7 +223,7 @@ void config_dci_pdu(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_dci_dl_pdu_rel15_t rel15->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon + 2; for (int i = 0; i < rel15->num_dci_options; i++) { - rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_SI, rel15->BWPSize, 0, mac->type0_PDCCH_CSS_config.num_rbs); + rel15->dci_length_options[i] = nr_dci_size(initialDownlinkBWP,initialUplinkBWP, mac->cg, &mac->def_dci_pdu_rel15[rel15->dci_format_options[i]], rel15->dci_format_options[i], NR_RNTI_SI, rel15->BWPSize, 0, coreset_id, mac->type0_PDCCH_CSS_config.num_rbs); } break; case NR_RNTI_SFI: diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 5af415eb7b..9ac9f0d880 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -904,6 +904,7 @@ void nr_generate_Msg3_retransmission(module_id_t module_idP, int CC_id, frame_t NR_RNTI_TC, pusch_pdu->bwp_size, ra->ul_bwp_id, + coresetid, nr_mac->cset0_bwp_size); // Mark the corresponding RBs as used @@ -1484,6 +1485,7 @@ void nr_generate_Msg2(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra NR_RNTI_RA, BWPSize, bwpid, + coresetid, nr_mac->cset0_bwp_size); // DL TX request @@ -1893,6 +1895,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra NR_RNTI_TC, pdsch_pdu_rel15->BWPSize, bwpid, + coresetid, nr_mac->cset0_bwp_size); // Add padding header and zero rest out if there is space left diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index b4ba1063f1..e2ceadb889 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -552,6 +552,7 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP, rnti_type, pdsch_pdu_rel15->BWPSize, 0, + 0, gNB_mac->cset0_bwp_size); LOG_D(MAC,"BWPSize: %i\n", pdcch_pdu_rel15->BWPSize); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 68725f9544..1bf245010a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1374,6 +1374,7 @@ void nr_schedule_ue_spec(module_id_t module_id, rnti_type, pdsch_pdu->BWPSize, bwp ? bwp->bwp_Id : 0, + coresetid, gNB_mac->cset0_bwp_size); LOG_D(NR_MAC, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 0f1cd2949b..a88d65ab0f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -1486,12 +1486,13 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, int rnti_type, int N_RB, int bwp_id, + NR_ControlResourceSetId_t coreset_id, uint16_t cset0_bwp_size) { uint8_t fsize = 0, pos = 0; uint64_t *dci_pdu = (uint64_t *)pdcch_dci_pdu->Payload; *dci_pdu=0; - int dci_size = nr_dci_size(scc->downlinkConfigCommon->initialDownlinkBWP,scc->uplinkConfigCommon->initialUplinkBWP, CellGroup, dci_pdu_rel15, dci_format, rnti_type, N_RB, bwp_id, cset0_bwp_size); + int dci_size = nr_dci_size(scc->downlinkConfigCommon->initialDownlinkBWP,scc->uplinkConfigCommon->initialUplinkBWP, CellGroup, dci_pdu_rel15, dci_format, rnti_type, N_RB, bwp_id, coreset_id, cset0_bwp_size); pdcch_dci_pdu->PayloadSizeBits = dci_size; AssertFatal(dci_size <= 64, "DCI sizes above 64 bits not yet supported"); if (dci_format == NR_DL_DCI_FORMAT_1_1 || dci_format == NR_UL_DCI_FORMAT_0_1) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index 2897a86c31..c6e7db8f07 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -876,9 +876,7 @@ int checkTargetSSBInTCIStates_pdcchConfig(int ssb_index_t, int Mod_idP, int UE_i NR_TCI_State_t *tci =NULL; NR_TCI_StateId_t *tci_id = NULL; NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; - const int bwp_id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; - NR_BWP_Downlink_t *bwp = CellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1]; - NR_ControlResourceSet_t *coreset = bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]; + NR_ControlResourceSet_t *coreset = sched_ctrl->coreset; int i; int flag = 0; int tci_stateID = -1; @@ -948,7 +946,6 @@ void tci_handling(module_id_t Mod_idP, int UE_id, frame_t frame, slot_t slot) { NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id]; const int bwp_id = sched_ctrl->active_bwp ? sched_ctrl->active_bwp->bwp_Id : 0; NR_CellGroupConfig_t *CellGroup = UE_info->CellGroup[UE_id]; - NR_BWP_Downlink_t *bwp = bwp_id > 0 ? sched_ctrl->active_bwp : NULL; //bwp indicator int n_dl_bwp=0; @@ -1078,10 +1075,8 @@ void tci_handling(module_id_t Mod_idP, int UE_id, frame_t frame, slot_t slot) { } } - sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tci_present_inDCI = bwp ? - bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[bwp_id-1]->tci_PresentInDCI : - CellGroup->spCellConfig->spCellConfigDedicated->initialDownlinkBWP->pdcch_Config->choice.setup->controlResourceSetToAddModList->list.array[0]->tci_PresentInDCI; - + sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tci_present_inDCI = sched_ctrl->coreset ? + sched_ctrl->coreset->tci_PresentInDCI : NULL; //filling pdsch tci state activation deactivation mac ce structure fields if(sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tci_present_inDCI) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 488995d2bc..44cbe2ce9d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1938,6 +1938,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot) rnti_types[0], pusch_pdu->bwp_size, bwp_id, + coresetid, nr_mac->cset0_bwp_size); memset(sched_pusch, 0, sizeof(*sched_pusch)); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 221ea878cb..da21f9e51f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -307,6 +307,7 @@ void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc, int rnti_types, int N_RB, int bwp_id, + NR_ControlResourceSetId_t coreset_id, uint16_t cset0_bwp_size); void prepare_dci(const NR_CellGroupConfig_t *CellGroup, From 7d1f693ba1f722d3b04ace0703d516fe56c90967 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 12 May 2022 13:28:18 +0100 Subject: [PATCH 222/233] Revert rbSize validation with target_dl_bw to previous version --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 2e76a2032a..c745c1921c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -303,7 +303,7 @@ void nr_preprocessor_phytest(module_id_t module_id, rbSize < target_dl_bw) rbSize++; /* found target_dl_bw? */ - if (rbStart+rbSize == target_dl_bw) + if (rbSize == target_dl_bw) break; /* at end and below target_dl_bw? */ if (rbStart + rbSize >= bwpSize) From 9f51b3f8b167b8d39aa2ef5850bc0cd5f133ced2 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 12 May 2022 14:42:47 +0100 Subject: [PATCH 223/233] Harmonize and reuse the nr_rrc_config_dl_tda function to prepare PDSCH-TimeDomainResourceAllocation list --- openair2/GNB_APP/gnb_config.c | 6 ++++-- openair2/RRC/NR/MESSAGES/asn1_msg.c | 16 ++++------------ openair2/RRC/NR/nr_rrc_config.c | 19 +++++++++++++------ openair2/RRC/NR/nr_rrc_config.h | 4 +++- openair2/RRC/NR/rrc_gNB_reconfig.c | 16 ++++------------ 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index bd3c3b8d2c..6e817aab8c 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -343,8 +343,10 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { if (*scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder!=0) scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder = NULL; - // prepare DL Allocation lists - nr_rrc_config_dl_tda(scc); + // Prepare PDSCH-TimeDomainResourceAllocation list + nr_rrc_config_dl_tda(scc, + scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList, + scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth); frame_type_t frame_type = get_frame_type((int)*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); if (frame_type == FDD) { diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index fabe00b832..7e38cac8a0 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1142,18 +1142,10 @@ void fill_default_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); - // Copy PDSCH TimeDomainResourceAllocation from InitialBWP - NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; - for (int i=0;idownlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) { - pdschi= calloc(1,sizeof(*pdschi)); - if(scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0){ - pdschi->k0 = calloc(1,sizeof(*pdschi->k0)); - *pdschi->k0 = *scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0; - } - pdschi->mappingType = scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->mappingType; - pdschi->startSymbolAndLength = scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; - ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); - } + // Prepare PDSCH-TimeDomainResourceAllocation list + nr_rrc_config_dl_tda(scc, + bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList, + curr_bwp); /// BWP dedicated configuration bwp->bwp_Dedicated=calloc(1,sizeof(*bwp->bwp_Dedicated)); diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index ff52fdb1ad..ef95443a5d 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -311,30 +311,37 @@ void prepare_sim_uecap(NR_UE_NR_Capability_t *cap, } } -void nr_rrc_config_dl_tda(NR_ServingCellConfigCommon_t *scc){ +void nr_rrc_config_dl_tda(NR_ServingCellConfigCommon_t *scc, + NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList, + int curr_bwp) { frame_type_t frame_type = get_frame_type(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); - int curr_bwp = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + // coreset duration setting to be improved in the framework of RRC harmonization, potentially using a common function int len_coreset = 1; if (curr_bwp < 48) len_coreset = 2; + // setting default TDA for DL with struct NR_PDSCH_TimeDomainResourceAllocation *timedomainresourceallocation = CALLOC(1,sizeof(NR_PDSCH_TimeDomainResourceAllocation_t)); + // k0: Slot offset between DCI and its scheduled PDSCH (see TS 38.214 clause 5.1.2.1) When the field is absent the UE applies the value 0. + //timedomainresourceallocation->k0 = calloc(1,sizeof(*timedomainresourceallocation->k0)); + //*timedomainresourceallocation->k0 = 0; timedomainresourceallocation->mappingType = NR_PDSCH_TimeDomainResourceAllocation__mappingType_typeA; timedomainresourceallocation->startSymbolAndLength = get_SLIV(len_coreset,14-len_coreset); // basic slot configuration starting in symbol 1 til the end of the slot - ASN_SEQUENCE_ADD(&scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list, - timedomainresourceallocation); + ASN_SEQUENCE_ADD(&pdsch_TimeDomainAllocationList->list, timedomainresourceallocation); if(frame_type==TDD) { // TDD if(scc->tdd_UL_DL_ConfigurationCommon) { int dl_symb = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSymbols; if(dl_symb > 1) { timedomainresourceallocation = CALLOC(1,sizeof(NR_PDSCH_TimeDomainResourceAllocation_t)); + // k0: Slot offset between DCI and its scheduled PDSCH (see TS 38.214 clause 5.1.2.1) When the field is absent the UE applies the value 0. + //timedomainresourceallocation->k0 = calloc(1,sizeof(*timedomainresourceallocation->k0)); + //*timedomainresourceallocation->k0 = 0; timedomainresourceallocation->mappingType = NR_PDSCH_TimeDomainResourceAllocation__mappingType_typeA; timedomainresourceallocation->startSymbolAndLength = get_SLIV(len_coreset,dl_symb-len_coreset); // mixed slot configuration starting in symbol 1 til the end of the dl allocation - ASN_SEQUENCE_ADD(&scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list, - timedomainresourceallocation); + ASN_SEQUENCE_ADD(&pdsch_TimeDomainAllocationList->list, timedomainresourceallocation); } } } diff --git a/openair2/RRC/NR/nr_rrc_config.h b/openair2/RRC/NR/nr_rrc_config.h index 4bc9513bc5..ca6e20512e 100644 --- a/openair2/RRC/NR/nr_rrc_config.h +++ b/openair2/RRC/NR/nr_rrc_config.h @@ -111,7 +111,9 @@ typedef struct physicalcellgroup_s{ long RNTI_Value[MAX_NUM_CCs]; }physicalcellgroup_t; -void nr_rrc_config_dl_tda(NR_ServingCellConfigCommon_t *scc); +void nr_rrc_config_dl_tda(NR_ServingCellConfigCommon_t *scc, + NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList, + int curr_bwp); void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay); void config_csirs(NR_ServingCellConfigCommon_t *servingcellconfigcommon, NR_CSI_MeasConfig_t *csi_MeasConfig, diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c index 867662b396..7e453d486a 100644 --- a/openair2/RRC/NR/rrc_gNB_reconfig.c +++ b/openair2/RRC/NR/rrc_gNB_reconfig.c @@ -196,18 +196,10 @@ void fill_default_nsa_downlinkBWP(NR_BWP_Downlink_t *bwp, bwp->bwp_Common->pdsch_ConfigCommon->choice.setup = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup)); bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList = calloc(1,sizeof(*bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList)); - // copy PDSCH TimeDomainResourceAllocation from InitialBWP - NR_PDSCH_TimeDomainResourceAllocation_t *pdschi; - for (int i=0;idownlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.count;i++) { - pdschi= calloc(1,sizeof(*pdschi)); - if(servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0){ - pdschi->k0 = calloc(1,sizeof(*pdschi->k0)); - *pdschi->k0 = *servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->k0; - } - pdschi->mappingType = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->mappingType; - pdschi->startSymbolAndLength = servingcellconfigcommon->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list.array[i]->startSymbolAndLength; - ASN_SEQUENCE_ADD(&bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList->list,pdschi); - } + // Prepare PDSCH-TimeDomainResourceAllocation list + nr_rrc_config_dl_tda(servingcellconfigcommon, + bwp->bwp_Common->pdsch_ConfigCommon->choice.setup->pdsch_TimeDomainAllocationList, + curr_bwp); /// BWP dedicated configuration From 2f3ce42d3b6105770ae3c515ba6d69c5c66fc04f Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 12 May 2022 17:04:09 +0100 Subject: [PATCH 224/233] Improve code readability in fix_scd function --- openair2/GNB_APP/gnb_config.c | 75 ++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 6e817aab8c..c11d6b8092 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -489,43 +489,44 @@ void fix_scd(NR_ServingCellConfig_t *scd) { // Check for DL PTRS parameters validity for (int bwp_i = 0 ; bwp_idownlinkBWP_ToAddModList->list.count; bwp_i++) { - if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + NR_DMRS_DownlinkConfig_t *dmrs_dl_config = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; + + if (dmrs_dl_config->phaseTrackingRS) { // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.count - 1; i >= 0; i--) { - if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] < 1) - || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] > 276)) { + for (int i = dmrs_dl_config->phaseTrackingRS->choice.setup->frequencyDensity->list.count - 1; i >= 0; i--) { + if ((*dmrs_dl_config->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] < 1) + || (*dmrs_dl_config->phaseTrackingRS->choice.setup->frequencyDensity->list.array[i] > 276)) { LOG_I(GNB_APP, "DL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); - scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; + free(dmrs_dl_config->phaseTrackingRS); + dmrs_dl_config->phaseTrackingRS = NULL; break; } } } - if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { + if (dmrs_dl_config->phaseTrackingRS) { // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = - scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.count - 1; i >= 0; i--) { - if ((*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] < 0) - || (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->timeDensity->list.array[i] > 29)) { + for (int i = dmrs_dl_config->phaseTrackingRS->choice.setup->timeDensity->list.count - 1; i >= 0; i--) { + if ((*dmrs_dl_config->phaseTrackingRS->choice.setup->timeDensity->list.array[i] < 0) + || (*dmrs_dl_config->phaseTrackingRS->choice.setup->timeDensity->list.array[i] > 29)) { LOG_I(GNB_APP, "DL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS); - scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS = NULL; + free(dmrs_dl_config->phaseTrackingRS); + dmrs_dl_config->phaseTrackingRS = NULL; break; } } } - if (scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS) { - if (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset > 2) { + if (dmrs_dl_config->phaseTrackingRS) { + if (*dmrs_dl_config->phaseTrackingRS->choice.setup->resourceElementOffset > 2) { LOG_I(GNB_APP, "Freeing DL PTRS resourceElementOffset \n"); - free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset); - scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->resourceElementOffset = NULL; + free(dmrs_dl_config->phaseTrackingRS->choice.setup->resourceElementOffset); + dmrs_dl_config->phaseTrackingRS->choice.setup->resourceElementOffset = NULL; } - if (*scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio > 1) { + if (*dmrs_dl_config->phaseTrackingRS->choice.setup->epre_Ratio > 1) { LOG_I(GNB_APP, "Freeing DL PTRS epre_Ratio \n"); - free(scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio); - scd->downlinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup->epre_Ratio = NULL; + free(dmrs_dl_config->phaseTrackingRS->choice.setup->epre_Ratio); + dmrs_dl_config->phaseTrackingRS->choice.setup->epre_Ratio = NULL; } } } @@ -533,38 +534,40 @@ void fix_scd(NR_ServingCellConfig_t *scd) { // Check for UL PTRS parameters validity for (int bwp_i = 0 ; bwp_iuplinkConfig->uplinkBWP_ToAddModList->list.count; bwp_i++) { - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + NR_DMRS_UplinkConfig_t *dmrs_ul_config = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + + if (dmrs_ul_config->phaseTrackingRS) { // If any of the frequencyDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.count-1; i >= 0; i--) { - if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) - || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] > 276)) { + for (int i = dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.count-1; i >= 0; i--) { + if ((*dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] < 1) + || (*dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->frequencyDensity->list.array[i] > 276)) { LOG_I(GNB_APP, "UL PTRS frequencyDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; + free(dmrs_ul_config->phaseTrackingRS); + dmrs_ul_config->phaseTrackingRS = NULL; break; } } } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + if (dmrs_ul_config->phaseTrackingRS) { // If any of the timeDensity values are not set or are out of bounds, PTRS is assumed to be not present - for (int i = scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.count-1; i >= 0; i--) { - if ((*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] < 0) - || (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] > 29)) { + for (int i = dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.count-1; i >= 0; i--) { + if ((*dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] < 0) + || (*dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->timeDensity->list.array[i] > 29)) { LOG_I(GNB_APP, "UL PTRS timeDensity %d not set. Assuming PTRS not present! \n", i); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS = NULL; + free(dmrs_ul_config->phaseTrackingRS); + dmrs_ul_config->phaseTrackingRS = NULL; break; } } } - if (scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS) { + if (dmrs_ul_config->phaseTrackingRS) { // Check for UL PTRS parameters validity - if (*scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { + if (*dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset > 2) { LOG_I(GNB_APP, "Freeing UL PTRS resourceElementOffset \n"); - free(scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); - scd->uplinkConfig->uplinkBWP_ToAddModList->list.array[bwp_i]->bwp_Dedicated->pusch_Config->choice.setup->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; + free(dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset); + dmrs_ul_config->phaseTrackingRS->choice.setup->transformPrecoderDisabled->resourceElementOffset = NULL; } } From 54f49fd25344ea43177a2f3afdbe2f3997ba4fa9 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Thu, 12 May 2022 17:33:35 +0100 Subject: [PATCH 225/233] Remove duplicated code for bwp common validation --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 11 +---- openair2/LAYER2/NR_MAC_UE/mac_proto.h | 2 + openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 43 ++++++++++++-------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index cc287872f1..b0d41f317b 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -508,16 +508,7 @@ void configure_ss_coreset(NR_UE_MAC_INST_t *mac, NR_ServingCellConfig_t *scd, NR_BWP_Id_t dl_bwp_id) { - - NR_BWP_DownlinkCommon_t *bwp_Common = NULL; - if(dl_bwp_id > 0 && scd->downlinkBWP_ToAddModList) { - bwp_Common = scd->downlinkBWP_ToAddModList->list.array[dl_bwp_id - 1]->bwp_Common; - } else if(mac->scc) { - bwp_Common = mac->scc->downlinkConfigCommon->initialDownlinkBWP; - } else if(mac->scc_SIB) { - bwp_Common = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; - } - + NR_BWP_DownlinkCommon_t *bwp_Common = get_bwp_downlink_common(mac, dl_bwp_id); AssertFatal(bwp_Common != NULL, "bwp_Common is null\n"); NR_SetupRelease_PDCCH_ConfigCommon_t *pdcch_ConfigCommon = bwp_Common->pdcch_ConfigCommon; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 86d72becef..ccd8ed8305 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -260,6 +260,8 @@ void get_bwp_info(NR_UE_MAC_INST_t *mac, NR_BWP_UplinkDedicated_t **ubwpd, NR_BWP_UplinkCommon_t **ubwpc); +NR_BWP_DownlinkCommon_t *get_bwp_downlink_common(NR_UE_MAC_INST_t *mac, NR_BWP_Id_t dl_bwp_id); + uint8_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, uint8_t dci_format, uint8_t dci_length, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index ccb90aa22f..dca9e1db56 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -191,24 +191,19 @@ void get_bwp_info(NR_UE_MAC_INST_t *mac, NR_BWP_UplinkDedicated_t **ubwpd, NR_BWP_UplinkCommon_t **ubwpc) { - if (dl_bwp_id > 0) { - AssertFatal(mac->DLbwp[dl_bwp_id-1]!=NULL,"mac->DLbwp[%d] is null, shouldn't be\n", - (int)dl_bwp_id-1); - *bwpd = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated; - if (mac->DLbwp[dl_bwp_id-1]->bwp_Common) *bwpc = mac->DLbwp[dl_bwp_id-1]->bwp_Common; - else if (mac->scc) *bwpc = mac->scc->downlinkConfigCommon->initialDownlinkBWP; - else if (mac->scc_SIB) *bwpc = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; - AssertFatal(*bwpc!=NULL,"bwpc shouldn't be null\n"); - } else { - if (mac->cg && - mac->cg->spCellConfig && - mac->cg->spCellConfig->spCellConfigDedicated && - mac->cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP) - *bwpd = mac->cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP; - if (mac->scc) *bwpc = mac->scc->downlinkConfigCommon->initialDownlinkBWP; - else if (mac->scc_SIB) *bwpc = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; - AssertFatal(*bwpc!=NULL,"bwpc shouldn't be null\n"); - } + if (dl_bwp_id > 0) { + AssertFatal(mac->DLbwp[dl_bwp_id-1]!=NULL,"mac->DLbwp[%d] is null, shouldn't be\n", (int)dl_bwp_id-1); + *bwpd = mac->DLbwp[dl_bwp_id-1]->bwp_Dedicated; + } else { + if (mac->cg && + mac->cg->spCellConfig && + mac->cg->spCellConfig->spCellConfigDedicated && + mac->cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP) + *bwpd = mac->cg->spCellConfig->spCellConfigDedicated->initialDownlinkBWP; + } + + *bwpc = get_bwp_downlink_common(mac, dl_bwp_id); + AssertFatal(*bwpc!=NULL,"bwpc shouldn't be null\n"); if (ul_bwp_id > 0) { AssertFatal(mac->ULbwp[ul_bwp_id-1]!=NULL,"mac->ULbwp[%d] is null, shouldn't be\n", @@ -233,6 +228,18 @@ void get_bwp_info(NR_UE_MAC_INST_t *mac, } } +NR_BWP_DownlinkCommon_t *get_bwp_downlink_common(NR_UE_MAC_INST_t *mac, NR_BWP_Id_t dl_bwp_id) { + NR_BWP_DownlinkCommon_t *bwp_Common = NULL; + if (dl_bwp_id > 0 && mac->cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList) { + bwp_Common = mac->cg->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[dl_bwp_id-1]->bwp_Common; + } else if (mac->scc) { + bwp_Common = mac->scc->downlinkConfigCommon->initialDownlinkBWP; + } else if (mac->scc_SIB) { + bwp_Common = &mac->scc_SIB->downlinkConfigCommon.initialDownlinkBWP; + } + return bwp_Common; +} + NR_PDSCH_TimeDomainResourceAllocationList_t *choose_dl_tda_list(NR_PDSCH_Config_t *pdsch_Config,NR_PDSCH_ConfigCommon_t *pdsch_ConfigCommon) { NR_PDSCH_TimeDomainResourceAllocationList_t *pdsch_TimeDomainAllocationList=NULL; From 09c3597b46e063d1d320ae9d6fdd2b2f198dfdee Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 12 May 2022 18:47:47 +0200 Subject: [PATCH 226/233] Only check T tracer if EPC has been defined --- ci-scripts/ran.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ci-scripts/ran.py b/ci-scripts/ran.py index 97c5b9696e..680763d247 100644 --- a/ci-scripts/ran.py +++ b/ci-scripts/ran.py @@ -383,17 +383,19 @@ class RANManagement(): # If tracer options is on, running tshark on EPC side and capture traffic b/ EPC and eNB - localEpcIpAddr = EPC.IPAddress - localEpcUserName = EPC.UserName - localEpcPassword = EPC.Password - mySSH.open(localEpcIpAddr, localEpcUserName, localEpcPassword) - eth_interface = 'any' - fltr = 'sctp' - logging.debug('\u001B[1m Launching tshark on interface ' + eth_interface + ' with filter "' + fltr + '"\u001B[0m') - self.epcPcapFile = 'enb_' + self.testCase_id + '_s1log.pcap' - mySSH.command('echo ' + localEpcPassword + ' | sudo -S rm -f /tmp/' + self.epcPcapFile , '\$', 5) - mySSH.command('echo $USER; nohup sudo tshark -f "host ' + lIpAddr +'" -i ' + eth_interface + ' -f "' + fltr + '" -w /tmp/' + self.epcPcapFile + ' > /tmp/tshark.log 2>&1 &', localEpcUserName, 5) - mySSH.close() + if EPC.IPAddress != "none": + localEpcIpAddr = EPC.IPAddress + localEpcUserName = EPC.UserName + localEpcPassword = EPC.Password + mySSH.open(localEpcIpAddr, localEpcUserName, localEpcPassword) + eth_interface = 'any' + fltr = 'sctp' + logging.debug('\u001B[1m Launching tshark on interface ' + eth_interface + ' with filter "' + fltr + '"\u001B[0m') + self.epcPcapFile = 'enb_' + self.testCase_id + '_s1log.pcap' + mySSH.command('echo ' + localEpcPassword + ' | sudo -S rm -f /tmp/' + self.epcPcapFile , '\$', 5) + mySSH.command('echo $USER; nohup sudo tshark -f "host ' + lIpAddr +'" -i ' + eth_interface + ' -f "' + fltr + '" -w /tmp/' + self.epcPcapFile + ' > /tmp/tshark.log 2>&1 &', localEpcUserName, 5) + mySSH.close() + mySSH.open(lIpAddr, lUserName, lPassWord) mySSH.command('cd ' + lSourcePath, '\$', 5) # Initialize_eNB_args usually start with -O and followed by the location in repository From a9dcdcbbcbbbadb7262d7db8e1217e0c00ee799d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 12 May 2022 20:54:02 +0200 Subject: [PATCH 227/233] Fix name of html module --- ci-scripts/cls_physim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-scripts/cls_physim.py b/ci-scripts/cls_physim.py index ecd8f63af1..0342333c06 100644 --- a/ci-scripts/cls_physim.py +++ b/ci-scripts/cls_physim.py @@ -168,7 +168,7 @@ class PhySim: mySSH.close() #check build status and update HTML object - lHTML = html.HTMLManagement() + lHTML = cls_oai_html.HTMLManagement() lHTML=self.__CheckBuild_PhySim(htmlObj,constObj) return lHTML @@ -186,6 +186,6 @@ class PhySim: mySSH.command(self.__workSpacePath+'ran_build/build/ldpctest ' + self.runargs + ' >> '+self.__runLogFile, '\$', 30) mySSH.close() #return updated HTML to main - lHTML = html.HTMLManagement() + lHTML = cls_oai_html.HTMLManagement() lHTML=self.__CheckResults_PhySim(htmlObj,constObj,testcase_id) return lHTML From 64f4d34c254d768d4103e50fb5cb0e442c2a51b9 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Thu, 12 May 2022 20:40:22 +0100 Subject: [PATCH 228/233] Fix tda in dlsim considering the new configuration for NSA in fill_default_nsa_downlinkBWP --- openair1/SIMULATION/NR_PHY/dlsim.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 5a4168624b..f007d64023 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -293,7 +293,7 @@ void nr_dlsim_preprocessor(module_id_t module_id, UE_info->CellGroup[0], sched_ctrl->active_bwp, NULL, - /* tda = */ 2, + /* tda = */ 0, g_nrOfLayers, sched_ctrl, ps); @@ -315,8 +315,7 @@ void nr_dlsim_preprocessor(module_id_t module_id, ps->N_PRB_DMRS * ps->N_DMRS_SLOT, 0 /* N_PRB_oh, 0 for initialBWP */, 0 /* tb_scaling */, - ps->nrOfLayers) - >> 3; + ps->nrOfLayers) >> 3; /* the simulator assumes the HARQ PID is equal to the slot number */ sched_pdsch->dl_harq_pid = slot; From 7c5a82bf1c671bbf95eb1beb7e3b1e74a2af9c82 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 12 May 2022 09:20:31 +0200 Subject: [PATCH 229/233] Format Hdashboard Gitlab post using Markdown --- ci-scripts/ran_dashboard/Hdashboard.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci-scripts/ran_dashboard/Hdashboard.py b/ci-scripts/ran_dashboard/Hdashboard.py index 1b65c7af4f..993d0dded8 100644 --- a/ci-scripts/ran_dashboard/Hdashboard.py +++ b/ci-scripts/ran_dashboard/Hdashboard.py @@ -528,15 +528,15 @@ class Dashboard: editable_mr = project.mergerequests.get(int(mr)) mr_notes = editable_mr.notes.list(all=True) - body = 'Consolidated Test Results
    '+\ - 'Tested CommitID: ' + commit + '
    ' + body = '[Consolidated Test Results](https://oaitestdashboard.s3.eu-west-1.amazonaws.com/MR'+mr+'/index.html)\\\n' + body += 'Tested CommitID: ' + commit + '\\\n' for i in range(0,n_tests): jobname = args[4*i] buildurl = args[4*i+1] buildid = args[4*i+2] status = args[4*i+3] - body += jobname+', status is '+status+', ('+buildid+')
    ' + body += jobname+': **'+status+'** ([' + buildid + '](' + buildurl + '))\\\n' #create new note mr_note = editable_mr.notes.create({ From 1c4bf402480f4e10415953cf134b83964e2e3081 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 13 May 2022 11:57:31 +0200 Subject: [PATCH 230/233] Fix caracal perf test: add option for N300, run only once (is consistent) --- ci-scripts/xml_files/gnb_phytest_usrp_run.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-scripts/xml_files/gnb_phytest_usrp_run.xml b/ci-scripts/xml_files/gnb_phytest_usrp_run.xml index 5705533ab4..e7eb651d50 100644 --- a/ci-scripts/xml_files/gnb_phytest_usrp_run.xml +++ b/ci-scripts/xml_files/gnb_phytest_usrp_run.xml @@ -24,7 +24,7 @@ gNB-PHY-Test Run-gNB-PHY-Test tasks - 3 + 1 090101 000001 090109 @@ -33,7 +33,7 @@ Initialize_eNB Initialize gNB USRP - -O ci-scripts/conf_files/gnb.band78.tm1.106PRB.usrpn300.conf --phy-test -q -U 787200 -T 106 -t 28 -D 130175 -m 28 -M 106 --log_config.global_log_options level,nocolor,time + -O ci-scripts/conf_files/gnb.band78.tm1.106PRB.usrpn300.conf --phy-test -q -U 787200 -T 106 -t 28 -D 130175 -m 28 -M 106 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time NR 192.168.20.2 From 9b2fa3ca63531b8b842c1cb51333f0aa7f53059b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 13 May 2022 14:14:47 +0200 Subject: [PATCH 231/233] Increase PUSCH decoding defaults --- ci-scripts/datalog_rt_stats.default.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci-scripts/datalog_rt_stats.default.yaml b/ci-scripts/datalog_rt_stats.default.yaml index 02a90c86f4..0da04dcaed 100644 --- a/ci-scripts/datalog_rt_stats.default.yaml +++ b/ci-scripts/datalog_rt_stats.default.yaml @@ -15,7 +15,8 @@ Ref : DLSCH encoding : 230.0 L1 Rx processing : 175.0 PUSCH inner-receiver : 100.0 - PUSCH decoding : 180.0 + #PUSCH decoding : 180.0 + PUSCH decoding : 240.0 DL & UL scheduling timing stats : 37.0 UL Indication : 38.0 Threshold : From cc238a91e4e92966d619a40cb93828bbce832968 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Fri, 13 May 2022 15:13:21 +0100 Subject: [PATCH 232/233] Compute TDA startSymbolIndex and nrOfSymbols everytime function nr_set_pdsch_semi_static is called --- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index a88d65ab0f..eed9313757 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -603,23 +603,21 @@ void nr_set_pdsch_semi_static(const NR_SIB1_t *sib1, NR_PDSCH_Config_t *pdsch_Config = NULL; if (bwpd && bwpd->pdsch_Config) pdsch_Config = bwpd->pdsch_Config->choice.setup; LOG_D(NR_MAC,"tda %d, ps->time_domain_allocation %d,layers %d, ps->nrOfLayers %d, pdsch_config %p\n",tda,ps->time_domain_allocation,layers,ps->nrOfLayers,pdsch_Config); - if (ps->time_domain_allocation != tda) { - reset_dmrs = true; - ps->time_domain_allocation = tda; - NR_PDSCH_TimeDomainResourceAllocationList_t *tdaList = get_pdsch_TimeDomainAllocationList(bwp, scc, sib1); - AssertFatal(tda < tdaList->list.count, "time_domain_allocation %d>=%d\n", tda, tdaList->list.count); - ps->mapping_type = tdaList->list.array[tda]->mappingType; - if (pdsch_Config) { - if (ps->mapping_type == NR_PDSCH_TimeDomainResourceAllocation__mappingType_typeB) - ps->dmrsConfigType = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->dmrs_Type == NULL ? 0 : 1; - else - ps->dmrsConfigType = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1; - } + reset_dmrs = true; + ps->time_domain_allocation = tda; + NR_PDSCH_TimeDomainResourceAllocationList_t *tdaList = get_pdsch_TimeDomainAllocationList(bwp, scc, sib1); + AssertFatal(tda < tdaList->list.count, "time_domain_allocation %d>=%d\n", tda, tdaList->list.count); + ps->mapping_type = tdaList->list.array[tda]->mappingType; + if (pdsch_Config) { + if (ps->mapping_type == NR_PDSCH_TimeDomainResourceAllocation__mappingType_typeB) + ps->dmrsConfigType = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup->dmrs_Type == NULL ? 0 : 1; else - ps->dmrsConfigType = NFAPI_NR_DMRS_TYPE1; - const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength; - SLIV2SL(startSymbolAndLength, &ps->startSymbolIndex, &ps->nrOfSymbols); + ps->dmrsConfigType = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->dmrs_Type == NULL ? 0 : 1; } + else + ps->dmrsConfigType = NFAPI_NR_DMRS_TYPE1; + const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength; + SLIV2SL(startSymbolAndLength, &ps->startSymbolIndex, &ps->nrOfSymbols); const long f = ((bwp || bwpd) && sched_ctrl->search_space && From 6c43cdf4dcf6215c0ef19a81fb66105280289e9e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 16 May 2022 21:39:06 +0200 Subject: [PATCH 233/233] Hack to prevent segfault --- common/utils/ocp_itti/intertask_interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/utils/ocp_itti/intertask_interface.cpp b/common/utils/ocp_itti/intertask_interface.cpp index 111a423dcb..43001ec95b 100644 --- a/common/utils/ocp_itti/intertask_interface.cpp +++ b/common/utils/ocp_itti/intertask_interface.cpp @@ -450,7 +450,7 @@ extern "C" { void itti_wait_tasks_end(void) { shutting_down = false; signal(SIGTERM, catch_sigterm); - signal(SIGINT, catch_sigterm); + //signal(SIGINT, catch_sigterm); while (! shutting_down) { sleep(24 * 3600);

    &7#;Zi<{%UbmkS_c2pl?SnDOV3ar*A!MTU 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®#FFXPBl-q2B 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 XJI&i2m|!?u8481QNlEEq^|<~6r_8hh literal 0 HcmV?d00001 diff --git a/doc/testbenches_doc_resources/5g-nsa-faraday-bench.png b/doc/testbenches_doc_resources/5g-nsa-faraday-bench.png new file mode 100644 index 0000000000000000000000000000000000000000..c677b598e9894a1bbf0e1e7321497e4c51e6fb17 GIT binary patch literal 89436 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 Xw=kVf=>HmO>>rBppYw?K*W&*G6`{5$ diff --git a/doc/testbenches_doc_resources/legacy1.jpg b/doc/testbenches_doc_resources/legacy1.jpg deleted file mode 100755 index c3579df3e4a56521eff8f23df1dbca9ba9fd3a59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107882 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 Q=k@ozuRl0u|MlB{0A&rZtN;K2 diff --git a/doc/testbenches_doc_resources/next_ci.jpg b/doc/testbenches_doc_resources/next_ci.jpg deleted file mode 100755 index 2d4111510c061b5d18ed1ae3097e3c6cc0040437..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157592 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�A(SmYoqJhO6gzHn~W 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 CdZAGO diff --git a/doc/testbenches_doc_resources/next_dev.jpg b/doc/testbenches_doc_resources/next_dev.jpg deleted file mode 100755 index 72e9f63bf91cdb7fd47a44780126b7269bf0475f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 185772 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