diff --git a/common/config/config_common.c b/common/config/config_common.c index d6e499b6a8..2bca71d912 100644 --- a/common/config/config_common.c +++ b/common/config/config_common.c @@ -108,6 +108,7 @@ void *config_allocate_new(configmodule_interface_t *cfg, int sz, bool autoFree) // add the memory piece in the managed memory pieces list pthread_mutex_lock(&cfg->memBlocks_mutex); int newBlockIdx=cfg->numptrs++; + AssertFatal(newBlockIdx < sizeofArray(cfg->oneBlock), "reached maximum number of dynamically allocatable blocks\n"); oneBlock_t* tmp=&cfg->oneBlock[newBlockIdx]; tmp->ptrs = (char *)ptr; tmp->ptrsAllocated = true; diff --git a/common/config/config_load_configmodule.h b/common/config/config_load_configmodule.h index bc38322805..3ec27d456f 100644 --- a/common/config/config_load_configmodule.h +++ b/common/config/config_load_configmodule.h @@ -41,7 +41,7 @@ #include "common/config/config_paramdesc.h" #include "common/utils/T/T.h" #define CONFIG_MAX_OOPT_PARAMS 10 // maximum number of parameters in the -O option (-O :P1:P2... -#define CONFIG_MAX_ALLOCATEDPTRS 2048 // maximum number of parameters that can be dynamicaly allocated in the config module +#define CONFIG_MAX_ALLOCATEDPTRS 32768 // maximum number of parameters that can be dynamicaly allocated in the config module /* default values for configuration module parameters */ #define CONFIG_LIBCONFIGFILE "libconfig" // use libconfig file diff --git a/common/utils/telnetsrv/CMakeLists.txt b/common/utils/telnetsrv/CMakeLists.txt index a7da081f09..5f190edc02 100644 --- a/common/utils/telnetsrv/CMakeLists.txt +++ b/common/utils/telnetsrv/CMakeLists.txt @@ -75,7 +75,13 @@ add_library(telnetsrv_rrc MODULE telnetsrv_rrc.c) target_link_libraries(telnetsrv_rrc PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_dependencies(telnetsrv telnetsrv_rrc) +message(STATUS "Add CI specific telnet functions in libtelnetsrv_o1.so") +add_library(telnetsrv_o1 MODULE telnetsrv_o1.c) +target_link_libraries(telnetsrv_o1 PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) +add_dependencies(telnetsrv telnetsrv_o1) + # all libraries should be written to root build dir -set_target_properties(telnetsrv telnetsrv_enb telnetsrv_5Gue telnetsrv_ci telnetsrv_ciUE telnetsrv_bearer telnetsrv_rrc +set_target_properties(telnetsrv telnetsrv_enb telnetsrv_5Gue telnetsrv_ci telnetsrv_ciUE + telnetsrv_bearer telnetsrv_rrc telnetsrv_o1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../../.. ) diff --git a/common/utils/telnetsrv/DOC/telneto1.md b/common/utils/telnetsrv/DOC/telneto1.md new file mode 100644 index 0000000000..4c6b9a3b4d --- /dev/null +++ b/common/utils/telnetsrv/DOC/telneto1.md @@ -0,0 +1,176 @@ +[[_TOC_]] + +The telnet O1 module (`telnetsrv_o1.c`) can be used to perform some O1-related +actions (reading data, starting and stopping the nr-softmodem, reconfigurating +frequency and bandwidth). + +# General usage + +The usage is similar to the general telnet usage, but in short: +``` +./build_oai --ninja -c --gNB --nrUE --build-lib telnetsrv +``` +to build everything including the telnet library. Then, run the nr-softmodem +by activating telnet and loading the `o1` module: +``` +./nr-softmodem -O --telnetsrv --telnetsrv.shrmod o1 +``` + +Afterwards, it should be possible to connect via telnet on localhost, port +9090. Use `help` to get help on the different command sections, and type e.g. +`o1 stats` to get statistics (more information further below): + +``` +$ telnet 127.0.0.1 9090 +Trying 127.0.0.1... +Connected to 127.0.0.1. +Escape character is '^]'. + +softmodem_gnb> help +[...] + module 4 = o1: + o1 stats + o1 config ? + o1 stop_modem + o1 start_modem +[...] +softmodem_gnb> o1 stats +[...] +softmodem_gnb> exit +Connection closed by foreign host. +``` + +It also possible to send a command "directly from the command line", by piping +the command into netcat: +``` +echo o1 stats | nc -N 127.0.0.1 9090 +``` + +Note that only one telnet client can be connected at a time. + +# Get statistics + +Use the `o1 stats` command. The output is in JSON format: +```json +{ + "o1-config": { + "BWP": { + "dl": [ + { + "bwp3gpp:isInitialBwp": true, + "bwp3gpp:numberOfRBs": 106, + "bwp3gpp:startRB": 0, + "bwp3gpp:subCarrierSpacing": 30 + } + ], + "ul": [ + { + "bwp3gpp:isInitialBwp": true, + "bwp3gpp:numberOfRBs": 106, + "bwp3gpp:startRB": 0, + "bwp3gpp:subCarrierSpacing": 30 + } + ] + }, + "NRCELLDU": { + "nrcelldu3gpp:ssbFrequency": 641280, + "nrcelldu3gpp:arfcnDL": 640008, + "nrcelldu3gpp:bSChannelBwDL": 40, + "nrcelldu3gpp:arfcnUL": 640008, + "nrcelldu3gpp:bSChannelBwUL": 40, + "nrcelldu3gpp:nRPCI": 0, + "nrcelldu3gpp:nRTAC": 1, + "nrcelldu3gpp:mcc": "208", + "nrcelldu3gpp:mnc": "95", + "nrcelldu3gpp:sd": 16777215, + "nrcelldu3gpp:sst": 1 + }, + "device": { + "gnbId": 1, + "gnbName": "gNB-Eurecom-5GNRBox", + "vendor": "OpenAirInterface" + } + }, + "O1-Operational": { + "frame-type": "tdd", + "band-number": 78, + "num-ues": 1, + "ues": [ + 6876 + ], + "load": 9, + "ues-thp": [ + { + "rnti": 6876, + "dl": 3279, + "ul": 2725 + } + ] + } +} +``` + +Note that no actual JSON engine is used, so no actual verification is done; it +is for convenience of the consumer. To verify, you can employ `jq`: +``` +echo o1 stats | nc -N 127.0.0.1 9090 | awk '/^{$/, /^}$/' | jq . +``` +(`awk`'s pattern matching makes that only everything between the first `{` and +its corresponding `}` is printed). + +There are two sections: +1. `.o1-config` show some stats that map directly to the O1 Netconf model. Note + that only one MCC/MNC/SD/SST (each) are supported right now. Also, note that + as per 3GPP specifications, SD of value `0xffffff` (16777215 in decimal) + means "no SD". `bSChannelBwDL/UL` is reported in MHz. +2. `.O1-operational` output some statistics that do not map yet to any netconf + parameters, but that might be useful nevertheless for a consumer. + +# Write a new configuration + +Use `o1 config` to write a configuration: +``` +echo o1 config nrcelldu3gpp:ssbFrequency 620736 nrcelldu3gpp:arfcnDL 620020 nrcelldu3gpp:bSChannelBwDL 51 bwp3gpp:numberOfRBs 51 bwp3gpp:startRB 0 | nc -N 127.0.0.1 9090 +``` +You have to pass the above parameters in exactly this order. The softmodem +needs to be stopped; it will pick up the new configuration when starting the +softmodem again. + +Note that you cannot switch three-quarter sampling for this as of now. + +For values of the configuration, refer to the next section. + +# Use hardcoded configuration + +Use `o1 bwconfig` to write a hard-coded configuration for 20 or 40 MHz cells: +``` +echo o1 bwconfig 20 | nc -N 127.0.0.1 9090 +echo o1 bwconfig 40 | nc -N 127.0.0.1 9090 +``` + +The softmodem needs to be stopped; it will pick up the new configuration when +starting the softmodem again. + +Use `o1 stats` to see which configurations are set by these commands for the +parameters `nrcelldu3gpp:ssbFrequency`, `nrcelldu3gpp:arfcnDL`, +`nrcelldu3gpp:arfcnUL`, `nrcelldu3gpp:bSChannelBwDL`, +`nrcelldu3gpp:bSChannelBwUL`, and `bwp3gpp:numberOfRBsbwp3gpp:startRB`. +Furthermore, for 20MHz, it *disables* three-quarter sampling, whereas it +*enables* three-quarter sampling for 40MHz. + +# Restart the softmodem + +Use `o1 stop_modem` to stop the `nr-softmodem`. To restart the softmodem, use +`o1 start_modem`: +``` +echo o1 stop_modem | nc -N 127.0.0.1 9090 +echo o1 start_modem | nc -N 127.0.0.1 9090 +``` + +In fact, stopping terminates all L1 threads. It will be as if the softmodem +"freezes", and no periodical output of statistics will occur (the O1 telnet +interface will still work, though). Starting again will "defreeze" the +softmodem. + +Upon restart, the DU sends a gNB-DU configuration update to the CU to inform it +about the updated configuration. Therefore, this also works in F1. diff --git a/common/utils/telnetsrv/DOC/telnetsrv.md b/common/utils/telnetsrv/DOC/telnetsrv.md index 11c6cf071f..68051e7df8 100644 --- a/common/utils/telnetsrv/DOC/telnetsrv.md +++ b/common/utils/telnetsrv/DOC/telnetsrv.md @@ -3,5 +3,6 @@ The oai embedded telnet server is an optional monitoring and debugging tool. It * [Using the telnet server](telnetusage.md) * [Adding commands to the oai telnet server](telnetaddcmd.md) * [telnet server architecture ](telnetarch.md) +* [on the telnet O1 module](telneto1.md) [oai Wikis home](https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/home) diff --git a/common/utils/telnetsrv/telnetsrv_o1.c b/common/utils/telnetsrv/telnetsrv_o1.c new file mode 100644 index 0000000000..dc48a477d3 --- /dev/null +++ b/common/utils/telnetsrv/telnetsrv_o1.c @@ -0,0 +1,418 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define TELNETSERVERCODE +#include "telnetsrv.h" + +#include "openair2/RRC/NR/nr_rrc_defs.h" +#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h" +#include "openair2/RRC/NR/nr_rrc_config.h" +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c" +#include "common/utils/nr/nr_common.h" + +#define ERROR_MSG_RET(mSG, aRGS...) do { prnt("FAILURE: " mSG, ##aRGS); return 1; } while (0) + +#define ISINITBWP "bwp3gpp:isInitialBwp" +//#define CYCLPREF "bwp3gpp:cyclicPrefix" +#define NUMRBS "bwp3gpp:numberOfRBs" +#define STARTRB "bwp3gpp:startRB" +#define BWPSCS "bwp3gpp:subCarrierSpacing" + +#define SSBFREQ "nrcelldu3gpp:ssbFrequency" +#define ARFCNDL "nrcelldu3gpp:arfcnDL" +#define BWDL "nrcelldu3gpp:bSChannelBwDL" +#define ARFCNUL "nrcelldu3gpp:arfcnUL" +#define BWUL "nrcelldu3gpp:bSChannelBwUL" +#define PCI "nrcelldu3gpp:nRPCI" +#define TAC "nrcelldu3gpp:nRTAC" +#define MCC "nrcelldu3gpp:mcc" +#define MNC "nrcelldu3gpp:mnc" +#define SD "nrcelldu3gpp:sd" +#define SST "nrcelldu3gpp:sst" + +typedef struct b { + long int dl; + long int ul; +} b_t; + +typedef struct ue_stat { + rnti_t rnti; + b_t thr; +} ue_stat_t; + +#define PRINTLIST_i(len, fmt, ...) \ + { \ + for (int i = 0; i < len; ++i) { \ + if (i != 0) prnt(", "); \ + prnt(fmt, __VA_ARGS__); \ + } \ + } \ + +static int get_stats(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (buf) + ERROR_MSG_RET("no parameter allowed\n"); + + gNB_MAC_INST *mac = RC.nrmac[0]; + AssertFatal(mac != NULL, "need MAC\n"); + NR_SCHED_LOCK(&mac->sched_lock); + + const f1ap_setup_req_t *sr = mac->f1_config.setup_req; + const f1ap_served_cell_info_t *cell_info = &sr->cell[0].info; + + const NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; + const NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; + const NR_FrequencyInfoUL_t *frequencyInfoUL = scc->uplinkConfigCommon->frequencyInfoUL; + frame_type_t frame_type = get_frame_type(*frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); + const NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + const NR_BWP_t *initialUL = &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + + int scs = initialDL->subcarrierSpacing; + AssertFatal(scs == initialUL->subcarrierSpacing, "different SCS for UL/DL not supported!\n"); + int band = *frequencyInfoDL->frequencyBandList.list.array[0]; + int nrb = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + AssertFatal(nrb == frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth, "different BW for UL/DL not supported!\n"); + frequency_range_t fr = band > 256 ? FR2 : FR1; + int bw_index = get_supported_band_index(scs, fr, nrb); + int bw_mhz = get_supported_bw_mhz(fr, bw_index); + + const mac_stats_t *stat = &mac->mac_stats; + static mac_stats_t last = {0}; + int diff_used = stat->used_prb_aggregate - last.used_prb_aggregate; + int diff_total = stat->total_prb_aggregate - last.total_prb_aggregate; + int load = diff_total > 0 ? 100 * diff_used / diff_total : 0; + last = *stat; + + static struct timespec tp_last = {0}; + struct timespec tp_now; + clock_gettime(CLOCK_MONOTONIC, &tp_now); + size_t diff_msec = (tp_now.tv_sec - tp_last.tv_sec) * 1000 + (tp_now.tv_nsec - tp_last.tv_nsec) / 1000000; + tp_last = tp_now; + + const int srb_flag = 0; + const int rb_id = 1; + static b_t last_total[MAX_MOBILES_PER_GNB] = {0}; // TODO: hash table? + ue_stat_t ue_stat[MAX_MOBILES_PER_GNB] = {0}; + int num_ues = 0; + UE_iterator((NR_UE_info_t **)mac->UE_info.list, it) { + nr_rlc_statistics_t rlc = {0}; + nr_rlc_get_statistics(it->rnti, srb_flag, rb_id, &rlc); + b_t *lt = &last_total[num_ues]; + ue_stat_t *ue_s = &ue_stat[num_ues]; + ue_s->rnti = it->rnti; + // static var last_total: we might have old data, larger than what + // reports RLC, leading to a huge number -> cut off to zero + if (lt->dl > rlc.txpdu_bytes) + lt->dl = rlc.txpdu_bytes; + if (lt->ul > rlc.rxpdu_bytes) + lt->ul = rlc.rxpdu_bytes; + ue_s->thr.dl = (rlc.txpdu_bytes - lt->dl) * 8 / diff_msec; + ue_s->thr.ul = (rlc.rxpdu_bytes - lt->ul) * 8 / diff_msec; + lt->dl = rlc.txpdu_bytes; + lt->ul = rlc.rxpdu_bytes; + num_ues++; + } + + prnt("{\n"); + prnt(" \"o1-config\": {\n"); + + prnt(" \"BWP\": {\n"); + prnt(" \"dl\": [{\n"); + prnt(" \"" ISINITBWP "\": true,\n"); + //prnt(" \"" CYCLPREF "\": %ld,\n", *initialDL->cyclicPrefix); + prnt(" \"" NUMRBS "\": %ld,\n", NRRIV2BW(initialDL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" STARTRB "\": %ld,\n", NRRIV2PRBOFFSET(initialDL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" BWPSCS "\": %ld\n", 15 * (1U << scs)); + prnt(" }],\n"); + prnt(" \"ul\": [{\n"); + prnt(" \"" ISINITBWP "\": true,\n"); + //prnt(" \"" CYCLPREF "\": %ld,\n", *initialUL->cyclicPrefix); + prnt(" \"" NUMRBS "\": %ld,\n", NRRIV2BW(initialUL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" STARTRB "\": %ld,\n", NRRIV2PRBOFFSET(initialUL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" BWPSCS "\": %ld\n", 15 * (1U << scs)); + prnt(" }]\n"); + prnt(" },\n"); + + prnt(" \"NRCELLDU\": {\n"); + prnt(" \"" SSBFREQ "\": %ld,\n", *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB); + prnt(" \"" ARFCNDL "\": %ld,\n", frequencyInfoDL->absoluteFrequencyPointA); + prnt(" \"" BWDL "\": %ld,\n", bw_mhz); + prnt(" \"" ARFCNUL "\": %ld,\n", frequencyInfoUL->absoluteFrequencyPointA ? *frequencyInfoUL->absoluteFrequencyPointA : frequencyInfoDL->absoluteFrequencyPointA); + prnt(" \"" BWUL "\": %ld,\n", bw_mhz); + prnt(" \"" PCI "\": %ld,\n", *scc->physCellId); + prnt(" \"" TAC "\": %ld,\n", *cell_info->tac); + prnt(" \"" MCC "\": \"%03d\",\n", cell_info->plmn.mcc); + prnt(" \"" MNC "\": \"%0*d\",\n", cell_info->plmn.mnc_digit_length, cell_info->plmn.mnc); + prnt(" \"" SD "\": %d,\n", cell_info->nssai[0].sd); + prnt(" \"" SST "\": %d\n", cell_info->nssai[0].sst); + prnt(" },\n"); + prnt(" \"device\": {\n"); + prnt(" \"gnbId\": %d,\n", sr->gNB_DU_id); + prnt(" \"gnbName\": \"%s\",\n", sr->gNB_DU_name); + prnt(" \"vendor\": \"OpenAirInterface\"\n"); + prnt(" }\n"); + prnt(" },\n"); + + prnt(" \"O1-Operational\": {\n"); + prnt(" \"frame-type\": \"%s\",\n", frame_type == TDD ? "tdd" : "fdd"); + prnt(" \"band-number\": %ld,\n", band); + prnt(" \"num-ues\": %d,\n", num_ues); + prnt(" \"ues\": ["); PRINTLIST_i(num_ues, "%d", ue_stat[i].rnti); prnt("],\n"); + prnt(" \"load\": %d,\n", load); + prnt(" \"ues-thp\": ["); + PRINTLIST_i(num_ues, "\n {\"rnti\": %d, \"dl\": %ld, \"ul\": %ld}", ue_stat[i].rnti, ue_stat[i].thr.dl, ue_stat[i].thr.ul); + prnt("\n ]\n"); + prnt(" }\n"); + prnt("}\n"); + prnt("OK\n"); + NR_SCHED_UNLOCK(&mac->sched_lock); + return 0; +} + +static int read_long(const char *buf, const char *end, const char *id, long *val) +{ + const char *curr = buf; + while (isspace(*curr) && curr < end) // skip leading spaces + curr++; + int len = strlen(id); + if (curr + len >= end) + return -1; + if (strncmp(curr, id, len) != 0) // check buf has id + return -1; + curr += len; + while (isspace(*curr) && curr < end) // skip middle spaces + curr++; + if (curr >= end) + return -1; + int nread = sscanf(curr, "%ld", val); + if (nread != 1) + return -1; + while (isdigit(*curr) && curr < end) // skip all digits read above + curr++; + if (curr > end) + return -1; + return curr - buf; +} + +bool running = true; // in the beginning, the softmodem is started automatically +static int set_config(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (!buf) + ERROR_MSG_RET("need param: o1 config param1 val1 [param2 val2 ...]\n"); + if (running) + ERROR_MSG_RET("cannot set parameters while L1 is running\n"); + const char *end = buf + strlen(buf); + + /* we need to update the following fields to change frequency and/or + * bandwidth: + * --gNBs.[0].servingCellConfigCommon.[0].absoluteFrequencySSB 620736 -> SSBFREQ + * --gNBs.[0].servingCellConfigCommon.[0].dl_absoluteFrequencyPointA 620020 -> ARFCNDL + * --gNBs.[0].servingCellConfigCommon.[0].dl_carrierBandwidth 51 -> BWDL + * --gNBs.[0].servingCellConfigCommon.[0].initialDLBWPlocationAndBandwidth 13750 -> NUMRBS + STARTRB + * --gNBs.[0].servingCellConfigCommon.[0].ul_carrierBandwidth 51 -> BWUL? + * --gNBs.[0].servingCellConfigCommon.[0].initialULBWPlocationAndBandwidth 13750 -> ? + */ + + int processed = 0; + int pos = 0; + + long ssbfreq; + processed = read_long(buf + pos, end, SSBFREQ, &ssbfreq); + if (processed < 0) + ERROR_MSG_RET("could not read " SSBFREQ " at index %d\n", pos + processed); + pos += processed; + prnt("setting " SSBFREQ ": %ld [len %d]\n", ssbfreq, pos); + + long arfcn; + processed = read_long(buf + pos, end, ARFCNDL, &arfcn); + if (processed < 0) + ERROR_MSG_RET("could not read " ARFCNDL " at index %d\n", pos + processed); + pos += processed; + prnt("setting " ARFCNDL ": %ld [len %d]\n", arfcn, pos); + + long bwdl; + processed = read_long(buf + pos, end, BWDL, &bwdl); + if (processed < 0) + ERROR_MSG_RET("could not read " BWDL " at index %d\n", pos + processed); + pos += processed; + prnt("setting " BWDL ": %ld [len %d]\n", bwdl, pos); + + long numrbs; + processed = read_long(buf + pos, end, NUMRBS, &numrbs); + if (processed < 0) + ERROR_MSG_RET("could not read " NUMRBS " at index %d\n", pos + processed); + pos += processed; + prnt("setting " NUMRBS ": %ld [len %d]\n", numrbs, pos); + + long startrb; + processed = read_long(buf + pos, end, STARTRB, &startrb); + if (processed < 0) + ERROR_MSG_RET("could not read " STARTRB " at index %d\n", pos + processed); + pos += processed; + prnt("setting " STARTRB ": %ld [len %d]\n", startrb, pos); + + int locationAndBandwidth = PRBalloc_to_locationandbandwidth0(numrbs, startrb, MAX_BWP_SIZE); + prnt("inferred locationAndBandwidth: %d\n", locationAndBandwidth); + prnt("OK\n"); + return 0; +} + +static int set_bwconfig(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (running) + ERROR_MSG_RET("cannot set parameters while L1 is running\n"); + if (!buf) + ERROR_MSG_RET("need param: o1 bwconfig \n"); + + char *end = NULL; + if (NULL != (end = strchr(buf, '\n'))) + *end = 0; + if (NULL != (end = strchr(buf, '\r'))) + *end = 0; + + gNB_MAC_INST *mac = RC.nrmac[0]; + NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; + NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; + NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + NR_FrequencyInfoUL_t *frequencyInfoUL = scc->uplinkConfigCommon->frequencyInfoUL; + NR_BWP_t *initialUL = &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + if (strcmp(buf, "40") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 641280; + frequencyInfoDL->absoluteFrequencyPointA = 640008; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 106; + initialDL->locationAndBandwidth = 28875; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 106; + initialUL->locationAndBandwidth = 28875; + get_softmodem_params()->threequarter_fs = 1; + } else if (strcmp(buf, "20") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 641280; + frequencyInfoDL->absoluteFrequencyPointA = 640596; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 51; + initialDL->locationAndBandwidth = 13750; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 51; + initialUL->locationAndBandwidth = 13750; + get_softmodem_params()->threequarter_fs = 0; + } else if (strcmp(buf, "100") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 646668; + frequencyInfoDL->absoluteFrequencyPointA = 643392; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 273; + initialDL->locationAndBandwidth = 1099; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 273; + initialUL->locationAndBandwidth = 1099; + get_softmodem_params()->threequarter_fs = 0; + } else if (strcmp(buf, "60") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 621984; + frequencyInfoDL->absoluteFrequencyPointA = 620040; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 162; + initialDL->locationAndBandwidth = 31624; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 162; + initialUL->locationAndBandwidth = 31624; + get_softmodem_params()->threequarter_fs = 0; + } else { + ERROR_MSG_RET("unhandled option %s\n", buf); + } + + free(RC.nrmac[0]->sched_ctrlCommon); + RC.nrmac[0]->sched_ctrlCommon = NULL; + + free_MIB_NR(mac->common_channels[0].mib); + mac->common_channels[0].mib = get_new_MIB_NR(scc); + + const f1ap_served_cell_info_t *info = &mac->f1_config.setup_req->cell[0].info; + nr_mac_configure_sib1(mac, &info->plmn, info->nr_cellid, *info->tac); + + prnt("OK\n"); + return 0; +} + +extern int stop_L1(module_id_t gnb_id); +static int stop_modem(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (!running) + ERROR_MSG_RET("cannot stop, nr-softmodem not running\n"); + + /* make UEs out of sync and wait 50ms to ensure no PUCCH is scheduled. After + * a restart, the frame/slot numbers will be different, which "confuses" the + * scheduler, which has many PUCCH structures filled with expected frame/slot + * combinations that won't happen. */ + const gNB_MAC_INST *mac = RC.nrmac[0]; + UE_iterator((NR_UE_info_t **)mac->UE_info.list, it) { + nr_mac_trigger_ul_failure(&it->UE_sched_ctrl, 1); + } + usleep(50000); + + stop_L1(0); + running = false; + prnt("OK\n"); + return 0; +} + +extern int start_L1L2(module_id_t gnb_id); +static int start_modem(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (running) + ERROR_MSG_RET("cannot start, nr-softmodem already running\n"); + start_L1L2(0); + running = true; + prnt("OK\n"); + return 0; +} + +extern void du_clear_all_ue_states(); +static int remove_mac_ues(char *buf, int debug, telnet_printfunc_t prnt) +{ + du_clear_all_ue_states(); + prnt("OK\n"); + return 0; +} + +static telnetshell_cmddef_t o1cmds[] = { + {"stats", "", get_stats}, + {"config", "[]", set_config}, + {"bwconfig", "", set_bwconfig}, + {"stop_modem", "", stop_modem}, + {"start_modem", "", start_modem}, + {"remove_mac_ues", "", remove_mac_ues}, + {"", "", NULL}, +}; + +static telnetshell_vardef_t o1vars[] = { + {"", 0, 0, NULL} +}; + +void add_o1_cmds(void) { + add_telnetcmd("o1", o1vars, o1cmds); +} diff --git a/docker/Dockerfile.gNB.ubuntu22 b/docker/Dockerfile.gNB.ubuntu22 index 8d154eeb13..35e442f93d 100644 --- a/docker/Dockerfile.gNB.ubuntu22 +++ b/docker/Dockerfile.gNB.ubuntu22 @@ -95,6 +95,10 @@ COPY --from=gnb-build \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \ /oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_bearer.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_rrc.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_o1.so \ /usr/local/lib/ # Now we are copying from builder-image the UHD files. diff --git a/executables/nr-cuup.c b/executables/nr-cuup.c index 381e56bd2f..978486f261 100644 --- a/executables/nr-cuup.c +++ b/executables/nr-cuup.c @@ -145,6 +145,9 @@ f1ap_cudu_inst_t *getCxt(instance_t instanceP) return &fake; } configmodule_interface_t *uniqCfg = NULL; + +void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, uint32_t gNB_ue_ngap_id) { }; + int main(int argc, char **argv) { /// static configuration for NR at the moment diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 4636395f65..8098b75a56 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -383,12 +383,16 @@ void init_gNB_Tpool(int inst) { void term_gNB_Tpool(int inst) { PHY_VARS_gNB *gNB = RC.gNB[inst]; - abortTpool(&gNB->threadPool); - abortNotifiedFIFO(&gNB->respDecode); abortNotifiedFIFO(&gNB->resp_L1); + pthread_join(gNB->L1_rx_thread, NULL); + abortNotifiedFIFO(&gNB->L1_tx_out); + pthread_join(gNB->L1_tx_thread, NULL); + + abortTpool(&gNB->threadPool); + abortNotifiedFIFO(&gNB->respPuschSymb); + abortNotifiedFIFO(&gNB->respDecode); abortNotifiedFIFO(&gNB->L1_tx_free); abortNotifiedFIFO(&gNB->L1_tx_filled); - abortNotifiedFIFO(&gNB->L1_tx_out); abortNotifiedFIFO(&gNB->L1_rx_out); gNB_L1_proc_t *proc = &gNB->proc; diff --git a/executables/nr-ru.c b/executables/nr-ru.c index dd31088ff8..37e520431c 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1443,6 +1443,12 @@ void kill_NR_RU_proc(int inst) { RU_t *ru = RC.ru[inst]; RU_proc_t *proc = &ru->proc; + if (ru->if_south != REMOTE_IF4p5) { + abortTpool(ru->threadPool); + abortNotifiedFIFO(ru->respfeprx); + abortNotifiedFIFO(ru->respfeptx); + } + /* Note: it seems pthread_FH and and FEP thread below both use * mutex_fep/cond_fep. Thus, we unlocked above for pthread_FH above and do * the same for FEP thread below again (using broadcast() to ensure both diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 8de047ea06..96527fb303 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -401,6 +401,105 @@ void terminate_task(task_id_t task_id, module_id_t mod_id) { //extern void free_transport(PHY_VARS_gNB *); extern void nr_phy_free_RU(RU_t *); +int stop_L1(module_id_t gnb_id) +{ + RU_t *ru = RC.ru[gnb_id]; + if (!ru) { + LOG_W(GNB_APP, "no RU configured\n"); + return -1; + } + + if (!RC.gNB[gnb_id]->configured) { + LOG_W(GNB_APP, "L1 already stopped\n"); + return -1; + } + + LOG_I(GNB_APP, "stopping nr-softmodem\n"); + oai_exit = 1; + + /* these tasks/layers need to pick up new configuration */ + if (RC.nb_nr_L1_inst > 0) + stop_gNB(RC.nb_nr_L1_inst); + + if (RC.nb_RU > 0) + stop_RU(RC.nb_RU); + + /* stop trx devices, multiple carrier currently not supported by RU */ + if (ru->rfdevice.trx_stop_func) { + ru->rfdevice.trx_stop_func(&ru->rfdevice); + LOG_I(GNB_APP, "turned off RU rfdevice\n"); + } + + if (ru->ifdevice.trx_stop_func) { + ru->ifdevice.trx_stop_func(&ru->ifdevice); + LOG_I(GNB_APP, "turned off RU ifdevice\n"); + } + + /* release memory used by the RU/gNB threads (incomplete), after all + * threads have been stopped (they partially use the same memory) */ + for (int inst = 0; inst < RC.nb_RU; inst++) { + nr_phy_free_RU(RC.ru[inst]); + } + + for (int inst = 0; inst < RC.nb_nr_L1_inst; inst++) { + phy_free_nr_gNB(RC.gNB[inst]); + } + + RC.gNB[gnb_id]->configured = 0; + return 0; +} + +/* + * Restart the nr-softmodem after it has been soft-stopped with stop_L1L2() + */ +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +int start_L1L2(module_id_t gnb_id) +{ + LOG_I(GNB_APP, "starting nr-softmodem\n"); + /* block threads */ + oai_exit = 0; + sync_var = -1; + extern void init_sched_response(void); + init_sched_response(); + + /* update config */ + gNB_MAC_INST *mac = RC.nrmac[0]; + NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; + nr_mac_config_scc(mac, scc, &mac->radio_config); + + NR_BCCH_BCH_Message_t *mib = mac->common_channels[0].mib; + const NR_BCCH_DL_SCH_Message_t *sib1 = mac->common_channels[0].sib1; + + /* update existing config in F1 Setup request structures */ + f1ap_setup_req_t *sr = mac->f1_config.setup_req; + DevAssert(sr->num_cells_available == 1); + f1ap_served_cell_info_t *info = &sr->cell[0].info; + DevAssert(info->mode == F1AP_MODE_TDD); + DevAssert(scc->tdd_UL_DL_ConfigurationCommon != NULL); + info->tdd = read_tdd_config(scc); /* updates radio config */ + /* send gNB-DU configuration update to RRC */ + f1ap_gnb_du_configuration_update_t update = { + .transaction_id = 1, + .num_cells_to_modify = 1, + }; + update.cell_to_modify[0].old_nr_cellid = info->nr_cellid; + update.cell_to_modify[0].info = *info; + update.cell_to_modify[0].sys_info = get_sys_info(mib, sib1); + mac->mac_rrc.gnb_du_configuration_update(&update); + + init_NR_RU(config_get_if(), NULL); + + start_NR_RU(); + wait_RUs(); + init_eNB_afterRU(); + LOG_I(GNB_APP, "Sending sync to all threads\n"); + pthread_mutex_lock(&sync_mutex); + sync_var=0; + pthread_cond_broadcast(&sync_cond); + pthread_mutex_unlock(&sync_mutex); + return 0; +} + static void wait_nfapi_init(char *thread_name) { pthread_mutex_lock( &nfapi_sync_mutex ); @@ -629,37 +728,15 @@ int main( int argc, char **argv ) { printf("TYPE TO TERMINATE\n"); itti_wait_tasks_end(NULL); printf("Returned from ITTI signal handler\n"); - oai_exit=1; - // cleanup - if (RC.nb_nr_L1_inst > 0) - stop_gNB(RC.nb_nr_L1_inst); - - if (RC.nb_RU > 0) - stop_RU(RC.nb_RU); - - /* release memory used by the RU/gNB threads (incomplete), after all - * threads have been stopped (they partially use the same memory) */ - for (int inst = 0; inst < RC.nb_RU; inst++) { - nr_phy_free_RU(RC.ru[inst]); - } - - for (int inst = 0; inst < RC.nb_nr_L1_inst; inst++) { - phy_free_nr_gNB(RC.gNB[inst]); - } + if (RC.nb_nr_L1_inst > 0 || RC.nb_RU > 0) + stop_L1(0); pthread_cond_destroy(&sync_cond); pthread_mutex_destroy(&sync_mutex); pthread_cond_destroy(&nfapi_sync_cond); pthread_mutex_destroy(&nfapi_sync_mutex); - // *** Handle per CC_id openair0 - - for(ru_id = 0; ru_id < RC.nb_RU; ru_id++) { - if (RC.ru[ru_id]->ifdevice.trx_end_func) - RC.ru[ru_id]->ifdevice.trx_end_func(&RC.ru[ru_id]->ifdevice); - } - free(pckg); logClean(); printf("Bye.\n"); diff --git a/executables/nr-softmodem.h b/executables/nr-softmodem.h index 4311777f6c..ecbf5d8759 100644 --- a/executables/nr-softmodem.h +++ b/executables/nr-softmodem.h @@ -52,7 +52,7 @@ extern void stop_RU(int nb_ru); extern void kill_NR_RU_proc(int inst); extern void set_function_spec_param(RU_t *ru); -void init_gNB_afterRU(void); +void init_eNB_afterRU(void); void init_pdcp(void); diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index bf447645d6..674c70cdc3 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -180,4 +180,8 @@ void nr_phy_free_RU(RU_t *ru) free_and_zero(ru->common.beam_id[i]); free_and_zero(ru->common.beam_id); } + + PHY_VARS_gNB *gNB0 = ru->gNB_list[0]; + gNB0->num_RU--; + DevAssert(gNB0->num_RU >= 0); } diff --git a/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c b/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c index 22fe1b0d13..baa2734f87 100644 --- a/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c +++ b/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c @@ -177,10 +177,9 @@ c16_t *gNB_dmrs_lowpaprtype1_sequence[U_GROUP_NUMBER][V_BASE_SEQUENCE_NUMBER][MA void generate_lowpapr_typ1_refsig_sequences(unsigned int scaling) { /* prevent multiple calls, relevant when both UE & gNB initialize this */ - static bool already_called = false; + bool already_called = gNB_dmrs_lowpaprtype1_sequence[0][0][0] != NULL; if (already_called) return; - already_called = true; unsigned int v = 0; // sequence hopping and group hopping are not supported yet for (unsigned int Msc_RS = 0; Msc_RS <= INDEX_SB_LESS_32; Msc_RS++) { @@ -200,9 +199,8 @@ c16_t *dmrs_lowpaprtype1_ul_ref_sig[U_GROUP_NUMBER][V_BASE_SEQUENCE_NUMBER][MAX_ void generate_ul_reference_signal_sequences(unsigned int scaling) { /* prevent multiple calls, relevant when both UE & gNB initialize this */ - static bool already_called = false; + bool already_called = rv_ul_ref_sig[0][0][0] != NULL; if (already_called) return; - already_called = true; unsigned int u,v,Msc_RS; diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 08bf2e8fe6..de69fb6c16 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -467,6 +467,7 @@ int main(int argc, char **argv){ ru->nb_rx = n_rx; ru->num_gNB = 1; ru->gNB_list[0] = gNB; + gNB->num_RU = 1; gNB->gNB_config.carrier_config.num_tx_ant.value = 1; gNB->gNB_config.carrier_config.num_rx_ant.value = 1; if (mu == 0) diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c index aed1ae6d23..bc5480a11b 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.c +++ b/openair2/F1AP/f1ap_cu_interface_management.c @@ -143,7 +143,6 @@ int CU_handle_gNB_DU_CONFIGURATION_UPDATE(instance_t instance, sctp_assoc_t asso message_p->ittiMsgHeader.originInstance = assoc_id; f1ap_gnb_du_configuration_update_t *req = &F1AP_GNB_DU_CONFIGURATION_UPDATE(message_p); // RRC thread will free it *req = msg; // copy F1 message to ITTI - free_f1ap_du_configuration_update(&msg); LOG_D(F1AP, "Sending F1AP_GNB_DU_CONFIGURATION_UPDATE ITTI message \n"); itti_send_msg_to_task(TASK_RRC_GNB, GNB_MODULE_ID_TO_INSTANCE(instance), message_p); return 0; diff --git a/openair2/F1AP/f1ap_du_interface_management.c b/openair2/F1AP/f1ap_du_interface_management.c index 8f6114a9e5..f12b104263 100644 --- a/openair2/F1AP/f1ap_du_interface_management.c +++ b/openair2/F1AP/f1ap_du_interface_management.c @@ -262,12 +262,9 @@ int DU_handle_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, free_f1ap_du_configuration_update_acknowledge(&in); return -1; } - // Allocate and send an ITTI message - MessageDef *msg_p = itti_alloc_new_message(TASK_DU_F1, 0, F1AP_GNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE); - f1ap_gnb_du_configuration_update_acknowledge_t *msg = &F1AP_GNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(msg_p); - // Copy the decoded message to the ITTI message (RRC thread will free it) - *msg = in; // Copy the decoded message to the ITTI message (RRC thread will free it) - itti_send_msg_to_task(TASK_GNB_APP, GNB_MODULE_ID_TO_INSTANCE(assoc_id), msg_p); + + gnb_du_configuration_update_acknowledge(&in); + free_f1ap_du_configuration_update_acknowledge(&in); return 0; } diff --git a/openair2/F1AP/f1ap_du_rrc_message_transfer.h b/openair2/F1AP/f1ap_du_rrc_message_transfer.h index 4064a7cd2a..3c3bc228ad 100644 --- a/openair2/F1AP/f1ap_du_rrc_message_transfer.h +++ b/openair2/F1AP/f1ap_du_rrc_message_transfer.h @@ -34,9 +34,10 @@ #ifndef F1AP_DU_RRC_MESSAGE_TRANSFER_H_ #define F1AP_DU_RRC_MESSAGE_TRANSFER_H_ -#include "f1ap_common.h" +#include -int DU_handle_DL_RRC_MESSAGE_TRANSFER(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu); +struct F1AP_F1AP_PDU; +int DU_handle_DL_RRC_MESSAGE_TRANSFER(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, struct F1AP_F1AP_PDU *pdu); int DU_send_UL_NR_RRC_MESSAGE_TRANSFER(sctp_assoc_t assoc_id, const f1ap_ul_rrc_message_t *msg); diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index f067985a2d..182f40952d 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -173,7 +173,7 @@ static bool decode_served_cell_info(const F1AP_Served_Cell_Information_t *in, f1 OCTET_STRING_TO_INT24(in->fiveGS_TAC, *info->tac); } // NR CGI (M) - TBCD_TO_MCC_MNC(&(in->nRCGI.pLMN_Identity), info->plmn.mcc, info->plmn.mnc, info->plmn.mnc_digit_length); + PLMNID_TO_MCC_MNC(&(in->nRCGI.pLMN_Identity), info->plmn.mcc, info->plmn.mnc, info->plmn.mnc_digit_length); // NR Cell Identity (M) BIT_STRING_TO_NR_CELL_IDENTITY(&in->nRCGI.nRCellIdentity, info->nr_cellid); // NR PCI (M) @@ -310,7 +310,7 @@ static bool decode_cells_to_activate(served_cells_to_activate_t *out, const F1AP "in->value.present != F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item\n"); const F1AP_Cells_to_be_Activated_List_Item_t *cell = &in->value.choice.Cells_to_be_Activated_List_Item; // NR CGI (M) - TBCD_TO_MCC_MNC(&cell->nRCGI.pLMN_Identity, out->plmn.mcc, out->plmn.mnc, out->plmn.mnc_digit_length); + PLMNID_TO_MCC_MNC(&cell->nRCGI.pLMN_Identity, out->plmn.mcc, out->plmn.mnc, out->plmn.mnc_digit_length); BIT_STRING_TO_NR_CELL_IDENTITY(&cell->nRCGI.nRCellIdentity, out->nr_cellid); // NR PCI (O) if (cell->nRPCI != NULL) @@ -534,20 +534,56 @@ bool decode_f1ap_setup_request(const F1AP_F1AP_PDU_t *pdu, f1ap_setup_req_t *out return true; } -void copy_f1ap_served_cell_info(f1ap_served_cell_info_t *dest, const f1ap_served_cell_info_t *src) +f1ap_served_cell_info_t copy_f1ap_served_cell_info(const f1ap_served_cell_info_t *src) { - // shallow copy - *dest = *src; - // tac + f1ap_served_cell_info_t dst = { + .plmn = src->plmn, + .nr_cellid = src->nr_cellid, + .nr_pci = src->nr_pci, + .num_ssi = src->num_ssi, + .mode = src->mode, + }; + + for (int i = 0; i < src->num_ssi; ++i) + dst.nssai[i] = src->nssai[i]; + + if (src->mode == F1AP_MODE_TDD) + dst.tdd = src->tdd; + else + dst.fdd = src->fdd; + if (src->tac) { - dest->tac = malloc_or_fail(sizeof(*dest->tac)); - *dest->tac = *src->tac; + dst.tac = malloc_or_fail(sizeof(*dst.tac)); + *dst.tac = *src->tac; } - // measurement timing config - if (src->measurement_timing_config_len) { - dest->measurement_timing_config = calloc_or_fail(src->measurement_timing_config_len, sizeof(*dest->measurement_timing_config)); - memcpy(dest->measurement_timing_config, src->measurement_timing_config, src->measurement_timing_config_len); + + if (src->measurement_timing_config_len > 0) { + dst.measurement_timing_config_len = src->measurement_timing_config_len; + dst.measurement_timing_config = calloc_or_fail(src->measurement_timing_config_len, sizeof(*dst.measurement_timing_config)); + memcpy(dst.measurement_timing_config, src->measurement_timing_config, src->measurement_timing_config_len); } + return dst; +} + +static f1ap_gnb_du_system_info_t *copy_f1ap_gnb_du_system_info(const f1ap_gnb_du_system_info_t *src) +{ + if (!src) + return NULL; + + f1ap_gnb_du_system_info_t *dst = calloc_or_fail(1, sizeof(*dst)); + if (src->mib_length > 0) { + dst->mib_length = src->mib_length; + dst->mib = calloc_or_fail(src->mib_length, sizeof(*src->mib)); + memcpy(dst->mib, src->mib, dst->mib_length); + } + + if (src->sib1_length > 0) { + dst->sib1_length = src->sib1_length; + dst->sib1 = calloc_or_fail(src->sib1_length, sizeof(*dst->sib1)); + memcpy(dst->sib1, src->sib1, dst->sib1_length); + } + + return dst; } /** @@ -567,25 +603,9 @@ f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg) cp.num_cells_available = msg->num_cells_available; for (int n = 0; n < msg->num_cells_available; n++) { /* cell.info */ - f1ap_served_cell_info_t *sci = &cp.cell[n].info; - const f1ap_served_cell_info_t *msg_sci = &msg->cell[n].info; - copy_f1ap_served_cell_info(sci, msg_sci); + cp.cell[n].info = copy_f1ap_served_cell_info(&msg->cell[n].info); /* cell.sys_info */ - if (msg->cell[n].sys_info) { - f1ap_gnb_du_system_info_t *orig_sys_info = msg->cell[n].sys_info; - f1ap_gnb_du_system_info_t *copy_sys_info = calloc_or_fail(1, sizeof(*copy_sys_info)); - cp.cell[n].sys_info = copy_sys_info; - if (orig_sys_info->mib_length > 0) { - copy_sys_info->mib = calloc_or_fail(orig_sys_info->mib_length, sizeof(*copy_sys_info->mib)); - copy_sys_info->mib_length = orig_sys_info->mib_length; - memcpy(copy_sys_info->mib, orig_sys_info->mib, copy_sys_info->mib_length); - } - if (orig_sys_info->sib1_length > 0) { - copy_sys_info->sib1 = calloc_or_fail(orig_sys_info->sib1_length, sizeof(*copy_sys_info->sib1)); - copy_sys_info->sib1_length = orig_sys_info->sib1_length; - memcpy(copy_sys_info->sib1, orig_sys_info->sib1, copy_sys_info->sib1_length); - } - } + cp.cell[n].sys_info = copy_f1ap_gnb_du_system_info(msg->cell[n].sys_info); } for (int i = 0; i < sizeofArray(msg->rrc_ver); i++) cp.rrc_ver[i] = msg->rrc_ver[i]; @@ -1002,7 +1022,6 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura /* mandatory */ /* c2. Served_Cells_To_Add */ if (msg->num_cells_to_add > 0) { - AssertFatal(false, "code for adding cells not tested\n"); asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBDUConfigurationUpdateIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_Served_Cells_To_Add_List; ie2->criticality = F1AP_Criticality_reject; @@ -1060,7 +1079,6 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura ie4->id = F1AP_ProtocolIE_ID_id_Served_Cells_To_Delete_List; ie4->criticality = F1AP_Criticality_reject; ie4->value.present = F1AP_GNBDUConfigurationUpdateIEs__value_PR_Served_Cells_To_Delete_List; - AssertFatal(msg->num_cells_to_delete == 0, "code for deleting cells not tested\n"); for (int i = 0; i < msg->num_cells_to_delete; i++) { asn1cSequenceAdd(ie4->value.choice.Served_Cells_To_Delete_List.list, F1AP_Served_Cells_To_Delete_ItemIEs_t, @@ -1114,8 +1132,8 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du } break; case F1AP_ProtocolIE_ID_id_Served_Cells_To_Add_List: { /* Served Cells To Add List */ - AssertError(out->num_cells_to_add > 0, return false, "at least 1 cell to add shall to be present"); out->num_cells_to_add = ie->value.choice.Served_Cells_To_Add_List.list.count; + AssertError(out->num_cells_to_add > 0, return false, "at least 1 cell to add shall to be present"); for (int i = 0; i < out->num_cells_to_add; i++) { F1AP_Served_Cells_To_Add_Item_t *served_cells_item = &((F1AP_Served_Cells_To_Add_ItemIEs_t *)ie->value.choice.Served_Cells_To_Add_List.list.array[i]) @@ -1142,7 +1160,7 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du /* Old NR CGI (M) */ F1AP_NRCGI_t *oldNRCGI = &served_cells_item->oldNRCGI; f1ap_plmn_t *old_plmn = &out->cell_to_modify[i].old_plmn; - TBCD_TO_MCC_MNC(&(oldNRCGI->pLMN_Identity), old_plmn->mcc, old_plmn->mnc, old_plmn->mnc_digit_length); + PLMNID_TO_MCC_MNC(&oldNRCGI->pLMN_Identity, old_plmn->mcc, old_plmn->mnc, old_plmn->mnc_digit_length); /* Old NR CGI Cell ID */ BIT_STRING_TO_NR_CELL_IDENTITY(&oldNRCGI->nRCellIdentity, out->cell_to_modify[i].old_nr_cellid); /* Served Cell Information (M) */ @@ -1166,7 +1184,7 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du F1AP_NRCGI_t *oldNRCGI = &served_cells_item->oldNRCGI; f1ap_plmn_t *plmn = &out->cell_to_delete[i].plmn; /* Old NR CGI (M) */ - TBCD_TO_MCC_MNC(&(oldNRCGI->pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); + PLMNID_TO_MCC_MNC(&(oldNRCGI->pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); // NR cellID BIT_STRING_TO_NR_CELL_IDENTITY(&oldNRCGI->nRCellIdentity, out->cell_to_delete[i].nr_cellid); } @@ -1216,10 +1234,8 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a for (int i = 0; i < a->num_cells_to_add; i++) { if (!eq_f1ap_cell_info(&a->cell_to_add[i].info, &b->cell_to_add[i].info)) return false; - if (a->cell_to_add[i].sys_info && b->cell_to_add[i].sys_info) { - if (!eq_f1ap_sys_info(a->cell_to_add[i].sys_info, b->cell_to_add[i].sys_info)) - return false; - } + if (!eq_f1ap_sys_info(a->cell_to_add[i].sys_info, b->cell_to_add[i].sys_info)) + return false; } /* to delete */ _F1_EQ_CHECK_INT(a->num_cells_to_delete, b->num_cells_to_delete); @@ -1231,12 +1247,13 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a /* to modify */ _F1_EQ_CHECK_INT(a->num_cells_to_modify, b->num_cells_to_modify); for (int i = 0; i < a->num_cells_to_modify; i++) { + if (!eq_f1ap_plmn(&a->cell_to_modify[i].old_plmn, &b->cell_to_modify[i].old_plmn)) + return false; + _F1_EQ_CHECK_LONG(a->cell_to_modify[i].old_nr_cellid, b->cell_to_modify[i].old_nr_cellid); if (!eq_f1ap_cell_info(&a->cell_to_modify[i].info, &b->cell_to_modify[i].info)) return false; - if (a->cell_to_modify[i].sys_info && b->cell_to_modify[i].sys_info) { - if (!eq_f1ap_sys_info(a->cell_to_modify[i].sys_info, b->cell_to_modify[i].sys_info)) - return false; - } + if (!eq_f1ap_sys_info(a->cell_to_modify[i].sys_info, b->cell_to_modify[i].sys_info)) + return false; } return true; } @@ -1256,6 +1273,10 @@ f1ap_gnb_du_configuration_update_t cp_f1ap_du_configuration_update(const f1ap_gn cp.transaction_id = msg->transaction_id; /* to add */ cp.num_cells_to_add = msg->num_cells_to_add; + for (int i = 0; i < cp.num_cells_to_add; ++i) { + cp.cell_to_add[i].info = copy_f1ap_served_cell_info(&msg->cell_to_add[i].info); + cp.cell_to_add[i].sys_info = copy_f1ap_gnb_du_system_info(msg->cell_to_add[i].sys_info); + } /* to delete */ cp.num_cells_to_delete = msg->num_cells_to_delete; for (int i = 0; i < cp.num_cells_to_delete; i++) { @@ -1265,31 +1286,10 @@ f1ap_gnb_du_configuration_update_t cp_f1ap_du_configuration_update(const f1ap_gn /* to modify */ cp.num_cells_to_modify = msg->num_cells_to_modify; for (int i = 0; i < cp.num_cells_to_modify; i++) { - cp.cell_to_modify[i].info = msg->cell_to_modify[i].info; - f1ap_served_cell_info_t *info = &cp.cell_to_modify[i].info; - if (info->measurement_timing_config_len > 0) { - info->measurement_timing_config = malloc_or_fail(info->measurement_timing_config_len * sizeof(*info->measurement_timing_config)); - for (int j = 0; j < info->measurement_timing_config_len; j++) - info->measurement_timing_config[j] = msg->cell_to_modify[i].info.measurement_timing_config[j]; - } - /* TAC */ - info->tac = calloc_or_fail(1, sizeof(*info->tac)); - *info->tac = *msg->cell_to_modify[i].info.tac; - /* System information */ - cp.cell_to_modify[i].sys_info = malloc_or_fail(sizeof(*cp.cell_to_modify[i].sys_info)); - f1ap_gnb_du_system_info_t *sys_info = cp.cell_to_modify[i].sys_info; - if (msg->cell_to_modify[i].sys_info->mib_length > 0) { - sys_info->mib_length = msg->cell_to_modify[i].sys_info->mib_length; - sys_info->mib = calloc_or_fail(msg->cell_to_modify[i].sys_info->mib_length, sizeof(*sys_info->mib)); - for (int j = 0; j < sys_info->mib_length; j++) - sys_info->mib[j] = msg->cell_to_modify[i].sys_info->mib[j]; - } - if (msg->cell_to_modify[i].sys_info->sib1_length > 0) { - sys_info->sib1_length = msg->cell_to_modify[i].sys_info->sib1_length; - sys_info->sib1 = calloc_or_fail(msg->cell_to_modify[i].sys_info->sib1_length, sizeof(*sys_info->sib1)); - for (int j = 0; j < sys_info->sib1_length; j++) - sys_info->sib1[j] = msg->cell_to_modify[i].sys_info->sib1[j]; - } + cp.cell_to_modify[i].old_plmn = msg->cell_to_modify[i].old_plmn; + cp.cell_to_modify[i].old_nr_cellid = msg->cell_to_modify[i].old_nr_cellid; + cp.cell_to_modify[i].info = copy_f1ap_served_cell_info(&msg->cell_to_modify[i].info); + cp.cell_to_modify[i].sys_info = copy_f1ap_gnb_du_system_info(msg->cell_to_modify[i].sys_info); } return cp; } @@ -1521,7 +1521,7 @@ bool decode_f1ap_cu_configuration_update_acknowledge(const F1AP_F1AP_PDU_t *pdu, const F1AP_Cells_Failed_to_be_Activated_List_Item_t *item = &itemIE->value.choice.Cells_Failed_to_be_Activated_List_Item; // NR CGI (M) f1ap_plmn_t *plmn = &out->cells_failed_to_be_activated[j].plmn; - TBCD_TO_MCC_MNC(&(item->nRCGI.pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); + PLMNID_TO_MCC_MNC(&(item->nRCGI.pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); BIT_STRING_TO_NR_CELL_IDENTITY(&item->nRCGI.nRCellIdentity, out->cells_failed_to_be_activated[j].nr_cellid); // Cause (M) switch (item->cause.present) { diff --git a/openair2/F1AP/lib/f1ap_interface_management.h b/openair2/F1AP/lib/f1ap_interface_management.h index e483a74301..6fd79b4007 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.h +++ b/openair2/F1AP/lib/f1ap_interface_management.h @@ -32,7 +32,7 @@ bool decode_f1ap_setup_request(const struct F1AP_F1AP_PDU *pdu, f1ap_setup_req_t f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg); bool eq_f1ap_setup_request(const f1ap_setup_req_t *a, const f1ap_setup_req_t *b); void free_f1ap_setup_request(const f1ap_setup_req_t *msg); -void copy_f1ap_served_cell_info(f1ap_served_cell_info_t *a, const f1ap_served_cell_info_t *b); +f1ap_served_cell_info_t copy_f1ap_served_cell_info(const f1ap_served_cell_info_t *src); void free_f1ap_cell(const f1ap_served_cell_info_t *info, const f1ap_gnb_du_system_info_t *sys_info); /* F1 Setup Response */ diff --git a/openair2/F1AP/lib/f1ap_lib_common.c b/openair2/F1AP/lib/f1ap_lib_common.c index 2938898aff..a569146815 100644 --- a/openair2/F1AP/lib/f1ap_lib_common.c +++ b/openair2/F1AP/lib/f1ap_lib_common.c @@ -53,7 +53,10 @@ bool eq_f1ap_cell_info(const f1ap_served_cell_info_t *a, const f1ap_served_cell_ { _F1_EQ_CHECK_LONG(a->nr_cellid, b->nr_cellid); _F1_EQ_CHECK_INT(a->nr_pci, b->nr_pci); - _F1_EQ_CHECK_INT(*a->tac, *b->tac); + if ((!a->tac) ^ (!b->tac)) + return false; + if (a->tac) + _F1_EQ_CHECK_INT(*a->tac, *b->tac); _F1_EQ_CHECK_INT(a->mode, b->mode); if (a->mode == F1AP_MODE_TDD) { /* TDD */ @@ -81,6 +84,13 @@ bool eq_f1ap_cell_info(const f1ap_served_cell_info_t *a, const f1ap_served_cell_ bool eq_f1ap_sys_info(const f1ap_gnb_du_system_info_t *a, const f1ap_gnb_du_system_info_t *b) { + if (!a && !b) + return true; + + /* will fail if not both a/b NULL or set */ + if ((!a) ^ (!b)) + return false; + /* MIB */ _F1_EQ_CHECK_INT(a->mib_length, b->mib_length); for (int i = 0; i < a->mib_length; i++) diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 4c75aa944e..5d153f1aea 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -420,12 +420,38 @@ static void test_f1ap_du_configuration_update(void) .plmn.mnc_digit_length = 3, .tac = tac, }; + char *mtc2_data = "mtc2"; + uint8_t *mtc2 = (void*)strdup(mtc2_data); + int mtc2_len = strlen(mtc2_data); + f1ap_served_cell_info_t info2 = { + .mode = F1AP_MODE_FDD, + .fdd.ul_freqinfo.arfcn = 640000, + .fdd.ul_freqinfo.band = 78, + .fdd.dl_freqinfo.arfcn = 600000, + .fdd.dl_freqinfo.band = 78, + .fdd.ul_tbw.nrb = 66, + .fdd.ul_tbw.scs = 1, + .fdd.dl_tbw.nrb = 66, + .fdd.dl_tbw.scs = 1, + .measurement_timing_config_len = mtc2_len, + .measurement_timing_config = mtc2, + .nr_cellid = 123456, + .plmn.mcc = 2, + .plmn.mnc = 2, + .plmn.mnc_digit_length = 2, + }; /* create message */ f1ap_gnb_du_configuration_update_t orig = { .transaction_id = 2, + .num_cells_to_add = 1, + .cell_to_add[0].info = info2, .num_cells_to_modify = 1, .cell_to_modify[0].info = info, - .num_cells_to_delete = 0, + .cell_to_modify[0].old_nr_cellid = 1235UL, + .cell_to_modify[0].old_plmn.mcc = 208, + .cell_to_modify[0].old_plmn.mnc = 88, + .cell_to_modify[0].old_plmn.mnc_digit_length = 2, + .num_cells_to_delete = 1, .cell_to_delete[0].nr_cellid = 1234UL, .cell_to_delete[0].plmn.mcc = 1, .cell_to_delete[0].plmn.mnc = 1, diff --git a/openair2/GNB_APP/gnb_app.c b/openair2/GNB_APP/gnb_app.c index 354ed2e415..2162d9998e 100644 --- a/openair2/GNB_APP/gnb_app.c +++ b/openair2/GNB_APP/gnb_app.c @@ -238,10 +238,6 @@ void *gNB_app_task(void *args_p) break; - case F1AP_GNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE: - LOG_E(GNB_APP, "[gNB %ld] Handling of %s message not implemented yet\n", instance, msg_name); - 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 0612fa9677..7555d1f5d0 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1166,7 +1166,7 @@ static int read_du_cell_info(configmodule_interface_t *cfg, return 1; } -static f1ap_tdd_info_t read_tdd_config(const NR_ServingCellConfigCommon_t *scc) +f1ap_tdd_info_t read_tdd_config(const NR_ServingCellConfigCommon_t *scc) { const NR_FrequencyInfoDL_t *dl = scc->downlinkConfigCommon->frequencyInfoDL; f1ap_tdd_info_t tdd = { @@ -1195,7 +1195,27 @@ static f1ap_fdd_info_t read_fdd_config(const NR_ServingCellConfigCommon_t *scc) return fdd; } -static f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, +f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCCH_DL_SCH_Message_t *sib1) +{ + int buf_len = 3; + f1ap_gnb_du_system_info_t *sys_info = calloc_or_fail(1, sizeof(*sys_info)); + + sys_info->mib = calloc_or_fail(buf_len, sizeof(*sys_info->mib)); + DevAssert(mib != NULL); + sys_info->mib_length = encode_MIB_NR(mib, 0, sys_info->mib, buf_len); + DevAssert(sys_info->mib_length == buf_len); + + DevAssert(sib1 != NULL); + NR_SIB1_t *bcch_SIB1 = sib1->message.choice.c1->choice.systemInformationBlockType1; + sys_info->sib1 = calloc_or_fail(NR_MAX_SIB_LENGTH / 8, sizeof(*sys_info->sib1)); + asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_SIB1, NULL, (void *)bcch_SIB1, sys_info->sib1, NR_MAX_SIB_LENGTH / 8); + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); + sys_info->sib1_length = (enc_rval.encoded + 7) / 8; + + return sys_info; +} + +f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, const char *name, const f1ap_served_cell_info_t *info, const NR_ServingCellConfigCommon_t *scc, @@ -1244,24 +1264,7 @@ static f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, if (IS_SA_MODE(get_softmodem_params())) { // in NSA we don't transmit SIB1, so cannot fill DU system information // so cannot send MIB either - - int buf_len = 3; // this is what we assume in monolithic - req->cell[0].sys_info = calloc(1, sizeof(*req->cell[0].sys_info)); - AssertFatal(req->cell[0].sys_info != NULL, "out of memory\n"); - f1ap_gnb_du_system_info_t *sys_info = req->cell[0].sys_info; - sys_info->mib = calloc(buf_len, sizeof(*sys_info->mib)); - DevAssert(sys_info->mib != NULL); - DevAssert(mib != NULL); - // encode only the mib message itself - sys_info->mib_length = encode_MIB_NR_setup(mib->message.choice.mib, 0, sys_info->mib, buf_len); - DevAssert(sys_info->mib_length == buf_len); - - DevAssert(sib1 != NULL); - NR_SIB1_t *bcch_SIB1 = sib1->message.choice.c1->choice.systemInformationBlockType1; - sys_info->sib1 = calloc(NR_MAX_SIB_LENGTH / 8, sizeof(*sys_info->sib1)); - asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_SIB1, NULL, (void *)bcch_SIB1, sys_info->sib1, NR_MAX_SIB_LENGTH / 8); - AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); - sys_info->sib1_length = (enc_rval.encoded + 7) / 8; + req->cell[0].sys_info = get_sys_info(mib, sib1); } int num = read_version(TO_STRING(NR_RRC_VERSION), &req->rrc_ver[0], &req->rrc_ver[1], &req->rrc_ver[2]); diff --git a/openair2/GNB_APP/gnb_config.h b/openair2/GNB_APP/gnb_config.h index aaa945509c..baf4ad5d94 100644 --- a/openair2/GNB_APP/gnb_config.h +++ b/openair2/GNB_APP/gnb_config.h @@ -95,6 +95,14 @@ gNB_RRC_INST *RCconfig_NRRRC(); int RCconfig_NR_NG(MessageDef *msg_p, uint32_t i); int RCconfig_NR_X2(MessageDef *msg_p, uint32_t i); void wait_f1_setup_response(void); +f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, + const char *name, + const f1ap_served_cell_info_t *info, + const NR_ServingCellConfigCommon_t *scc, + NR_BCCH_BCH_Message_t *mib, + const NR_BCCH_DL_SCH_Message_t *sib1); +f1ap_tdd_info_t read_tdd_config(const NR_ServingCellConfigCommon_t *scc); +f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCCH_DL_SCH_Message_t *sib1); int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update); MessageDef *RCconfig_NR_CU_E1(const E1_t *entity); ngran_node_t get_node_type(void); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 29e4ea1122..c7faf78179 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1941,7 +1941,8 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP, NR_UE_info_t *UE = find_nr_UE(&nr_mac->UE_info, ra->rnti); if (!UE) { - LOG_E(NR_MAC, "want to generate %s, but rnti %04x not in the table\n", ra_type_str, ra->rnti); + LOG_E(NR_MAC, "want to generate %s, but rnti %04x not in the table. Abort RA\n", ra_type_str, ra->rnti); + nr_clear_ra_proc(ra); return; } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 55c86594cd..730e954f4b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -938,6 +938,9 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_UEs_t *UE_info = &gNB_mac->UE_info; nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; + const NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + gNB_mac->mac_stats.total_prb_aggregate += NRRIV2BW(initialDL->locationAndBandwidth, MAX_BWP_SIZE); + UE_iterator(UE_info->list, UE) { NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_DL_BWP_t *current_BWP = &UE->current_DL_BWP; @@ -1252,6 +1255,7 @@ void nr_schedule_ue_spec(module_id_t module_id, T(T_GNB_MAC_RETRANSMISSION_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_INT(harq->round), T_BUFFER(harq->transportBlock, TBS)); UE->mac_stats.dl.total_rbs_retx += sched_pdsch->rbSize; + gNB_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; } else { /* initial transmission */ LOG_D(NR_MAC, "Initial HARQ transmission in %d.%d\n", frame, slot); uint8_t *buf = (uint8_t *) harq->transportBlock; @@ -1369,6 +1373,7 @@ void nr_schedule_ue_spec(module_id_t module_id, UE->mac_stats.dl.num_mac_sdu += sdus; UE->mac_stats.dl.current_rbs = sched_pdsch->rbSize; UE->mac_stats.dl.total_sdu_bytes += dlsch_total_bytes; + gNB_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; /* save retransmission information */ harq->sched_pdsch = *sched_pdsch; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index e0cc7a8fe0..1f9cb9c0c1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3319,7 +3319,7 @@ bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti) .gNB_CU_ue_id = ue_data.secondary_ue, .gNB_DU_ue_id = rnti, .cause = F1AP_CAUSE_RADIO_NETWORK, - .cause_value = F1AP_CauseRadioNetwork_rl_failure_others, + .cause_value = F1AP_CauseRadioNetwork_rl_failure_rlc, }; nrmac->mac_rrc.ue_context_release_request(&request); return true; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 1faee06a39..9c6f5f4b27 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -99,7 +99,8 @@ static bool check_plmn_identity(const f1ap_plmn_t *check_plmn, const f1ap_plmn_t return plmn->mcc == check_plmn->mcc && plmn->mnc_digit_length == check_plmn->mnc_digit_length && plmn->mnc == check_plmn->mnc; } -static void du_clear_all_ue_states() +/* not static, so we can call it from the outside (in telnet) */ +void du_clear_all_ue_states() { gNB_MAC_INST *mac = RC.nrmac[0]; NR_SCHED_LOCK(&mac->sched_lock); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c index 6d8f7a7115..544ab3889c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c @@ -82,10 +82,8 @@ static void gnb_du_configuration_update_f1ap(const f1ap_gnb_du_configuration_upd f1ap_gnb_du_configuration_update_t cp = cp_f1ap_du_configuration_update(upd); /* transfer to ITTI message */ F1AP_GNB_DU_CONFIGURATION_UPDATE(msg) = cp; - /* free after copy */ - free_f1ap_du_configuration_update(upd); /* send to RRC task */ - itti_send_msg_to_task(TASK_RRC_GNB, 0, msg); + itti_send_msg_to_task(TASK_DU_F1, 0, msg); } static void ue_context_setup_response_f1ap(const f1ap_ue_context_setup_t *req, const f1ap_ue_context_setup_t *resp) diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 420445076f..641f6d93d1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -807,6 +807,11 @@ typedef struct { int8_t nvipc_poll_core; } nvipc_params_t; +typedef struct { + uint64_t total_prb_aggregate; + uint64_t used_prb_aggregate; +} mac_stats_t; + /*! \brief top level eNB MAC structure */ typedef struct gNB_MAC_INST_s { /// Ethernet parameters for northbound midhaul interface @@ -930,6 +935,8 @@ typedef struct gNB_MAC_INST_s { pthread_mutex_t sched_lock; + mac_stats_t mac_stats; + } gNB_MAC_INST; #endif /*__LAYER2_NR_MAC_GNB_H__ */ diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index e70d7b66eb..f6e7c15e8b 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -920,10 +920,14 @@ void nr_rrc_config_dl_tda(struct NR_PDSCH_TimeDomainResourceAllocationList *pdsc } -void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay){ - +void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay) +{ //TODO change to accomodate for SRS + const NR_PUSCH_TimeDomainResourceAllocationList_t *tda = + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + AssertFatal(tda->list.count == 0, "already have pusch_TimeDomainAllocationList members\n"); + frame_type_t frame_type = get_frame_type(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); const int k2 = min_fb_delay; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index e8c7fcdc2c..9e552eaa2f 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -106,6 +106,21 @@ extern RAN_CONTEXT_t RC; mui_t rrc_gNB_mui = 0; +typedef struct deliver_ue_ctxt_release_data_t { + gNB_RRC_INST *rrc; + f1ap_ue_context_release_cmd_t *release_cmd; + sctp_assoc_t assoc_id; +} deliver_ue_ctxt_release_data_t; + +static void rrc_deliver_ue_ctxt_release_cmd(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id) +{ + DevAssert(deliver_pdu_data != NULL); + deliver_ue_ctxt_release_data_t *data = deliver_pdu_data; + data->release_cmd->rrc_container = (uint8_t*) buf; + data->release_cmd->rrc_container_length = size; + data->rrc->mac_rrc.ue_context_release_command(data->assoc_id, data->release_cmd); +} + ///---------------------------------------------------------------------------------------------------------------/// ///---------------------------------------------------------------------------------------------------------------/// @@ -1974,8 +1989,18 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_CU_ue_id); // TODO what happens if no AMF connected? should also handle, set an_release true if (!ue_context_p) { - LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", req->gNB_CU_ue_id); - // TODO just request the DU to release to make it happy + LOG_W(RRC, "could not find UE context for CU UE ID %u: auto-generate release command\n", req->gNB_CU_ue_id); + uint8_t buffer[RRC_BUF_SIZE] = {0}; + int size = do_NR_RRCRelease(buffer, RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(0)); + f1ap_ue_context_release_cmd_t ue_context_release_cmd = { + .gNB_CU_ue_id = req->gNB_CU_ue_id, + .gNB_DU_ue_id = req->gNB_DU_ue_id, + .cause = F1AP_CAUSE_RADIO_NETWORK, + .cause_value = 10, // 10 = F1AP_CauseRadioNetwork_normal_release + .srb_id = DCCH, + }; + deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd}; + nr_pdcp_data_req_srb(req->gNB_CU_ue_id, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_release_cmd, &data); return; } @@ -2473,6 +2498,7 @@ void *rrc_gnb_task(void *args_p) { switch (ITTI_MSG_ID(msg_p)) { case TERMINATE_MESSAGE: LOG_W(NR_RRC, " *** Exiting NR_RRC thread\n"); + timer_remove(stats_timer_id); itti_exit_task(); break; @@ -2645,20 +2671,6 @@ void rrc_gNB_generate_SecurityModeCommand(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, buffer, size); } -typedef struct deliver_ue_ctxt_release_data_t { - gNB_RRC_INST *rrc; - f1ap_ue_context_release_cmd_t *release_cmd; - sctp_assoc_t assoc_id; -} deliver_ue_ctxt_release_data_t; -static void rrc_deliver_ue_ctxt_release_cmd(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id) -{ - DevAssert(deliver_pdu_data != NULL); - deliver_ue_ctxt_release_data_t *data = deliver_pdu_data; - data->release_cmd->rrc_container = (uint8_t*) buf; - data->release_cmd->rrc_container_length = size; - data->rrc->mac_rrc.ue_context_release_command(data->assoc_id, data->release_cmd); -} - //----------------------------------------------------------------------------- /* * Generate the RRC Connection Release to UE. diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index bc3c564516..654ef04be1 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -448,7 +448,7 @@ static void update_cell_info(nr_rrc_du_container_t *du, const f1ap_served_cell_i f1ap_served_cell_info_t *ci = &du->setup_req->cell[0].info; // make sure no memory is allocated free_f1ap_cell(ci, NULL); - copy_f1ap_served_cell_info(ci, new_ci); + *ci = copy_f1ap_served_cell_info(new_ci); NR_MeasurementTimingConfiguration_t *new_mtc = extract_mtc(new_ci->measurement_timing_config, new_ci->measurement_timing_config_len); @@ -505,8 +505,10 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update // MIB is mandatory, so will be overwritten. SIB1 is optional, so will // only be overwritten if present in sys_info ASN_STRUCT_FREE(asn_DEF_NR_MIB, du->mib); - if (sys_info->sib1 != NULL) + if (sys_info->sib1 != NULL) { ASN_STRUCT_FREE(asn_DEF_NR_SIB1, du->sib1); + du->sib1 = NULL; + } NR_MIB_t *mib = NULL; if (!extract_sys_info(sys_info, &mib, &du->sib1)) { @@ -531,8 +533,6 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update /* Send DU Configuration Acknowledgement */ f1ap_gnb_du_configuration_update_acknowledge_t ack = {.transaction_id = conf_up->transaction_id}; rrc->mac_rrc.gnb_du_configuration_update_acknowledge(assoc_id, &ack); - /* free F1AP message after use */ - free_f1ap_du_configuration_update(conf_up); } void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t *lc, sctp_assoc_t assoc_id) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index d362d5ef7b..faa185a0cb 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -1085,7 +1085,16 @@ static void rfsimulator_end(openair0_device *device) { } close(s->epollfd); hashtable_destroy(&s->fd_to_buf_map); + free(s); } +static void stopServer(openair0_device *device) +{ + rfsimulator_state_t *t = (rfsimulator_state_t *) device->priv; + DevAssert(t != NULL); + close(t->listen_sock); + rfsimulator_end(device); +} + static int rfsimulator_stop(openair0_device *device) { return 0; } @@ -1136,7 +1145,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { device->trx_start_func = rfsimulator->role == SIMU_ROLE_SERVER ? startServer : startClient; device->trx_get_stats_func = rfsimulator_get_stats; device->trx_reset_stats_func = rfsimulator_reset_stats; - device->trx_end_func = rfsimulator_end; + device->trx_end_func = rfsimulator->role == SIMU_ROLE_SERVER ? stopServer : rfsimulator_end; device->trx_stop_func = rfsimulator_stop; device->trx_set_freq_func = rfsimulator_set_freq; device->trx_set_gains_func = rfsimulator_set_gains;